在 Codex 动手前拦一下:Hooks 如何给 Agent 工作流加上确定性护栏
提示词可以告诉 Codex“不要执行危险命令”,但语言规则依赖模型理解,并不是确定性控制。
如果你希望在固定时机执行一段明确检查,例如:
- 提交提示词前检测是否误贴密钥;
- Bash 执行前拦截危险删除;
- 工具运行后检查测试是否失败;
- 对话结束前要求补充验证;
- 会话启动时加载本地项目说明;
就可以考虑使用 Hooks。
Hooks 是什么
Hooks 是 Codex 的生命周期扩展机制。它会在固定事件发生时运行外部脚本。
一次典型任务可能经历:
SessionStart
→ UserPromptSubmit
→ PreToolUse
→ PermissionRequest
→ PostToolUse
→ Stop
发生上下文压缩时还有 PreCompact 和 PostCompact;启动子代理时还有 SubagentStart 与 SubagentStop。
Hooks 的优势是确定性:脚本写了什么检查,就执行什么检查。它的风险也来自这里:Hook 本身就是会被自动运行的代码,因此必须安全、短小和可审查。
一个 Hook 有三层
Hooks 配置通常包含:
- 事件:什么时候触发;
- matcher:匹配哪些工具或场景;
- handler:真正执行的命令。
例如,只在 Bash 调用前执行检查:
{
"hooks": {
"PreToolUse": [
{
"matcher": "^Bash$",
"hooks": [
{
"type": "command",
"command": "python3 /repo/.codex/hooks/check_bash.py",
"timeout": 10
}
]
}
]
}
}
检查脚本从标准输入读取事件信息,分析即将执行的命令,再返回允许、拒绝或补充上下文的结果。
常见事件怎样选择
| 事件 | 适合做什么 |
|---|---|
SessionStart |
加载项目说明和本地上下文 |
UserPromptSubmit |
检查敏感信息、提醒补充复现步骤 |
PreToolUse |
在命令执行前拦截危险操作 |
PermissionRequest |
对额外权限请求做允许或拒绝 |
PostToolUse |
根据工具输出要求继续验证 |
Stop |
缺少测试或交付总结时继续一轮 |
PreCompact / PostCompact |
在上下文压缩前后保存和恢复关键信息 |
需要注意:PostToolUse 发生在工具执行之后。它可以改变 Codex 接下来看到的反馈,却不能撤销已经发生的副作用。
所以危险命令必须尽量在 PreToolUse 阶段拦截。
配置放在哪里
常见位置包括:
~/.codex/hooks.json
~/.codex/config.toml
<repo>/.codex/hooks.json
<repo>/.codex/config.toml
用户级 Hook 可以跨项目使用;仓库级 Hook 适合团队共享,但只有在项目配置被信任后才应该加载。
多个位置的匹配 Hook 可能同时运行,因此不要依赖固定执行顺序。团队也应避免在多个 Hook 中对同一事件做互相冲突的决策。
Hooks 适合做什么
- 检查明确的敏感信息模式;
- 阻止已知危险命令;
- 自动补充固定上下文;
- 提醒执行测试和交付检查;
- 记录经过脱敏的任务审计信息。
Hooks 不应该做什么
- 承载大量业务逻辑;
- 替代单元测试和 CI;
- 自动发布、部署或迁移数据库;
- 下载远程脚本后直接执行;
- 绕过沙盒和人工审批;
- 依赖不稳定的私有 transcript 格式。
Hooks 是护栏,不是另一个隐藏的应用程序。
信任机制不能跳过
非托管的 command Hook 在运行前需要审查。Hook 内容发生变化后,也应该重新确认。
审查时至少检查:
- 会执行什么命令;
- 会读取和写入哪些路径;
- 是否访问网络;
- 是否包含凭据;
- 失败时会阻止任务还是静默继续;
- Windows、macOS 和 Linux 路径是否兼容。
团队仓库里的 .codex/hooks.json 应进入正常代码 review。
AGENTS.md 与 Hooks 怎样配合
可以这样理解:
AGENTS.md 负责说明规则
Hooks 负责执行确定性检查
沙盒负责限制可操作范围
审批负责处理越界请求
CI 负责验证最终结果
例如:
AGENTS.md写“不允许修改构建产物”;PreToolUse检查命令是否直接写入dist/;PostToolUse发现测试失败后提醒继续处理;Stop在没有验证结果时要求补充总结。
写在最后
Hooks 最有价值的地方,是把少量明确规则从“希望 Codex 记得”变成“每次都会检查”。
但 Hook 越多,隐藏行为也越多。最好的 Hook 往往只做一件事:输入明确、逻辑短小、输出可解释、失败可以定位。
先从一个真正需要的护栏开始,比一次搭建复杂的生命周期系统更可靠。