在软件工程与智能体(Agent)架构开发中,Hook(钩子) 是一种极其核心的进阶设计模式。它通过在核心流程中埋下“插座”,实现了高内聚、低耦合的代码结构。
一、 为什么需要 Hooks?(痛点与核心价值)
1. 传统硬编码的痛点 (s03 阶段)
在没有引入 Hook 之前,诸如“权限检查”、“日志记录”、“耗时统计”等横切关注点(Cross-cutting Concerns)的代码,必须硬编码(Hardcode)在系统的核心业务循环(如 agent_loop)内部。
- 后果: 导致核心循环代码迅速臃肿,变成难以维护的“屎山代码”;且每次增加新功能,都必须修改核心底层逻辑,极易引入新 Bug。
2. Hooks 的解耦哲学
Hooks 允许核心流程保持绝对的纯粹和单一职责(Stay boring on purpose)。它只在特定的生命周期关键节点留出事件插座(Event Slots)。核心代码只管在走到这一步时“高喊”一声触发事件,而具体执行什么业务逻辑,完全由外部注册的函数决定。
二、 Hook 系统的底层技术实现
在底层,一个标准的 Hook 系统由 1 个核心字典(注册表) 和 2 个核心函数(注册与触发) 支撑,代码极其朴素优雅:
1. 初始化钩子注册表(HOOKS 字典)
1
2
3
4
5
6
7
|
# 蓄水池:用来存放不同节点挂载的回调函数列表
HOOKS = {
"UserPromptSubmit": [], # 用户输入后,大模型思考前
"PreToolUse": [], # 大模型决定调用工具,但工具尚未真正执行前
"PostToolUse": [], # 工具执行完毕,拿到结果后
"Stop": [] # 核心循环准备退出前
}
|
2. 注册钩子(register_hook)
1
2
3
4
5
6
7
|
def register_hook(event: str, callback):
"""
管道工:把外部定义的业务函数追加到对应事件的列表中排队。
此时函数并不运行,只是登记。
"""
if event in HOOKS:
HOOKS[event].append(callback)
|
3. 触发钩子(trigger_hooks)与高级拦截机制
1
2
3
4
5
6
7
8
9
10
11
|
def trigger_hooks(event: str, *args):
"""
引爆器:在核心循环中被动触发,按顺序遍历并执行该事件下挂载的所有钩子。
"""
for callback in HOOKS[event]:
result = callback(*args)
# 💡 核心设计:拦截机制 (Interceptor)
# 如果某一个高优先级的钩子(如安全检查)返回了非空内容,立刻中断后续执行,实现安全拦截
if result is not None:
return result
return None
|
三、 Hook 的实际使用流程(三步连贯动作)
使用 Hook 系统在开发中遵循标准的 写钩子 $\rightarrow$ 登钩子 $\rightarrow$ 踩钩子 闭环:
1. 第一步:写钩子(编写独立的业务扩展功能)
1
2
3
4
5
6
7
8
9
10
|
# 开发一个日志钩子
def log_hook(block):
print(f"【HOOK 日志】AI 准备调用工具: {block.name}")
return None # 返回 None 表示不拦截,放行
# 开发一个安全拦截钩子
def permission_hook(block):
if block.name == "bash" and "rm -rf" in block.input.get("command", ""):
return "Permission denied: 严禁执行危险的删库跑路命令!" # 触发拦截
return None
|
2. 第二步:登钩子(将功能挂载到对应的插座上)
1
2
3
|
# 在程序初始化时进行功能拼装
register_hook("PreToolUse", permission_hook) # 优先进行安全检查
register_hook("PreToolUse", log_hook) # 其次记录日志
|
3. 第三步:踩钩子(核心循环中的被动触发)
1
2
3
4
5
6
7
8
9
10
11
12
13
|
def agent_loop(messages: list):
while True:
# ... AI 思考产出 tool_use 指令 ...
# 💥 核心代码在此“踩”下触发器,解耦业务逻辑
blocked = trigger_hooks("PreToolUse", block)
if blocked:
# 如果被前面的安全钩子拦截了(返回了拦截报错字符串),则直接跳过工具执行
results.append({"type": "tool_result", "content": str(blocked)})
continue
# ... 如果没被拦截,正常运行底层的工具(如读写文件) ...
|
四、 本次 Agent 架构(s04)基于 Hook 实现的功能清单
通过 Hook 模式,s04 版本在不污染 agent_loop 主干的前提下,完美插入了以下 5 个跨切面功能:
| 钩子名称 |
挂载节点 |
核心功能说明 |
context_inject_hook |
UserPromptSubmit |
在用户提交问题进入 AI 思考前,做环境感知或前置日志记录。 |
permission_hook |
PreToolUse |
核心防线:拦截高危敏感命令(sudo, rm -rf),处理文件越界写入并弹出用户确认提示。 |
log_hook |
PreToolUse |
统一接管工具调用的前置控制台日志打印。 |
large_output_hook |
PostToolUse |
后置监控:检查工具执行输出是否超过 10 万字,过大时进行告警,防止撑爆上下文。 |
summary_hook |
Stop |
在整个会话周期结束时,自动盘点并打印本次任务一共消耗了多少次工具调用。 |