总览
OpenClaw 并发安全由三套核心机制协同构成:
Per-Request 请求隔离模型 + SessionLock 会话级互斥锁 + CronContextHolder 定时任务上下文隔离
属于 UpClaw「并发安全」改造板块,解决多渠道并发消息、多子代理并行、定时任务与用户会话抢占资源、上下文污染、会话状态竞态、重复执行、数据错乱等线上典型并发问题。
原生 AgentScope 痛点:
- 无会话锁,同一会话并发多条消息,多轮推理互相抢占上下文,造成消息乱序、上下文覆盖;
- 所有请求共享全局静态上下文,并发读写导致记忆、会话状态互相串扰;
- 定时任务(HEARTBEAT、后台压缩、记忆归档)与用户前台会话共用一套上下文,变量污染;
- 子代理并行创建、工具并发调用缺少会话内限流与互斥保护;
- 缺少统一上下文持有者,多插件异步回调容易丢失会话标识、租户、trace 信息。
三层并发防护定位分工:
- Per-Request:线程/协程维度,请求上下文隔离,解决跨会话串数据;
- SessionLock:单会话维度互斥锁,防止同一个会话并发消息造成状态撕裂;
- CronContextHolder:后台定时任务独立上下文容器,隔离定时任务与前台用户会话环境。
生效层级:网关多并发接入层 → Agent 会话层 → 后台调度层,覆盖全链路。
一、Per-Request 请求隔离模型
1. 定义
Per-Request 是基于协程本地存储(Goroutine Local Storage / Async Local Storage)的上下文传播模型。
每一条外部消息(飞书、Websocket、HTTP、SFA2A子代理消息)进入网关,自动生成独立请求上下文 RequestContext,贯穿整条调用链路:插件回调、模型调用、工具执行、记忆读写、Langfuse 埋点。
存储核心元数据:
traceId、sessionId、tenantId、agentName、userId
当前生效MD配置快照、权限快照
链路标签、cjkRatio、环境标识
2. 核心机制
-
上下文自动透传
所有可插拔插件(ModelProvider、ToolAdapter、Observer、SecurityPolicy)、生命周期钩子、异步回调,自动继承父 RequestContext;
异步派生任务(工具后台归档、记忆异步写入)会自动拷贝一份只读快照,防止父上下文提前销毁导致空指针。
-
禁止全局静态变量存储会话信息
强制规范:租户、会话、Trace 相关信息不允许存在全局变量 ,全部从 RequestContext 获取。
杜绝 A 用户会话数据泄漏到 B 用户会话。
-
生命周期边界
消息入站 → 创建 RequestContext 绑定协程
执行完整Agent推理循环
消息响应返回 → 自动清理上下文,释放引用 -
与子代理SFA2A联动
父代理发起 spawn_subagent 时,生成全新独立子 RequestContext;
仅传递精简任务参数,不直接复用父上下文引用,配合子代理防污染机制。
3. 解决的并发问题
- 跨会话上下文串扰(最常见BUG:不同用户对话记忆混淆、权限错乱);
- 异步回调丢失 traceId、无法上报 Langfuse;
- 插件并发读写全局状态造成数据竞争;
4. 局限性
Per-Request 只能隔离不同请求 ,无法限制同一个会话同时并发多条消息 。
例:用户快速连续发送2条消息,两条请求各自拥有独立 RequestContext,但共享同一个 Session 内存状态,会并发读写会话上下文、LongTermMemory,引发消息乱序、上下文覆盖。
👉 该缺口由 SessionLock 会话锁补齐。
二、SessionLock 会话级互斥锁(核心)
1. 定义
以 sessionId 为锁粒度的会话互斥锁 ,属于会话层面并发控制原语。
规则:同一个会话,同一时刻只允许执行一轮 Agent ReAct 主循环。
2. 两种锁模式(可通过 ConditionalOnProperty 切换)
Mode A:排队模式(默认生产模式 FIFO Queue)
同一会话并发多条消息,进入会话内部等待队列,串行依次执行;
优点:消息不会丢失,保证时序;适合客服IM、聊天场景。
Mode B:拒绝模式(高速API场景)
会话正在繁忙时,新入消息直接返回 session busy;
适合实时指令类场景,避免消息堆积延迟持续放大。
3. 完整生命周期执行卡点
消息入站完整流程:
Channel接收消息 → 创建Per-Request上下文
→ 尝试抢占 SessionLock(sessionId)
├─ 获取锁成功:执行onSessionCreate/推理循环/工具调用
└─ 获取锁失败:排队 or 返回繁忙
→ 推理、工具、记忆读写全部持有锁期间执行
→ 本轮Agent循环结束 → 释放SessionLock
→ 下一条排队消息开始执行
重要边界:
锁保护范围:主Agent推理循环 ;
子代理拥有独立 sessionId,持有独立 SessionLock;
父会话锁不会阻塞子代理并行执行(支持可控多子代理并发,由
maxConcurrentSubAgents限流)。
4. 锁配套能力
-
锁超时自动释放(防死锁)
配置
sessionLockTimeout(默认300s):Agent 发生模型卡死、沙箱挂起、无限工具循环时,超时强制解锁,避免会话永久卡死。
同时写入告警日志,在 Langfuse
SessionSpan标记锁超时。 -
锁状态可观测
Observer 插件采集指标:
- 当前持有锁会话数量
- 会话排队队列长度
- 锁平均持有时长、锁超时次数
-
和三层Tool治理联动
持有锁期间所有工具执行、记忆读写串行化;
防止同一会话并发读写 LongTermMemory 造成记忆写入冲突、摘要错乱。
-
和Context Compaction联动
压缩属于会话临界区操作;压缩进行中,同会话其他消息排队,避免一边压缩一边拼装上下文引发内存视图撕裂。
5. 解决典型并发故障
- 用户快速连发多条消息 → 上下文并发读写导致对话历史错乱、消息乱序;
- 同时触发上下文压缩+新一轮推理 → ContextEngine 视图竞争;
- 多轮并发工具调用,LongTermMemory 同时读写产生脏数据;
6. 边界约束(设计取舍)
SessionLock 是会话串行化保护,不会阻止:
- 不同session之间完全并行(网关水平扩容无瓶颈);
- 会话内合法并行子代理(受全局子代理并发上限管控);
三、CronContextHolder 定时任务上下文隔离
1. 背景痛点
HEARTBEAT.md、定时记忆归档、后台批量压缩、会话自动回收等定时任务,如果直接复用前台会话上下文会出现两类严重问题:
- 定时任务修改会话状态时,前台用户消息同时进入,产生竞态;
- 定时任务没有合法
RequestContext,插件、观测、记忆模块取不到租户/trace信息,埋点失效; - 定时任务异常变量泄漏污染正常用户请求上下文。
2. CronContextHolder 定义
专门为后台定时/后台异步任务 设计的独立上下文持有者。
不复用 Per-Request,拥有独立上下文生命周期。
分为两类实例:
- 全局定时上下文:不属于任何用户会话(全局记忆清理、系统巡检)
- 会话绑定定时上下文:归属某个session(会话心跳、定时自动压缩)
元数据组成
contextType: cron
sessionId(可选)、tenantId、traceId(自动生成cron-trace标识)
readOnly:标记定时任务是否允许修改会话状态
schedulerTaskId、taskType: heartbeat/compaction/memory_clean
3. 运行规则
- 定时任务启动时,由 Scheduler 插槽主动创建 CronContextHolder;
- 如果是绑定会话的心跳任务:尝试非阻塞抢占SessionLock
- 会话当前空闲:拿到锁执行后台清理、记忆归档;
- 会话正在处理用户消息:直接跳过本轮心跳,避免抢占用户请求资源;
- 定时任务执行全程上下文隔离,不会污染任何 Per-Request 用户上下文;
- 定时任务所有日志、Span 统一标记
context_type=cron,Langfuse 可区分前台流量与后台任务流量。
4. 典型场景示例
- HEARTBEAT 定时触发后台 Context Compaction
CronContextHolder 创建后台上下文,尝试抢占会话锁;用户正在聊天则本轮压缩延后,不抢占用户推理算力。 - 过期记忆定时归档
全局定时上下文,无会话绑定,批量操作向量库,独立观测指标。 - 超时会话自动销毁
Scheduler 通过 CronContext 遍历会话表,安全执行会话资源回收。
四、三者协同完整并发模型(标准时序)
场景:用户在IM连续发送两条消息
消息1、消息2同时抵达网关
│
├─ 两条消息各自生成独立 Per-Request 上下文
│
├─ 两条请求同时尝试抢占同一个 sessionId 的 SessionLock
│
├─ 消息1抢占成功,进入ReAct循环
└─ 消息2进入会话内部等待队列(排队模式)
│
消息1执行:LLM推理 → Tool调用 → Memory读写 → 上下文更新
消息1执行完毕 → 释放SessionLock
↓
队列中消息2获取锁,开始执行
场景:会话心跳定时任务 + 用户消息并发
用户消息:Per-Request,抢占SessionLock(成功,正在推理)
定时心跳任务:CronContextHolder 发起,非阻塞抢锁
→ 锁被占用,心跳任务直接跳过本轮执行,等待下一个周期
避免定时后台任务抢占用户前台算力。
场景:父代理并行拉起多个子代理
父会话持有父SessionLock
spawn_subagent 创建多个子session
每个子session拥有独立SessionLock、独立Per-Request上下文
子代理之间可以并行执行,受maxConcurrentSubAgents全局限流管控
完美支持多子代理并发分工。
五、与现有全体系联动关系
1. 联动 12+ ConditionalOnProperty
claw.session.lock.mode:排队/拒绝模式切换claw.session.lock.timeout:死锁保护时长claw.scheduler.enable:开关Cron定时上下文claw.subagent.maxConcurrentSubAgents:控制子代理并行上限
2. 联动 SubAgent 安全约束
父会话锁不阻塞子代理并行;
子代理各自独立锁,实现可控并发;
同时配合防递归、防污染隔离。
3. 联动 Context Compaction
上下文压缩属于临界区操作,必须持有SessionLock;
定时后台压缩使用 CronContext + 非阻塞锁,优先保障用户对话。
4. 联动四层 Langfuse 追踪
- Per-Request → Trace/Span 携带用户请求标识
- CronContext → Span 标记定时任务类型
- SessionLock 抢占、排队、超时事件生成独立 RulePolicySpan,可观测会话拥堵情况
5. 联动三层Tool治理
同会话工具串行执行,防止并发读写沙箱、并发调用API产生重复操作;
子代理独立会话可并行执行工具。
六、并发安全三层机制优缺点
优势
- 分层粒度清晰
跨请求隔离(Per-Request)→ 同会话串行保护(SessionLock)→ 后台任务隔离(CronContextHolder),覆盖所有并发场景; - 兼顾串行安全与并行能力
同会话串行防错乱,不同会话完全水平扩展;支持可控子代理并行; - 防止死锁+资源抢占均衡
定时任务非阻塞抢锁,不抢夺用户请求资源;锁超时自动释放; - 全链路可观测
锁竞争、排队、定时任务流量全部埋点,方便定位会话堆积、并发竞态问题; - 适配多渠道(飞书、Web、API、SFA2A)统一并发模型。
短板
- 单会话串行天然限制超高频指令场景;可拆分为多会话架构或者切换拒绝模式;
- 大量会话同时排队会增加端到端延迟,需要监控队列长度设置告警;
- 协程模型下需要严格规范开发,禁止插件随意跨协程传递原始上下文引用。
七、对比原生AgentScope / Codex / Claude Code
-
原生 AgentScope
无 SessionLock;无标准化 Per-Request 上下文模型;无独立定时上下文;
并发多条消息极易产生上下文竞争、记忆错乱,仅适合演示原型,不支持线上IM高并发场景。
-
OpenAI Codex
云端任务相互隔离,但不存在"会话"持久概念,没有会话锁机制;面向单次代码任务,不支持长期持续聊天会话并发管控。
-
Claude Code
本地单进程单会话模型,没有多并发网关设计,不存在完整并发安全体系。
-
OpenClaw
面向多渠道接入、多租户、长期持续会话、前后台混合流量设计的完整三层并发安全模型,是企业AI中台网关必备基础设施。
八、生产落地典型风险案例
案例1:用户连续发送多条指令,AI来回混淆历史对话
根因:无SessionLock,多条请求并发读写同一会话上下文;
修复:启用排队模式SessionLock,串行执行。
案例2:定时后台压缩和用户聊天同时执行,上下文崩溃
根因:后台任务无独立上下文,无锁争抢;
修复:压缩逻辑使用CronContextHolder,非阻塞抢占会话锁,繁忙则跳过。
案例3:异步工具回调,日志无法关联traceId
根因:异步协程丢失请求上下文;
修复:Per-Request异步自动快照拷贝上下文。
案例4:多子代理同时写入父代理回流结果,父上下文错乱
根因:多子代理并发回流;
解决方案:父会话串行汇总子代理结果,子代理内部允许并行执行。
九、落地价值(UpClaw 从可用 → 可上线)
- 解决多渠道高并发接入场景会话数据竞争、消息乱序、记忆错乱等稳定性故障;
- 隔离前台用户流量与后台定时任务,保障用户体验优先;
- 统一上下文传播规范,约束插件开发,避免内存泄漏、跨租户数据泄漏;
- 并发行为全链路可观测,支持容量规划、拥堵告警;
- 在安全性(串行防竞态)与性能(跨会话水平扩展、可控子代理并行)之间取得工程平衡。