Skip to content

🐣 小a的安检想法

循环转起来了,小a却想到一个问题:"如果我想在工具执行前拦一下——比如模型要跑 rm -rf,或者想确认一下——该怎么办?难道要改循环的代码?"

老z笑了:"好问题。这正是一个成熟 Agent 该有的能力——Hooks(钩子)。"


9.1 Hooks 是什么:循环里的"插手点"

🧙 Hooks 是 Agent 在循环的关键节点上预留的"插手点"。

你不用改循环本身,只要把一段逻辑"挂"上去,Agent 跑到那个节点就会自动调用它。

老z打比方:hook 就像路口的红绿灯——工具是车,红绿灯在车通过前检查它。

两个最核心的 Hook

Hook触发时机能干什么
beforeToolCall工具执行拦截危险操作、审查参数、改写参数、二次确认
afterToolCall工具执行修改结果、标记错误、决定是否提前结束

🧙 beforeToolCall 的拦截能力(重点):

它能返回"阻止执行"——模型想调一个危险工具,你在这里拦下来:

模型要调 bash("rm -rf /")

beforeToolCall 检查:
  • 命令危险?→ 返回"阻止"
  • 命令安全?→ 放行

被阻止时,模型收到"错误结果"而非静默忽略

9.2 为什么需要 Hooks:三种用途

🧙 Hooks 解决三类问题:

用途一:安全(最重要)

Agent 会执行命令、读写文件。没有拦截点,Agent 就是裸奔。 Hooks 让"危险操作前先确认/拦截"成为可能——这是第 15 章"安全与沙箱"的底层机制。

用途二:定制行为

不改循环源码,就能:给工具输出加日志、改结果格式、加二次确认弹窗。扩展逻辑全在外面,循环核心保持纯净。

用途三:解耦

循环是"发动机",Hooks 是"可插拔的仪表盘"。不加 Hooks,循环照跑;加了,行为就变。 这正是"开放扩展,封闭修改"——不用改核心代码,就能定制行为。


9.3 Hooks 的完整家族

🧙 不止 before/afterToolCall,成熟 Agent 在循环的每个节点都有 Hook:

Hook触发时机典型用途
beforeToolCall工具执行前拦截/审查/二次确认
afterToolCall工具执行后改结果/标错/终止
shouldStopAfterTurn一轮对话结束时自定义"要不要继续"
prepareNextTurn准备下一轮前改下一轮的模型/上下文
convertToLlm每次调 LLM 前过滤/转换消息格式

🐣 小a:"这么多 Hook,它们覆盖了啥?"

🧙 "覆盖了循环的每个关键节点——工具前后、轮次前后、调 LLM 前。 等于循环被'拆开了接口',你可以在任何环节插手,而不用改循环本身。这就是 Hook 的威力:开放扩展,封闭修改。"


9.4 一个真实例子:危险命令确认

把 Hooks 用在刀刃上——最常见的场景:

Agent 想执行 bash("rm -rf /tmp/cache")

beforeToolCall 触发:
  1. 检查命令是否危险(rm -rf 等)
  2. 危险 → 问用户:"确认执行 rm -rf /tmp/cache?(y/N)"

  用户拒绝 → 返回"blocked"结果给模型
  用户同意 → 放行执行

模型收到"被阻止"的错误结果 → 调整策略

🧙 为什么"被阻止"要告诉模型?

fail loud,不静默吞掉。 模型知道自己被拦了、为什么被拦,才能调整策略(换个命令、换个方式)。如果静默忽略,模型会困惑"为什么没效果",可能反复重试同一操作。


9.5 Hooks vs 其他扩展机制

是什么什么时候介入
Hooks在循环节点插手(拦/改)工具执行前后、轮次边界
Tool(第 6 章)给 Agent 新增能力模型要调用时
Skill(第 11 章)打包的能力包(工具+提示词+知识)Agent 启动时加载
MCP(第 10 章)工具的统一标准Agent 调用外部工具时

🧙 一句话区分:

  • Hooks = 改行为(拦截、修改现有流程)
  • Tool = 加能力(让 Agent 会干新事)
  • Skill = 打包能力(一组工具+提示词+知识)
  • MCP = 统一接口(让工具跨框架共享)

9.6 Hooks 的代价

