Hooks 与 Subagents 的分工

机制何时运行适合不适合
Hook指定事件发生时格式化、审计、通知、危险命令拦截大段开放式研究
Subagent主 Agent 委派任务时上下文隔离、专业审查、并行探索必须共享同一实时状态的细碎步骤
Skill直接调用或任务匹配时可复用知识、清单和多步流程必须在每次事件中强制执行的规则

一个稳妥组合是:Skill 定义审查标准,Subagent 按标准独立审查,Hook 在关键事件上跑确定性校验。 不要用 Hook 强行启动另一个完整 Claude Code 进程,容易产生递归和不可控成本。

一、Hooks:从最小配置开始

在用户级 ~/.claude/settings.json 或项目级 .claude/settings.json 中添加hooks。下面示例在 Edit 或 Write 完成后运行快速 lint:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "npm run lint --silent",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

matcher 用来筛选事件目标;它不是完整的安全策略。Bash matcher 只能先筛到 Bash 工具, 想判断具体命令还需要读取 stdin 中的 tool_input.command

当前处理器类型

  • command:本机 shell,适合确定性检查与自动化。
  • http:把事件 JSON POST 到指定服务。
  • mcp_tool:调用已连接 MCP Server 的工具。
  • prompt:单次模型判断,适合需要语义判断但不需要工具的条件。
  • agent:带工具的多轮核验,目前属于实验性能力,生产守卫优先用 command。

常用事件,而不是完整事件表

  • PreToolUse:工具执行前,可做权限或参数检查。
  • PostToolUse / PostToolUseFailure:工具成功或失败后处理。
  • UserPromptSubmit:用户提交提示后、模型处理前补充上下文。
  • SubagentStart / SubagentStop:子 Agent 生命周期。
  • Stop / StopFailure:正常完成与异常结束是不同事件。
  • SessionStart / SessionEnd:会话初始化与清理。

事件列表持续扩展,完整字段应查 Hooks Reference,不要把博客中的枚举当作永久 API。

正确读取 stdin,并明确阻止

把复杂逻辑放到版本库里的脚本,settings.json 只引用它。示例:

#!/usr/bin/env bash
set -euo pipefail

payload=$(cat)
command=$(jq -r '.tool_input.command // ""' <<<"$payload")

if [[ "$command" =~ git[[:space:]]+(commit|push) ]]; then
  if ! npm test; then
    echo "Tests failed; commit or push blocked." >&2
    exit 2
  fi
fi

exit 0

这个脚本依赖 jq。在 PreToolUse 的 Bash matcher 下运行时,测试失败会转换成 exit 2, 因而明确阻止本次工具调用。旧写法 npm test || true 会吞掉失败,实际上无法拦截。

返回值速查

command Hook 结果含义
exit 0成功;stdout 会按事件规则作为信息处理。
exit 2阻止;stderr 会作为反馈提供给 Claude。
其他非 0记录 Hook 错误,动作通常继续。
JSON 输出可表达更细的 decision、reason 或 additionalContext,字段随事件变化。

二、Subagents:用独立上下文换隔离

项目级 Subagent 放在 .claude/agents/;个人级放在 ~/.claude/agents/。示例:

---
name: code-reviewer
description: Review a completed code change for correctness, security, missing tests, and regressions. Use after implementation or before merge.
tools: Read, Grep, Glob, Bash
model: inherit
---

Review the requested diff without modifying files.

1. Read the diff and the directly affected tests.
2. Verify each finding against the current source.
3. Report only actionable issues with file and line references.
4. End with residual risks and missing verification.

description 决定自动委派时机;tools 决定能力边界。只读审查能不用 Edit/Write 就不要开放。 如果省略 tools,Subagent 可能继承更多可用工具,因此团队模板应显式配置。

适合并行的任务

  • 分别审查安全、性能和测试覆盖,输出只读报告。
  • 在互不重叠的目录中搜索迁移影响。
  • 比较多个独立方案,最后由主 Agent 决策。

不适合并行的任务

  • 多个 Agent 同时修改同一文件或同一数据库状态。
  • 后一步强依赖前一步完整输出的流水线。
  • 任务太小,委派和汇总成本高于直接完成。

并行耗时不会简单等于“最慢的那个任务”:模型排队、工具争用、汇总和冲突处理都会增加成本。 先从 2 个互不写同一状态的任务开始,记录实际耗时与用量,再决定是否扩大。

推荐的组合工作流

  1. 主 Agent 明确目标、边界与验收条件。
  2. 实现前,把一次性探索委派给只读 Subagent。
  3. 主 Agent 完成修改,PostToolUse Hook 运行快速格式或 lint 检查。
  4. 独立 reviewer Subagent 审查 diff,不直接修改。
  5. 主 Agent处理发现并运行完整测试。
  6. PreToolUse Hook 只在真正危险的命令前做确定性守卫。

排错清单

  • /hooks 检查 Hook 是否被发现、匹配到哪个事件。
  • 先把 stdin JSON 写入临时日志,确认实际字段,再写解析逻辑;日志不要记录凭证。
  • 给 command 设置合理 timeout,频繁事件只运行足够快的检查。
  • 出现“明明失败却没拦住”,先看是不是返回了 1 而不是 2,或脚本末尾吞掉了错误。
  • Subagent 没触发时,检查 name/description、工具名和权限;零个可用工具会导致启动失败。
  • 涉及实验性的 agent Hook 时,保留 command 级安全边界,不把模型判断当唯一守卫。

常见问题

Hook 返回非 0 就一定会阻止工具调用吗?

不会。command Hook 的 exit 2 才是阻止信号;exit 0 正常继续,其他非 0 通常记录为 Hook 错误但动作继续。不同事件还支持各自的 JSON decision 输出。

Subagent 适合并行修改同一批文件吗?

通常不适合。并行任务应尽量只读或按互不重叠的文件、模块和输出拆分;共享写入由主 Agent 串行整合更安全。

Hooks、Skills 和 Subagents 怎么选?

必须在某个事件上自动发生用 Hook;可复用知识与步骤用 Skill;会产生大量中间上下文或需要独立权限的侧任务用 Subagent。