Hook 把 Agent 的核心循环和横切逻辑解耦,日志、鉴权、工具拦截这类关注点不用塞进提示词,也不用改内核。OpenClaw 用两套 Hook 覆盖两类需求:文件式内部 Hook 管命令与生命周期自动化,类型化插件 Hook(api.on(…))管运行时策略,带优先级、合并规则和 block/cancel 语义。
一、没有 Hook 会怎样
| 场景 | 无 Hook 的做法 | 有 Hook 的做法 |
|---|---|---|
| 记录 /reset 操作 | 改内核或靠日志盲猜 | 订阅 command:reset 事件 |
| 拦截危险工具调用 | 写死在工具函数里 | beforetoolcall 统一闸口 |
| 改写系统提示词 | 改核心 prompt 拼接逻辑 | beforepromptbuild 注入 |
| 出站消息审核 | 每处发送点加判断 | message_sending 统一取消 |
没有 Hook,横切逻辑要么污染核心代码,要么被复制到每个调用点,改一处要动全局。
二、典型场景
- 审计与合规:订阅命令、会话、消息事件,把 /reset、会话压缩、消息进出落盘,不动业务代码;
- 工具调用治理:beforetoolcall 拦截参数,命中策略直接 block,比如禁掉生产环境的高危命令;
- 提示词注入:beforepromptbuild 追加 prependContext、调整 systemPrompt,做品牌语气或领域知识注入;
- 出站消息控制:message_sending 返回 cancel 取消违规外发,落地内容合规;
- 遥测与诊断:走独立诊断事件总线导出耗时、token、错误率,不污染策略面;
- 生命周期联动:gatewaystart / gatewaystop 触发外部系统注册或清理。
三、OpenClaw 的两套 Hook
3.1 内部 Hook:事件驱动的小脚本
每个 Hook 是一个目录,含 HOOK.md 元数据加 handler 实现,订阅事件分 command、session、agent、gateway、message 五族。
const handler = async (event) => {
if (event.type !== "command" || event.action !== "new") return;
console.log(`[my-hook] New command triggered`);
event.messages.push("Hook executed!");
};
export default handler;
写错事件名 hook 会静默不跑,加载器会告警,openclaw hooks info 也能标出来。内部 Hook 只适合副作用自动化,不能持有长生命周期定时器或 socket,那是插件的活。
3.2 类型化插件 Hook:运行时策略
插件通过 api.on(…) 注册带契约的 Hook,挂在 agent 循环与网关管线内:beforemodelresolve、beforepromptbuild、beforetoolcall / aftertoolcall、beforeagentreply、agentend、beforecompaction / aftercompaction、messagereceived / sending / sent、sessionstart / end、gatewaystart / stop。
决策规则明确:beforetoolcall 返回 { block: true } 是终态,停掉低优先级处理器;message_sending 返回 { cancel: true } 同理。
四、选哪个扩展面
| 需求 | 用哪个 | 原因 |
|---|---|---|
| 保存快照、记日志、调外部 API | 内部 Hook | 面向操作员的副作用自动化 |
| 重写提示词、阻止工具、取消出站消息 | 类型化插件 Hook | 有优先级与 block/cancel 契约 |
| 只做遥测导出 | 诊断事件 | 独立事件总线,不占策略面 |
判断标准一句话:要像小集成一样跑自动化,用内部 Hook;要控制运行时生命周期,用插件 Hook。
五、中间件模式的边界
Hook 本质是中间件:把请求处理拆成可组合的层,每层干一件事(鉴权、日志、脱敏、限流)。层与层靠优先级排序,block/cancel 提供短路语义。副作用是 Hook 变多后执行顺序问题变明显,OpenClaw 用显式优先级加”终态”规则压住复杂度,避免出现”谁先执行看运气”的中间件地狱。
常见问题(FAQ)
Q1:内部 Hook 和插件 Hook 能同时用吗?
能。一个管事件自动化,一个管运行时策略,职责不重叠。
Q2:Hook 写错事件名会怎样?
静默不触发,但加载器告警,openclaw hooks info 也能查出来。
Q3:beforetoolcall 能拦截所有工具吗?
能,返回 { block: true } 即终态拦截,优先于低优先级处理器。