🧙 Hooks 不是免费的:

  1. 循环变难预测:挂了 beforeToolCall,模型不知道自己的请求会被改——调试时要想"是不是被 hook 拦了"
  2. 串多了会乱:多个 hook 逻辑要自己在函数里组织,顺序和覆盖要想清楚
  3. 可能拖慢:hook 是 async 的(要等用户确认、查远程),慢 hook 会卡住循环

关键:给 hook 设"可中断"机制——用户按了取消,hook 必须响应,不能无限等。


9.7 拦截 vs 订阅:两种介入方式,别混为一谈

"前面讲的 hook,好像都是'拦'?"小a问,"那我想'只看不改'(比如记录日志)呢?"

老z点头:"好问题。Hook 其实有两种介入方式,很多人混着用,结果设计得一团糟。"

🧙 两种介入方式:

拦截式(intercept)订阅式(subscribe)
干什么在节点改流程(拦下/改写/决定放不放行)在节点旁听(观察、记录,不干预)
典型例子beforeToolCall 阻止危险命令记一条"模型调了 bash"的日志
返回值会影响流程(放行/阻止/改写)不影响流程(纯观察)
心智模型门卫(决定谁进谁不进)摄像头(只记录,不拦人)

设计 Hook 时先问一句:我是要"改流程"还是"看流程"? 想改,用拦截式;只想记录,用订阅式——别把记录逻辑写进拦截 hook 里,也别用订阅式 hook 假装能拦截。

生命周期 hook:不止工具级

🧙 前几节讲的都是"循环内"的 hook。成熟框架还有"生命周期"维度的 hook:

Hook触发时机典型用途
onSessionStart会话开始时加载长期记忆(第 8 章)、初始化上下文
onTurnStart每轮开始时准备这轮的模型参数
onSessionEnd会话结束时持久化记忆、清理资源

循环内 hook 管"每一步",生命周期 hook 管"整个会话的起止"。 两者合起来,才覆盖了 Agent 的完整生命周期。

多个 hook 的执行顺序

🧙 挂了多个 hook,顺序怎么定?

  • 按注册顺序执行(先挂的先跑)是常见约定——所以挂载顺序本身就是设计
  • 拦截式 hook 通常串行(一个决定阻止,后面的就不用跑了)
  • 订阅式 hook 可以并行(都只是看,互不影响)

老z类比:"这就像 Express 的中间件——app.use(...) 挂一串,请求按顺序穿过,每个中间件可以选择'放行(next())'或'拦下'。Hook 就是 Agent 循环里的中间件。 读过 Node.js 后端的读者,一秒就懂。"

🐣 小a:"原来 Hook 的本质,是把『循环』拆成了一段段可以插入的节点?"

🧙 "对。Hook 的完整画像:拦截改流程、订阅看流程、生命周期管起止、顺序按注册。 深度源码版见附录 H。"


本章小结

🐣 小a的红绿灯

┌──────── Hooks ────────┐
│                        │
│  • Hook = 循环的插手点 │
│    beforeToolCall(前) │
│    afterToolCall(后)  │
│                        │
│  • 三用途:安全/定制/  │
│    解耦               │
│                        │
│  • 完整家族:工具前后/ │
│    轮次/调LLM前        │
│                        │
│  • 拦截要 fail loud:   │
│    告诉模型"你被拦了"  │
│                        │
│  • 两种介入:           │
│    拦截(改流程)/      │
│    订阅(看流程)        │
│                        │
│  • 生命周期:           │
│    onSessionStart/End  │
│                        │
│  • 代价:难预测/串乱/   │
│    拖慢 → 要可中断     │
└────────────────────────┘

关键认知:Hooks 让 Agent 的循环"可插手"——不改核心代码就能拦截、定制、解耦。它是安全机制(危险操作拦截)的底层骨架,也是"开放扩展、封闭修改"的体现。设计时分清"拦截改流程、订阅看流程"。深度源码版见附录 H。


课后实验

  1. 设计拦截:为你的 Agent 设计一个 beforeToolCall——拦截 bash 工具里的危险命令。
  2. 体会 fail loud:对比"被拦截后模型收到错误提示" vs "被静默忽略",模型行为有何不同。
  3. 画 Hook 位置图:在循环图上标出 before/afterToolCall、轮次边界、调 LLM 前这几个 hook 的位置。
  4. 区分两种介入:为同一个节点(如 afterToolCall)分别写一个"拦截式"(修改结果)和一个"订阅式"(记录日志)的 hook,感受两者写法的差异。

下一章:工具能力怎么跨框架共享?小a遇到了工具的"巴别塔危机"。 → 第 10 章 · MCP