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 commitgit 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 内的事件分发组件,事件发生时执行对应钩子并收集结果。
graph LR EV[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 会话结束时 清理临时资源、归档日志

这些事件点在会话中的分布如下(其中工具调用环节会随任务的复杂程度循环多次):

graph LR SS[SessionStart
会话启动] --> 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 钩子在一次工具调用中的完整工作过程:

sequenceDiagram participant U as 用户 participant H as Harness participant K as PreToolUse 钩子 participant T as 工具 U->>H: 提交任务 H->>H: LLM 生成工具调用指令 H->>K: 触发钩子(stdin 传入工具名与参数) alt 校验通过(退出码 0) K-->>H: 放行 H->>T: 执行工具 T-->>H: 返回结果 H->>K: 触发 PostToolUse 钩子 else 校验失败(退出码 2) K-->>H: 返回拦截原因(stderr) H->>H: 把原因反馈给 LLM,要求重新决策 end H-->>U: 输出最终结果

注意"拦截后"的分支:被拦截的操作会让模型收到失败原因,模型通常会修正方案后重试——Hook 以这种"不伤和气"的方式持续纠正模型行为。

3. Hook 与相关概念的关系

3.1 概念关系图谱

graph LR Hook[Hook
确定性钩子] --> 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 的操作管控可以搭出三层防线:

graph LR OP[Agent 工具调用] --> HOOK[Hook 预检
黑名单拦截与参数校验] 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 的执行质量:工作流的每个步骤经过工具调用时,PreToolUsePostToolUse 钩子同样生效,为自动化流程提供强制校验;
  • 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)

运行逻辑:

  1. 从 stdin 读取事件 JSON,取出待执行的命令;
  2. 逐个比对黑名单关键词;
  3. 命中则向 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 自动触发,核心特性是确定性强制性
  • 常见事件覆盖会话全程:SessionStartUserPromptSubmitPreToolUsePostToolUseStopPreCompactSessionEnd 等。
  • 钩子通过 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 等事件中向对话注入额外上下文,具体能力以所用工具的官方文档为准。