Nader Dabit 在 X 上发布了一篇关于 Agent Hooks 的深度文章,提出了一种让 Agent 工作流变得可编程、可控制的新机制。核心思路很简单:把「应该每次都执行的规则」从提示词里抽出来,挂到 Agent 生命周期的固定节点上,变成代码层面的确定性控制。
问题:提示词不是控制机制
如果你曾经反复提醒 Agent「不要改 generated 目录」「提交前跑测试」「别读 .env 文件」,你已经遇到了提示词的极限。提示词是建议性的——模型可能记住,可能忘记,可能在复杂任务中偏离。Hooks 解决的是同一个问题,但用完全不同的方式:不是请求模型遵守,而是在关键节点插入代码拦截。
六个核心生命周期节点
Dabit 定义了六个覆盖主要开发流程的钩子:
SessionStart —— 会话开始时加载项目上下文。包括目录结构、测试命令、受保护路径、活跃事件或分支特定说明。这比放在提示词里更可靠,因为每次会话都会自动注入。
UserPromptSubmit —— 模型看到用户输入前,先检查并路由。比如提到「退款」「支付」「账单」时自动追加支付相关的约束上下文,提到「迁移」时追加迁移检查清单。
PreToolUse —— 工具调用前拦截。这是最强的预防机制:检查文件路径、shell 命令、MCP 工具参数,阻止对 generated/、.env、.git 等保护路径的写入,拦截 rm -rf、drop table 等危险命令。
PostToolUse —— 工具调用后验证。跑测试、格式化、静态分析、秘密扫描,把结果写入状态文件供后续钩子读取。这是「检查发生了什么」的地方。
Stop —— 阻止 Agent 结束回合。读取 PostToolUse 留下的质量门状态,如果测试失败就阻止完成,强制修复后才能继续。
SessionEnd —— 会话结束时清理。写审计日志、刷指标、导摘要、删临时文件。
执行模型:事件 → 匹配器 → 处理程序 → 结果
每个钩子的运行逻辑统一:
- 事件:生命周期时刻(如 PreToolUse)
- 匹配器/过滤器(可选):缩小触发范围,比如「仅文件编辑」「仅 shell 命令」
- 处理程序:shell 命令、HTTP 请求、MCP 工具调用、LLM 提示或子 Agent
- 结果:返回的上下文、决策、日志条目或状态更新
关键认知:Hooks 不是让 Agent 整体变得确定性——模型仍然可以选择不同的计划、编辑、工具调用和恢复路径。Hooks 让的是「特定检查点和副作用」变得确定性:当匹配的生命周期事件发生时,你的处理程序运行,其结果可以作为上下文、决策、副作用或记录状态应用。
实用主义的分层策略
Dabit 给出了清晰的分工建议:
- 项目说明:编码风格、架构指导、命名约定、测试偏好、示例
- Hooks:必需上下文、行动前策略、行动后验证、完成门、日志
- CI:Agent 产生 diff 后的独立验证
- 人工审查:产品判断、权衡、不可逆风险、最终所有权
把一切放进 Hooks 会造成不必要的自动化。把一切放进提示词会让必需行为依赖模型遵守。实用的分割是:提示词用于指导,Hooks 用于控制。
落地路径:从一条规则开始
不要一上来就建完整的治理系统。Dabit 建议的实施顺序:
- 第一条:PreToolUse 钩子,阻止对 generated/、.env、敏感 fixture 的编辑。容易解释、容易测试、立即有价值。
- 第二条:PostToolUse 质量门,编辑后跑最快的有用测试命令,写入
.hook-state/last_quality_gate.json。 - 第三条:Stop 钩子,读取质量门状态,失败时阻止完成。
- 之后:SessionStart 上下文、Prompt 路由、最终审计记录。
这个顺序让开发者快速获得价值:更少的重复提醒、更少的意外编辑、更快的变更反馈、更少的完成前手动检查。
为什么这很重要
Hooks 让 Agent 工作流更可靠,通过将可重复规则从模型记忆移到已知生命周期点的代码中。这对三类人意义重大:
- 个人开发者:想要更少的重复指令
- 团队:想要共享的仓库行为
- 公司:想要 Agent 在现有工程控制内运行
Agent 仍然可以推理、写代码、从错误中恢复,但测试、策略、日志和完成门作为工作流的确定性部分运行。
目前 Claude Code、Devin for Terminal、OpenAI Codex、Cursor 都已支持 Hooks 机制,具体实现细节各有差异,但生命周期模型基本一致。