一、定位与原生并发痛点
Observability(可观测体系)依托Observer可插拔插槽 + 全局生命周期Hook事件总线 + 四层Langfuse追踪实现全链路埋点;Per-Request Hook是观测链路并发安全的底层核心规范,隶属于 UpClaw「可观测性、并发安全」两大改造板块,与前文Per-Request上下文、SessionLock、CronContextHolder三位一体,解决观测场景典型并发故障:
无规范约束下典型线上问题
- Trace串扰(最频发)
异步Hook、后台回调、子代理回调丢失上下文,A会话Span挂载到B会话Trace;Langfuse链路父子关系错乱、标签混淆、租户数据交叉。 - 上下文污染(协程/异步逃逸)
全局事件总线广播Hook,异步协程复用陈旧RequestContext,traceId、sessionId错乱;前台流量与Cron定时任务观测标签互相覆盖。 - Span重复创建/提前关闭
多协程并发触发同一个生命周期Hook,多次创建同类型Span;上下文提前销毁导致endSpan()调用时报错、链路截断。 - 异步埋点丢失元数据
工具后台归档、记忆异步写入、流式推理分片回调取不到tenantId、userId,无法做租户成本分摊、审计追溯。 - Cron定时观测与用户流量无法区分
定时任务Hook缺少独立上下文标记,前台埋点与后台定时Span混在一起,容量、成本统计失真。
核心设计总纲
所有观测Hook执行,强制绑定Per-Request上下文载体;禁止Hook直接读写全局静态链路变量;区分三类上下文载体:Per-Request(前台用户消息)、CronContextHolder(后台定时)、SubAgent独立RequestContext(子代理);异步派生任务强制上下文快照拷贝。
观测层三层并发隔离模型:
- 载体层:Per-Request / CronContextHolder 上下文隔离(数据源隔离)
- 调度层:事件总线Hook上下文自动透传、异步快照拷贝(传播安全)
- 存储层:Span/Trace线程独立构造、父子链路强绑定、防并发写冲突(观测数据安全)
二、基础概念梳理
1. Per-Request上下文(RequestContext)回顾
每条外部消息(飞书、Websocket、HTTP、SFA2A父→子代理消息)进入网关,创建独立RequestContext,存储:traceId、sessionId、tenantId、agentName、userId、链路标签、cjkRatio、env、当前MD快照
2. 两类Hook体系(观测全部基于这两类Hook)
- 同步生命周期Hook:主线程/主协程执行(
before_model_resolve、before_tool_run、before_prompt_build) - 异步回调Hook:派生协程执行(流式chunk回调、工具异步归档、记忆后台写入、子代理结果回调)
并发风险主要集中在异步Hook,同步Hook天然复用当前协程上下文,风险较低。
3. Per-Request Hook 强制规范定义
任何观测逻辑(创建Span、记录指标、日志、Langfuse埋点)不允许脱离合法上下文载体执行;Hook触发时自动绑定当前协程活跃上下文;异步派生任务自动拷贝只读快照,禁止传递原始上下文引用。
三、四层核心并发安全机制(Per-Request Hook完整体系)
第一层:事件总线上下文自动绑定(Hook触发时自动注入载体)
OpenClaw全局事件总线(EventBus)做了一层封装:
消息入站→创建RequestContext→绑定当前协程存储→触发整条链路所有生命周期Hook。
规则:
- 同步Hook:直接读取当前协程活跃
RequestContext,无需手动传参; - 派生异步任务(子协程):总线自动捕获当前上下文,生成只读快照副本传递给异步Hook;
- Cron定时任务触发Hook:载体是
CronContextHolder,标记context_type=cron,不使用Per-Request; - 子代理spawn_subagent:新建独立
RequestContext,不继承父原始上下文引用,仅拷贝精简标签;防止父会话庞大上下文污染子代理观测链路。
禁止反模式(线上高危写法)
❌ 在Hook内部把RequestContext存入全局Map、全局静态变量;
❌ 异步回调直接持有原始上下文引用;父请求完成销毁上下文,异步回调访问已释放对象,引发错乱、空指针;
✅ 标准模式:异步自动快照拷贝,快照只读,不允许修改会话状态。
第二层:上下文三类载体区分(观测标签天然隔离)
所有Hook执行前自动识别载体类型,写入Span统一标签context_type:
user_request:Per-Request 前台用户消息(IM、API、Web)cron_task:CronContextHolder 后台定时心跳、批量归档subagent:子代理独立RequestContext
价值:
四层Langfuse可以直接按标签筛选前台流量/后台定时/子代理流量;成本、告警、观测报表天然隔离,不会混算。
第三层:Hook异步快照拷贝机制(解决最大并发风险)
1. 快照拷贝触发时机
出现以下场景,框架自动生成上下文只读快照:
- LLM流式输出on_stream_chunk 分片异步回调
- Tool执行完成后异步记忆归档Hook
- 工具后台结果持久化
- SubAgent结果异步回流父代理Hook
- Scheduler定时任务派生后台任务
2. 快照约束
- 只读快照:快照只能读取元数据(traceId、tenantId),禁止通过快照修改会话状态、修改标签;
- 快照生命周期独立于原始RequestContext;原始请求响应完成销毁,快照依然有效供异步埋点使用;
- 快照不携带可变会话指针(session状态、上下文视图),仅携带链路标识、静态标签;杜绝异步协程篡改活跃会话。
时序示例(流式推理典型场景)
用户消息入站 → 创建RequestContext(活跃) 触发before_model_resolve Hook(同步,使用活跃上下文) 发起LLM流式调用 每一块chunk触发on_stream_chunk异步Hook → 框架自动拷贝上下文只读快照传入异步回调 主线程推理完成,原始RequestContext标记可回收 异步分片Hook继续执行埋点,仅依赖快照,不受主上下文销毁影响第四层:观测数据(Span/Trace)并发写入保护
结合四层Langfuse追踪,Per-Request Hook配套三条链路构造规则:
- Trace根节点与RequestContext一一绑定
一条用户请求对应唯一Trace,所有Span父子挂载到该Trace;不允许跨Trace挂载Span(从根源杜绝Trace串扰)。 - Hook Span幂等标识防重复创建
每个生命周期Hook生成Span携带hook_name + traceId + turnSeq复合唯一标识;并发多次触发同一Hook时,先检测是否已存在同标识Span,避免重复创建。 - Span生命周期与Hook生命周期绑定
Hook启动 → startSpan() Hook执行完成/异常 → endSpan()同步Hook:同步启停;
异步Hook:快照内持有span引用,异步回调结束再关闭;
配套超时兜底:超过maxSpanTtl自动endSpan,防止链路永久悬挂。
四、完整Hook执行时序(前台用户请求标准链路)
Channel接收消息 │ 1. 创建 Per-Request RequestContext,绑定当前协程 │ 2. 事件总线触发前置Hook:onInputReceived ├─ 同步Hook:直接读取活跃上下文,创建顶层Trace │ 3. 进入Agent主循环(SessionLock串行保护同一会话) │ 依次触发: │ before_prompt_build → ContextEngineSpan │ before_model_resolve │ llm_start → Generation开始 │ │ ├─ 流式分片异步on_stream_chunk Hook │ │ → 自动拷贝上下文只读快照传入异步回调 │ 4. llm输出完成,解析工具调用指令 │ before_tool_run → ToolRunSpan │ 工具执行前后Hook │ 5. 本轮Agent循环结束 after_agent_turn │ 同步关闭本轮Trace顶层Span │ 6. HTTP/IM响应返回用户 │ → 原始活跃RequestContext标记待回收 │ 7. 派生异步任务(记忆持久化、后台埋点) │ 继续使用预先拷贝的只读快照完成剩余观测上报 │ 8. 所有异步快照任务执行完毕 → 资源彻底释放五、Cron定时任务观测Hook特殊规则(与前台严格隔离)
定时任务Hook载体为CronContextHolder,绝对不复用Per-Request,强制规则:
- Cron上下文独立生成专属cron-traceId,不挂载任何用户会话Trace;
- 标签固定
context_type=cron_task; - 定时任务尝试抢占SessionLock(非阻塞)逻辑,所有抢占、跳过事件生成独立
CronScheduleSpan; - 禁止Cron Hook创建Span挂载到用户会话Trace;杜绝后台定时操作混入用户对话链路。
六、子代理SubAgent观测Hook隔离规则
- spawn_subagent 创建子会话时,新建独立RequestContext与独立Trace;
- 父代理仅传递精简任务参数快照,不传递父完整原始上下文;
- 父Trace内部生成
SubAgentDispatchSpan,作为父侧引用锚点;子代理整套四层链路独立存在,通过span属性建立关联,不是直接父子Span嵌套;
设计目标:防止子代理海量工具Span污染父Trace视图,同时可以双向关联溯源,兼顾隔离与可追溯。
七、和现有全部核心模块联动
1. 联动 并发安全三套件
- Per-Request Hook 建立在Per-Request上下文基础之上;
- SessionLock:同一会话串行执行主Agent循环Hook,避免同一轮Turn并发创建冲突Span;
- CronContextHolder:区分定时任务上下文载体,隔离前后台观测链路。
2. 联动 SubAgent三层安全约束
子代理独立上下文,观测链路隔离;防污染机制在观测层落地,子代理内部埋点不会串入父会话。
3. 联动四层Langfuse追踪
所有Span、Generation全部由Per-Request Hook驱动创建;自动携带租户、Agent、上下文类型标签,实现分层观测采样、成本统计。
4. 联动 12+ ConditionalOnProperty 条件开关
可以按环境/租户/Agent精细化控制观测Hook启用:
ConditionalOnTenant:金融租户开启全量Hook,完整记录Generation输入输出;普通租户启用采样模式;ConditionalOnGlobalEnv:dev开启全部调试Hook,prod关闭部分高消耗调试埋点;ConditionalOnSubAgent:控制是否采集子代理完整内部Span。
5. 联动 Context Compaction
上下文压缩在ContextEngineSpan内完整记录;压缩发生时的反抖动、CJK优化标记全部写入Span标签;快照机制保证后台异步压缩埋点不会污染活跃用户上下文。
八、常见错误模式 vs 标准正确模式
错误1:异步Hook直接持有原始RequestContext引用
现象:主请求提前完成释放上下文,异步回调读取上下文出现随机错乱、trace串号。
✅ 框架自动快照拷贝;业务代码禁止手动保存原始上下文长期引用。
错误2:Cron定时Hook强行复用用户会话RequestContext
现象:定时任务Span挂载到用户Trace,报表、成本统计失真。
✅ 定时任务统一使用CronContextHolder载体,独立traceId。
错误3:在Hook全局变量缓存sessionId/traceId
多并发下全局变量被覆盖,后到来请求覆盖前一条请求标识。
✅ 所有标识从当前Hook绑定的上下文载体实时读取,禁止缓存到全局。
错误4:子代理直接复用父RequestContext
大量父会话标签、冗余上下文传入子代理;观测链路庞大混乱,存在数据泄漏风险。
✅ 子代理新建独立RequestContext,仅传递必要任务参数快照。
九、优势总结
- 从根源杜绝Trace串扰
上下文载体隔离 + 异步只读快照双保险,解决企业多租户高并发观测最棘手链路错乱问题; - 前后台流量天然隔离
用户请求 / 子代理 / Cron定时三类流量观测标签自动区分,容量规划、成本分摊、告警可以独立配置; - 异步场景完整覆盖
流式LLM、异步记忆写入、后台归档等高并发异步场景埋点稳定,不会丢失元数据; - 与整套安全、并发体系统一标准
观测层并发模型和会话锁、定时安全约束、子代理隔离保持一致设计思想,运维与开发一套规范; - 可观测审计闭环
每条Span天然携带租户、会话、上下文类型,满足政企审计、资源计量需求。
十、横向对比:原生AgentScope / Codex / Claude Code
- 原生 AgentScope
无标准化Per-Request Hook上下文透传;异步回调没有快照机制;观测埋点大多硬编码全局变量;并发场景极易Trace串扰,不支持多租户生产观测。 - OpenAI Codex
云端任务隔离,但不存在持久会话模型,没有分层上下文载体;没有区分前台/后台定时流量;Hook体系封闭,无法自定义分层观测策略。 - Claude Code
本地单会话单进程模型,无网关并发层,不存在多请求并发观测问题,缺少分布式多租户观测并发安全设计。 - OpenClaw Per-Request Hook观测并发模型
面向多渠道、多租户、前后台混合流量、多子代理并行的AI中台设计,将上下文传播规范内置进Hook事件总线,实现观测链路稳定可控,是企业级LLMOps可观测底座必备能力。
十一、生产落地最佳实践
- 业务插件禁止自行缓存RequestContext,所有埋点元数据在Hook触发时刻从载体实时读取;
- 开发自定义Observer插件时,异步回调依赖框架自动生成的只读快照,不要捕获原始上下文;
- 排查Trace错乱故障,优先查看Span标签
context_type,快速区分是用户流量、定时任务还是子代理; - 高并发生产环境,对高消耗Generation全量文本采集启用租户/Agent粒度采样开关,平衡合规与算力开销;
- Langfuse配置告警:监控
trace_cross(跨会话挂载Span)异常,一旦出现代表Hook上下文传递存在Bug。