原文:Hooks + Plugin Architecture
双层 Hook
OpenClaw 有两套独立的 Hook 系统:
1. Internal Hooks(Gateway 层)
事件驱动的 TypeScript 函数,在 Gateway 进程内执行。
<hook-directory>/
├── HOOK.md ← 元数据(frontmatter)+ 文档
└── handler.ts ← TypeScript 处理函数
发现来源(按优先级):
- Bundled hooks(随 OpenClaw 安装)
- Plugin hooks(已安装的插件)
- Managed hooks(
~/.openclaw/hooks/) - Workspace hooks(
<workspace>/hooks/,默认禁用——安全考虑)
内置 Hook:
| Hook | 事件 | 作用 |
|---|---|---|
session-memory | /new, /reset | 保存会话上下文到记忆文件 |
bootstrap-extra-files | agent:bootstrap | 注入 workspace 文件到上下文 |
command-logger | 所有命令 | 记录到 ~/.openclaw/logs/commands.log |
boot-md | gateway:startup | Gateway 启动时运行 BOOT.md |
事件类型:
- 命令事件:
command:new,command:reset,command:stop - 会话事件:
session:compact:before/after,session:patch - Agent 事件:
agent:bootstrap - Gateway 事件:
gateway:startup - 消息事件:
message:received,message:sent
2. Plugin Hooks(Agent 生命周期层)
在 Agent Loop 和 Gateway 管线内部的扩展点,通过 Plugin SDK 注册。
| Hook | 时机 | 能力 |
|---|---|---|
before_model_resolve | 模型解析前 | 动态覆盖 provider/model |
before_prompt_build | Prompt 组装前 | 注入上下文/系统提示 |
before_tool_call | 工具调用前 | 阻断({block: true}) |
after_tool_call | 工具调用后 | 检查/修改结果 |
tool_result_persist | 结果持久化前 | 同步变换工具输出 |
agent_end | Agent 完成 | 检查最终消息 |
before/after_compaction | 压缩前后 | 观察/注释压缩过程 |
message_received | 收到消息 | 预处理 |
message_sending | 发送前 | 取消({cancel: true}) |
session_start/end | 会话生命周期 | 初始化/清理 |
与 Claude Code Hook 的对比
| Claude Code | OpenClaw Internal | OpenClaw Plugin | |
|---|---|---|---|
| 语言 | Shell / HTTP / LLM prompt / Agent | TypeScript | TypeScript (SDK) |
| 配置 | JSON(settings.json) | HOOK.md + handler.ts | Plugin 代码 |
| 阻断能力 | exit 2 / permissionDecision: deny | 有限 | {block: true} / {cancel: true} |
| 工具拦截 | PreToolUse(匹配工具名) | 无 | before_tool_call |
| 输出修改 | 无 | 无 | tool_result_persist |
| Prompt 注入 | 无(用 CLAUDE.md) | agent:bootstrap | before_prompt_build |
| 模型覆盖 | 无 | 无 | before_model_resolve |
关键差异:
- Claude Code 的 Hook 是配置驱动(JSON + shell 脚本),零编程门槛
- OpenClaw 的 Internal Hook 是代码驱动(TypeScript),更灵活但门槛更高
- OpenClaw 的 Plugin Hook 能修改 Agent Loop 的内部状态,Claude Code 的 Hook 只能在 Loop 外部运行
信任边界
- Bundled/Managed hooks:信任代码
- Workspace hooks:需要
openclaw hooks enable显式启用——防止仓库中的恶意 hook
知识检测
概念理解题
-
为什么 OpenClaw 需要两层 Hook(Internal + Plugin),而 Claude Code 只有一层?
-
OpenClaw 的
tool_result_persisthook 能在工具输出写入会话记录之前修改它——这有什么用?Claude Code 有类似能力吗? -
Workspace hooks 默认禁用的安全原因是什么?在什么情况下你会启用它?
迁移思考题
- 你的 Hook SlipBox 卡片描述了 Hook 的通用概念。在学完 Claude Code 和 OpenClaw 两套 Hook 系统后,你对 Hook 概念有什么新的理解?(提示:考虑”确定性 vs 概率性”、“外部 vs 内部”这两个维度)
参见
参考答案
1. 为什么两层 Hook
因为 OpenClaw 有两个层次的扩展需求。Internal Hook 解决”Gateway 运维”层面的问题(启动时执行脚本、命令触发时记录日志),这些是事件驱动的、和 Agent 推理无关的。Plugin Hook 解决”Agent 行为”层面的问题(修改模型选择、注入上下文、拦截工具调用),这些深入到 Agent Loop 内部。Claude Code 只有一层是因为它的架构更简单——没有 Gateway/Agent 的分离,所有东西在一个进程里。
2. tool_result_persist 的用途
用例:(1) 隐私过滤——工具返回了用户的敏感信息(手机号、地址),在持久化到 session 记录前脱敏 (2) 格式化——把大量工具输出压缩为摘要再存储,减少 session 文件膨胀 (3) 注解——给工具结果加上元数据(执行耗时、成本)。Claude Code 的 PostToolUse Hook 只能”观察然后反馈给 Claude”,不能修改已经写入的结果。
3. Workspace hooks 默认禁用的原因
Workspace hooks 存在于项目仓库中——任何能 push 到仓库的人都可以注入 Hook 代码。如果你 clone 一个别人的仓库并运行 OpenClaw,仓库中的恶意 Hook 会在你的 Gateway 上执行(以你的权限)。默认禁用 + 显式 openclaw hooks enable = 信任边界——你必须审查后主动信任。启用的场景:你自己的项目仓库、你信任的团队仓库。
4. Hook 概念的新理解
学完两套系统后,Hook 可以在两个维度上分类:
确定性 vs 概率性:Claude Code 的 command 和 OpenClaw 的 Internal Hook 是确定性的(脚本执行);Claude Code 的 prompt 类型是概率性的(LLM 判断)。
外部 vs 内部:Claude Code 的 Hook 全部在 Agent Loop 外部运行(在接缝处触发但不修改内部状态);OpenClaw 的 Plugin Hook 在 Loop 内部运行(可以修改模型选择、Prompt 内容、工具输出)。
内部 Hook 更强大但更危险——bug 会直接影响 Agent 行为;外部 Hook 更安全但能力有限。