Agent 系统中 Hook中间件模式有什么用?能举几个典型场景吗?OpenClaw 的 Hook 系统是怎么设计的?

魏远标 Lv7

Agent 系统中 Hook中间件模式有什么用?能举几个典型场景吗?OpenClaw 的 Hook 系统是怎么设计的?

参考答案

Hook/中间件模式就是在 Agent 的关键生命周期节点上挂自定义逻辑,不动核心代码就能做定制。

通俗理解:Hook 就像流水线上的”检查站”,产品(消息/请求)经过每个检查站时,你可以检查、修改甚至拦截它,但流水线的主体流程不需要改动。

典型场景:

1)模型调用前换模型。比如做 A/B 测试,50% 的请求走 GPT,50% 走 Claude,Hook 里随机一下就搞定,不用改业务代码。

2)构建 System Prompt 时注入额外上下文。比如当前用户偏好用中文回复,Hook 里把这个偏好塞进 prompt 就行。

3)消息收到时做内容审核。用户输入可能包含敏感内容,Hook 里调一下审核 API,命中就拦截,不让请求往下走。

4)Agent 回复后做日志记录和分析,统计 token 消耗、响应时间、模型选择分布这些指标。

OpenClaw 的 Hook 系统分两层:

内部 Hook ,事件类型覆盖 commandsessionagentgatewaymessage,支持 {type}:{action} 粒度,比如 message:receivedgateway:startup

插件 Typed Hook ,提供强类型的 Hook 名:before_model_resolve 修改模型选择、before_prompt_build 注入上下文和 System Prompt、llm_input 拦截 LLM 输入、subagent_spawning 拦截子 Agent 创建。

一个请求进来,依次经过:message:received Hook 接收消息,before_model_resolve Hook 决定用哪个模型,before_prompt_build Hook 注入上下文,llm_input Hook 拦截或修改 LLM 输入,模型调用,结果返回,后置 Hook 做日志和分析。

扩展知识

Hook 的注册和优先级

插件注册 Hook 的方式很直观:

1
2
3
4
5
6
7


typescript

复制代码

api.on("before_prompt_build", handler, { priority: 10 });

priority 控制多个 Hook 的执行顺序,数字越大优先级越高(源码中按 (b.priority ?? 0) - (a.priority ?? 0) 降序排列)。多个插件都监听同一个事件时,按 priority 从高到低依次执行。

Hook 的两种执行模式

OpenClaw 的 Hook 分为两类执行模式:

Void Hook(发射即忘):多个 handler 并行执行,不返回值,用于观测和记录。比如 message_receivedagent_endllm_input 这些都是 void hook,适合做日志、审计、监控这类不影响主流程的事情。

Modifying Hook(可修改数据):多个 handler 串行按优先级依次执行,每个 handler 的返回值会跟前面的结果合并,最终产出一个修改后的结果。比如 before_prompt_build 可以往 prompt 里注入内容,before_tool_call 可以拦截或修改工具参数。

这个区分很重要:void hook 因为并行执行所以快,但不能影响数据流;modifying hook 因为串行所以能做管道式的数据变换,但执行顺序依赖 priority。

和其他框架的 Hook 机制对比

LangChain 的 Callback 机制也是类似思路,通过 CallbackManager 注册 on_llm_starton_llm_end 这些回调。

区别在于 LangChain 的 Callback 更偏向观察和记录,OpenClaw 的 Hook 可以直接修改数据流,比如 before_model_resolve 可以换掉模型、llm_input 可以改写输入内容,拦截能力更强。

面试官追问

提问:如果多个 Hook 都想修改同一个数据,比如都想改 prompt,怎么保证最终结果是对的?

回答:靠 priority 排序(数字越大越先执行),所有 Modifying Hook 按优先级依次执行,每个 Hook 拿到的是上一个 Hook 处理后的结果,类似管道。所以写 Hook 的时候要注意,你拿到的 prompt 可能已经被前面的 Hook 改过了。如果两个 Hook 互相冲突,比如一个要加中文指令一个要加英文指令,那就得在 priority 上做好约定,或者抽出一个更高层的配置来统一管理。

提问:Hook 执行报错了怎么办?会不会把整个 Agent 请求搞挂?

回答:看设计。比较稳妥的做法是每个 Hook 执行都 try-catch 包住,单个 Hook 报错不影响后续 Hook 和主流程。OpenClaw 的内部 Hook 就是这个策略,报错了打日志然后继续往下走。但某些关键 Hook 比如内容审核,如果报错了可能应该直接终止请求,不能放行。所以更好的设计是 Hook 注册时声明 critical: true/false,critical 的 Hook 报错就中断,非 critical 的报错就跳过。

提问:Hook 系统和 AOP 有什么区别?

回答:思路很像,都是在不改核心代码的前提下插入横切逻辑。区别在于粒度和实现方式。AOP 一般基于字节码增强或动态代理,切面可以精确到某个方法的某个参数,粒度很细。Hook 系统是应用层面的事件机制,粒度是预定义的生命周期节点,不能随意切任意方法。Hook 更轻量、更容易理解,AOP 更强大但也更复杂,调试时容易踩坑。Agent 系统里用 Hook 就够了,没必要上 AOP。