HookMatcher 通过正则模式把 hook 函数连接到工具调用。匹配器写对了,hook 只对需要它的工具触发——精准定位、不浪费处理、不意外匹配。
注册模式
HookMatcher(
matcher='process_refund', # 正则匹配工具名
hooks=[enforce_refund_limit] # 要运行的 hook 函数
)
这个 hook 只对 process_refund 触发。其他工具不会触发它。
正则意味着子串匹配
matcher='update' 匹配任何包含 “update” 的工具:update_account、get_status_update、update_settings。这会捕获到不想要的工具。
修复:锚定模式。matcher='^update_account$' 只匹配 update_account——起始(^)和结束($)锚点确保精确匹配。
对于管控型 hook(阻止、拒绝),用锚定模式。退款限制 hook 意外地对 check_refund_status 触发就是误触。
没有 matcher = 对每个工具调用都触发
省略 matcher 字段意味着 hook 对所有工具调用运行。这对通用关注点(审计日志)是对的,但对定向管控是浪费。
某系统用一个无 matcher 的 hook 对全部 8 个工具跑安全检查 + 变更日志。分析显示 40% 的 hook 处理时间浪费在 Read、Grep、Glob、Task 和 WebSearch 上——这些都不需要管控检查。
修复:拆成定向 matcher。SDK 级过滤比 hook 函数内部的提前返回逻辑更高效。
用独立 HookMatcher 做分层管控
三层管控,三个独立注册:
# 第 1 层:所有工具的审计日志
HookMatcher(hooks=[audit_log]) # 无 matcher → 所有工具
# 第 2 层:文件修改工具的路径校验
HookMatcher(matcher='Edit|Write', hooks=[validate_path])
# 第 3 层:shell 执行的命令黑名单
HookMatcher(matcher='Bash', hooks=[block_dangerous])
每个 HookMatcher 精确定位需要管控的工具。Read/Grep/Glob 只走审计日志。Edit/Write 走审计 + 路径校验。Bash 走审计 + 安全检查。关注点清晰分离。
反模式:一个 HookMatcher 把三种检查塞进一个函数用 if-else 路由。这混合了关注点、增加维护复杂性、也无法利用 SDK 级的 matcher 过滤。
审计缺口诊断
需求:“记录所有工具调用,校验 Write 路径,完全阻止 Bash。”
已注册:
HookMatcher(matcher='Write', hooks=[validate_path])HookMatcher(matcher='Bash', hooks=[block_bash])
缺失:一个无 matcher 的 HookMatcher 做通用审计日志。Read、Grep 和 Glob 调用没有被记录。修复:加 HookMatcher(hooks=[audit_log])。
合并反模式
一个团队提议把 12 个 HookMatcher 减到 2 个(一个 PreToolUse,一个 PostToolUse,都不带 matcher,所有路由逻辑放在内部)。“更少注册 = 更简单配置。”
拒绝。 合并去掉了基于 matcher 的过滤:
- 两个 hook 对每个工具调用都触发,即使只有特定工具需要处理 → 延迟增加
- 无关的管控逻辑混在单个函数里 → 可维护性降低
- 改一个工具的逻辑可能破坏另一个的 → 脆弱代码
HookMatcher 模式就是为了在注册层面做关注点分离。更多注册 + 定向 matcher 比更少注册 + 复杂条件逻辑更容易维护。
完整注册示例
一个 CI/CD agent 有 4 个需求:
| 需求 | Hook 类型 | Matcher | 原因 |
|---|---|---|---|
| 审计所有调用 | PreToolUse | (无) | 通用日志 |
| 安全管控 | PreToolUse | 'Bash|Write' | 只对危险工具 |
| 输出标准化 | PostToolUse | 'Read|Grep' | 只对有原始输出的工具 |
| 部署门控 | PreToolUse | 'deploy' | 只对部署工具 |
四个定向条目,每个只在需要时触发。干净、高效、可维护。
同一 hook 类型的多个 matcher
在同一 hook 类型下注册多个 HookMatcher 条目(比如三个 PreToolUse 条目)是设计意图。它们独立运行——每个对自己匹配的工具触发、跑自己的 hook 函数。
一句话总结: 管控型用带正则锚定的定向 matcher('^tool_name$'),通用日志用无 matcher 条目,每个关注点独立 HookMatcher 条目——合并成少数通吃 hook 浪费 40% 处理时间并混合无关关注点。