Hook(钩子)是一种事件驱动的扩展机制:用户预先定义好脚本或命令,把它们"挂"在 Agent 执行生命周期的特定事件点上(例如工具调用执行前、工具执行后、会话结束时),事件一旦发生,Agent 的执行引擎就自动触发这些脚本。它的核心价值在于提供确定性控制——模型的行为由大语言模型(LLM)概率生成,提示词中的约束对模型只有"建议"效果,而 Hook 由系统强制执行,任何"必须遵守"或"必须禁止"的规则都可以通过 Hook 落地。
本教程将系统讲解 Hook 的概念定义、工作机制、事件类型与输入输出协议,梳理它与 Skill、MCP、沙箱等概念的分工,并结合主流 Agent 工具介绍典型应用场景,帮助你把 Hook 用作 Agent 工程化的关键抓手。
前置教程
如想快速开始学习本教程,你可能需要先完成以下前置教程:
- Agent概念详解,Agent 的执行过程是 Hook 的挂载对象,Hook 在 Agent 生命周期的关键节点上自动触发。
- Harness概念详解,Hook 挂载在 Harness 的事件总线上,理解 Harness 的执行链路有助于定位 Hook 的工作位置。
- ToolCalling工具调用技术分析与应用教程,工具调用执行前后(PreToolUse、PostToolUse)是 Hook 最常用的事件点。
- Skill概念详解,Skill 与 Hook 是 Agent 的两大扩展机制,对比学习能更准确把握两者的分工。
1. 什么是 Hook
1.1 概念定义
Hook(钩子)在软件工程中泛指一种回调机制:系统在执行流程的特定位置预留挂载点,当流程运行到该位置时,自动调用预先注册的外部代码。
这一思想由来已久,你很可能早已接触过它的各种形态:
| 形态 | 挂载点 | 典型用途 |
|---|---|---|
| 操作系统钩子函数 | 键盘、鼠标等系统事件 | 输入法监听按键、自动化工具 |
| Git Hooks | git commit、git push 等操作前后 |
提交前跑代码检查、校验提交信息 |
| Webhook | 外部服务的业务事件 | 支付成功后回调你的接口发货 |
在 AI Agent 语境下,Hook 是用户预先定义的命令脚本,挂载在 Agent 生命周期的特定事件点上,由 Agent 的执行引擎在事件发生时自动触发执行。
可以把 Agent 想象成一条工厂流水线,Hook 就是流水线上的质检工位:产品(操作)每经过一个工位,质检脚本自动运行——不合格的直接拦下,合格的放行继续。全程不需要工人(用户)盯着,也不需要流水线本身(LLM)配合。
1.2 Hook 的两个关键特性
| 特性 | 含义 | 带来的价值 |
|---|---|---|
| 确定性 | 触发条件是明确的事件点,执行逻辑是用户编写的脚本 | 同样的输入必然产生同样的结果,与模型的"发挥"无关 |
| 强制性 | 由执行引擎在系统层触发,模型无法绕过或"忘记" | 把提示词中的口头约定升级为强制规则 |
1.3 为什么 Agent 需要 Hook
现代 Agent 工具的执行核心是 LLM,它在带来灵活性的同时也带来了不确定性——模型可能遗忘约束、误判风险,长任务中尤其明显:
| 场景 | 只靠提示词约束 | 加入 Hook 之后 |
|---|---|---|
| 提交信息规范 | 模型"通常"遵守,偶尔忘记 | PreToolUse 钩子强制校验提交信息格式 |
| 危险命令防护 | 依赖模型识别风险,存在漏判 | 命中黑名单直接拦截,模型无法执行 |
| 代码风格统一 | 提醒模型注意格式,效果不稳定 | PostToolUse 钩子在每次写文件后自动格式化 |
| 长任务完成感知 | 模型说完了,用户却不在屏幕前 | Stop 钩子把完成通知推送到手机 |
提示词约束影响模型"想做什么",Hook 决定系统"允许做什么"。两者配合,才能构成完整的 Agent 行为控制体系。
2. Hook 的工作机制
2.1 事件驱动模型
Hook 的运行依赖三个角色协同:
- 事件源:Harness 的执行流程,在关键节点对外发布事件;
- 钩子:用户注册的脚本,声明自己关心的事件;
- 调度器:Harness 内的事件分发组件,事件发生时执行对应钩子并收集结果。
发布事件] --> DISP[事件调度器
匹配已注册钩子] DISP --> H1[钩子脚本 A
校验与拦截] DISP --> H2[钩子脚本 B
格式化与通知] H1 --> FB[结果反馈
放行或阻断] H2 --> FB FB --> EV style EV fill:#ebdef0,stroke:#8e44ad,stroke-width:2px style DISP fill:#d6eaf8,stroke:#2980b9,stroke-width:2px style H1 fill:#fef9e7,stroke:#b7950b,stroke-width:2px style H2 fill:#fef9e7,stroke:#b7950b,stroke-width:2px style FB fill:#d5f5e3,stroke:#27ae60,stroke-width:2px
2.2 Agent 生命周期中的常见事件点
主流 Agent 工具围绕"一次会话"的完整生命周期设计了一组标准事件。以 Claude Code 的事件体系为例,大量兼容其生态的 Agent 工具沿用了相同的命名与结构:
| 事件 | 触发时机 | 典型用途 |
|---|---|---|
SessionStart |
会话启动或恢复时 | 初始化环境、注入项目背景 |
UserPromptSubmit |
用户提交提示词时 | 注入项目规范、过滤敏感内容 |
PreToolUse |
工具调用执行前 | 拦截危险命令、校验参数 |
PostToolUse |
工具执行完成后 | 自动格式化、触发增量检查 |
Notification |
Agent 发出通知时 | 转发提醒到桌面或手机 |
Stop |
主 Agent 完成响应时 | 任务完成通知、收尾审计 |
SubagentStop |
子 Agent 完成时 | 汇总子任务结果 |
PreCompact |
上下文压缩前 | 备份完整对话历史 |
SessionEnd |
会话结束时 | 清理临时资源、归档日志 |
这些事件点在会话中的分布如下(其中工具调用环节会随任务的复杂程度循环多次):
会话启动] --> UPS[UserPromptSubmit
提交提示词] UPS --> LLM[LLM 生成响应] LLM --> PTU[PreToolUse
工具执行前] PTU --> T[工具执行] T --> PTD[PostToolUse
工具执行后] PTD --> LLM LLM --> ST[Stop
响应完成] ST --> PC[PreCompact
上下文压缩前] PC --> SE[SessionEnd
会话结束] style SS fill:#d6eaf8,stroke:#2980b9,stroke-width:2px style UPS fill:#d5f5e3,stroke:#27ae60,stroke-width:2px style LLM fill:#ffecd6,stroke:#e67e22,stroke-width:2px style PTU fill:#fadbd8,stroke:#c0392b,stroke-width:2px style T fill:#ffecd6,stroke:#e67e22,stroke-width:2px style PTD fill:#fadbd8,stroke:#c0392b,stroke-width:2px style ST fill:#d5f5e3,stroke:#27ae60,stroke-width:2px style PC fill:#ebdef0,stroke:#8e44ad,stroke-width:2px style SE fill:#d6eaf8,stroke:#2980b9,stroke-width:2px
2.3 Hook 的输入与输出协议
钩子脚本与执行引擎之间通过标准化协议交互,不依赖任何特定编程语言或 SDK。
输入:事件发生时,Harness 把事件上下文序列化为 JSON,通过标准输入(stdin)传给钩子脚本。以 PreToolUse 事件为例:
{
"session_id": "abc123",
"cwd": "/home/user/project",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": {
"command": "rm -rf ./build"
}
}
输出:钩子通过退出码与标准输出(stdout、stderr)向引擎反馈决策。以 Claude Code 的约定为例:
| 退出码 | 含义 | 效果 |
|---|---|---|
0 |
成功 | 流程继续;UserPromptSubmit 的 stdout 内容会注入对话上下文 |
2 |
阻断 | 当前操作被阻止,stderr 内容反馈给模型作为修正依据 |
| 其他非零 | 非阻断错误 | 记录错误日志,流程继续 |
除了退出码,钩子也可以输出结构化 JSON 进行更精细的控制,例如 {"decision": "block", "reason": "禁止执行 force push"}。
钩子脚本只通过"标准输入读 JSON、退出码给结论"与引擎交互,因此可以用任何语言编写——Python、Bash、PowerShell、Node.js 均可。
2.4 一次完整触发的流程
以"拦截危险命令"为例,观察 PreToolUse 钩子在一次工具调用中的完整工作过程:
注意"拦截后"的分支:被拦截的操作会让模型收到失败原因,模型通常会修正方案后重试——Hook 以这种"不伤和气"的方式持续纠正模型行为。
3. Hook 与相关概念的关系
3.1 概念关系图谱
确定性钩子] --> Harness[Harness
执行引擎] Harness --> Agent[Agent
智能体] Agent --> Skill[Skill
行为模板] Agent --> MCP[MCP
工具集成] Agent --> Sandbox[Sandbox
安全沙箱] style Hook fill:#fef9e7,stroke:#b7950b,stroke-width:2px style Harness fill:#ebdef0,stroke:#8e44ad,stroke-width:2px style Agent fill:#e8f4f8,stroke:#1a6b8a,stroke-width:2px style Skill fill:#fdebd0,stroke:#e67e22,stroke-width:2px style MCP fill:#fadbd8,stroke:#c0392b,stroke-width:2px style Sandbox fill:#d5f5e3,stroke:#27ae60,stroke-width:2px
3.2 Hook vs Skill
Skill(见 Skill概念详解)与 Hook 是 Agent 最常用的两种扩展机制,两者经常被混淆:
| 维度 | Hook | Skill |
|---|---|---|
| 本质 | 挂载在事件点上的可执行脚本 | 注入系统提示的行为指令模板 |
| 触发方式 | 事件驱动,由引擎自动触发 | 用户显式调用或模型自主加载 |
| 执行者 | 操作系统直接运行脚本 | LLM 阅读指令后按指令行事 |
| 确定性 | 强制执行,结果确定 | 依赖模型理解与配合,属概率性约束 |
| 适用场景 | 校验、拦截、格式化、通知 | 流程指导、领域知识、操作规范 |
核心分工:需要"必须发生"或"必须禁止"的规则用 Hook 实现;需要"指导模型如何做事"的知识用 Skill 承载。实际项目中两者常常配合——Skill 告诉模型遵守提交规范,Hook 在提交动作上兜底校验。
3.3 Hook vs MCP 工具
MCP(见 MCP概念详解与应用完整教程)为 Agent 接入外部工具提供了标准协议,它解决"模型能调用什么";Hook 解决"调用前后系统强制做什么":
| 维度 | MCP 工具 | Hook |
|---|---|---|
| 角色 | 能力的提供者,等待模型调用 | 规则的执行者,监听事件并介入 |
| 触发方向 | 模型主动发起调用 | 事件发生时系统自动触发 |
| 决策权 | 返回数据给模型参考 | 可直接放行、阻断或补充上下文 |
| 类比 | 餐厅菜单上的菜 | 后厨的食品安全检查员 |
3.4 Hook 与权限审批、沙箱:三层防线
结合 ToolCalling工具调用技术分析与应用教程 中的审批机制与 Sandbox沙箱概念详解 中的沙箱机制,Agent 的操作管控可以搭出三层防线:
黑名单拦截与参数校验] HOOK --> APPROVE[权限审批
用户人工把关] APPROVE --> SB[Sandbox 沙箱
环境兜底隔离] SB --> OK[安全执行] style OP fill:#e8f4f8,stroke:#1a6b8a,stroke-width:2px style HOOK fill:#fef9e7,stroke:#b7950b,stroke-width:2px style APPROVE fill:#d6eaf8,stroke:#2980b9,stroke-width:2px style SB fill:#fadbd8,stroke:#c0392b,stroke-width:2px style OK fill:#d5f5e3,stroke:#27ae60,stroke-width:2px
| 防线 | 机制 | 特点 |
|---|---|---|
| 第一层 | Hook 预检 | 规则明确、成本极低,可自动拦截高频风险 |
| 第二层 | 权限审批 | 人工判断,适合低频高危操作 |
| 第三层 | 沙箱 | 环境级隔离,前两层失手也能限制损失 |
Hook 处于最前端:它以极低成本自动处理大量"规则明确"的风险,把人工审批的注意力留给真正需要判断的场景。
3.5 Hook 与 Workflow
Workflow概念详解 中的多步骤工作流同样运行在 Harness 之上,Hook 与 Workflow 的关系体现在两点:
- Hook 保障 Workflow 的执行质量:工作流的每个步骤经过工具调用时,
PreToolUse、PostToolUse钩子同样生效,为自动化流程提供强制校验; - Workflow 可复用 Hook 的产物:钩子注入的上下文(如
UserPromptSubmit注入的项目规范)会进入会话上下文,影响工作流中后续步骤的决策。
4. Hook 在 AI Agent 中的应用
4.1 主流工具的 Hook 支持情况
| 工具 | Hook 机制 | 配置位置 |
|---|---|---|
| Claude Code | 完整事件体系(PreToolUse、PostToolUse 等),支持匹配器与退出码协议 | 项目级或用户级 settings.json |
| ZCode | 与 Claude Code 同构的事件与匹配器设计 | 项目级或用户级 settings.json |
| Codex | notify 配置项,在关键事件(如回合完成)时调用外部程序 |
用户级 ~/.codex/config.toml |
4.2 配置结构
以 Claude Code 为例,钩子在 JSON 配置中按"事件名、匹配器、命令列表"三层组织:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python .claude/hooks/guard_dangerous_cmd.py"
}
]
}
],
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "python .claude/hooks/auto_format.py"
}
]
}
]
}
}
配置要点:
- matcher 用正则表达式匹配工具名,
Write|Edit表示对 Write 和 Edit 两个工具生效,留空则匹配全部工具; - 同一事件可注册多条钩子,按配置顺序依次执行;
- 项目级配置与用户级配置可以叠加,用户级钩子对所有项目生效。
4.3 场景一:拦截危险命令(PreToolUse)
下面是一个 Bash 命令黑名单钩子,命中黑名单时以退出码 2 阻断:
import json
import sys
data = json.load(sys.stdin)
command = data.get("tool_input", {}).get("command", "")
FORBIDDEN_KEYWORDS = ["rm -rf /", "git push --force", "DROP TABLE"]
for keyword in FORBIDDEN_KEYWORDS:
if keyword in command:
print(f"命令已拦截:包含禁止关键词 {keyword}", file=sys.stderr)
sys.exit(2)
sys.exit(0)
运行逻辑:
- 从 stdin 读取事件 JSON,取出待执行的命令;
- 逐个比对黑名单关键词;
- 命中则向 stderr 写入拦截原因并以退出码 2 结束,引擎会把原因反馈给模型;未命中则以退出码 0 放行。
4.4 场景二:写文件后自动格式化(PostToolUse)
每次 Agent 编辑 Python 文件后自动执行格式化,保证代码风格统一:
import json
import subprocess
import sys
data = json.load(sys.stdin)
file_path = data.get("tool_input", {}).get("file_path", "")
if file_path.endswith(".py"):
subprocess.run(["black", "--quiet", file_path], check=False)
sys.exit(0)
这类钩子体现了 Hook 的典型价值:格式化动作与模型"是否记得遵守格式规范"完全解耦,只要写文件动作发生,格式化就必然执行。
4.5 场景三:任务完成提醒(Stop)
长时间运行的任务结束后,把结果推送到手机或桌面(通知通道的搭建可参考 一键配置DeepSeek Harness等Agent桌面、手机提醒教程):
import json
import urllib.request
NOTIFY_URL = "http://127.0.0.1:8000/notify"
payload = json.dumps({"title": "Agent 任务完成", "body": "主会话响应已结束"}).encode()
req = urllib.request.Request(NOTIFY_URL, data=payload, headers={"Content-Type": "application/json"})
urllib.request.urlopen(req, timeout=3)
配合 Stop 事件注册后,你离开屏幕时也能第一时间收到 Agent 的完工通知。
4.6 设计原则与注意事项
| 原则 | 说明 |
|---|---|
| 快速执行 | 钩子在关键路径上运行,脚本要快,超过超时时间会被终止 |
| 幂等安全 | 同一事件可能触发多次,脚本重复执行不应产生副作用 |
| 精准匹配 | matcher 尽量收窄范围,避免对无关工具全量触发 |
| 明确报错 | 拦截时在 stderr 写清原因,给模型修正的依据 |
| 最小拦截 | 黑名单只放确定危险的规则,宽泛规则会造成大量误伤 |
| 版本管理 | 钩子脚本纳入项目仓库,团队共享同一套规则 |
钩子拥有拦截模型操作的权力,脚本本身的来源必须可信:只运行自己或团队审查过的钩子,来路不明的钩子脚本等于给项目开了后门。
5. 总结
5.1 核心内容回顾
- Hook(钩子) 是挂载在 Agent 生命周期事件点上的用户自定义脚本,由 Harness 自动触发,核心特性是确定性与强制性。
- 常见事件覆盖会话全程:
SessionStart、UserPromptSubmit、PreToolUse、PostToolUse、Stop、PreCompact、SessionEnd等。 - 钩子通过 stdin 读 JSON、退出码给决策与引擎交互;退出码 2 可阻断操作,并把 stderr 中的原因反馈给模型修正。
- Hook 与 Skill 的分工:Hook 负责"强制规则",Skill 负责"行为指导",两者经常配合使用。
- Hook、权限审批、沙箱构成 Agent 操作管控的三层防线,Hook 位于最前端做低成本自动预检。
- 典型应用包括危险命令拦截、写后自动格式化、任务完成通知与审计日志。
5.2 常见问题与解答
问:Hook 和 MCP 工具功能有重叠,应该选哪个?
答:两者定位不同。MCP 工具扩展 Agent "能做什么",模型按需调用;Hook 约束 Agent "做事时必须遵守什么",由系统强制执行。需要新增能力时用 MCP,需要强制规则时用 Hook。
问:钩子脚本自身出错会影响 Agent 运行吗?
答:退出码为 2 时会按设计阻断当前操作;其他非零退出码通常只记录错误、不中断流程。为避免影响正常使用,钩子脚本应做好异常捕获,宁可放行,也尽量不因脚本自身崩溃而阻断。
问:提示词里已经写了规则,还需要 Hook 吗?
答:看规则的重要程度。提示词约束属概率性建议,模型在长上下文中可能遗忘;涉及安全红线、提交规范、代码风格等"必须遵守"的规则,建议用 Hook 兜底。
问:钩子能修改工具参数或补充上下文吗?
答:可以。部分工具链支持钩子通过结构化 JSON 输出改写工具参数,或在 UserPromptSubmit 等事件中向对话注入额外上下文,具体能力以所用工具的官方文档为准。
举手提问