跳转到内容
全部开发指南

担心 AI 误删生产库?用 Claude Code Hooks 装上三道硬护栏

AIGoCode发布于 更新于

担心 AI 误删生产库?用 Claude Code Hooks 装上三道硬护栏 封面

2026 年 4 月,开发者圈流传过一起事故:一家做车辆租赁管理系统的公司 PocketOS,创始人让 AI 编程工具处理 staging 环境的凭证问题,AI 却在一个不相关的文件里找到了云平台 token,直接调用删除存储卷的接口,几秒钟内生产数据库连同备份一起没了。事后复盘,AI 自己承认了两条错误:没有验证就猜测,没被要求就执行了不可逆操作。

这类事故靠在 prompt 里多写几句“不要删库”是防不住的。Claude Code 给出的办法是 Claude Code Hooks:在 Claude Code 生命周期的固定节点挂上你自己的脚本,由代码而不是模型来决定一个操作能不能执行。

如果你已经在用 Claude Code,并且开始让它跑自动模式,这篇值得读一读。下面依次讲 Hooks 的工作方式、三个最实用的护栏(拦截危险操作、编辑后自动格式化、压缩后补回上下文),再介绍 prompt 类型和 MCP 工具类型的 hook,最后是几条容易踩的坑。

Claude Code Hooks 原文配图 1

Claude Code Hooks 是什么

官方文档的定义是:在 Claude Code 生命周期的特定节点自动执行的、用户自定义的 shell 命令、HTTP 端点或 LLM prompt。实际用起来,它主要做两件事:

  • 拦:模型准备执行某个操作,脚本判定不行,这个操作就不会发生;
  • 补:模型做完一件事,脚本接着补上后续步骤,比如格式化代码、记日志、发通知。

它和 prompt 约束的区别在于确定性。CLAUDE.md 里的规则,模型在长对话或边缘情况下可能会忽略;hook 是代码层面的判断,PreToolUse 脚本只要以退出码 2 结束,这次工具调用就会被阻止,跟模型听不听话无关。

Hooks 事件有 30 多个,先掌握 5 个

截至 2026 年 10 月,官方文档列出的 Hooks 事件已经超过 30 个(原文写作时是 28 个),按触发频率大致分三档:

  • 每个会话一次:SessionStart、SessionEnd;
  • 每个回合一次:UserPromptSubmit、Stop 等;
  • 每次工具调用:PreToolUse、PostToolUse、PostToolUseFailure、PermissionRequest 等。

另外还有子 Agent、任务、上下文压缩、文件变化等专用事件。日常配置先掌握下面 5 个就够用:

事件触发时机常见用途
PreToolUse工具执行前拦截操作、修改参数
PostToolUse工具执行后格式化、日志、通知
SessionStart会话开始、恢复或压缩后注入上下文
PreCompact上下文压缩前保存关键信息
NotificationClaude 需要你确认时桌面通知,不用一直盯终端

其余事件用到时再查官方参考文档。

Claude Code Hooks 原文配图 2

护栏一:用 PreToolUse 拦截危险操作

开头那起事故,缺的正是一道 PreToolUse 拦截。官方文档的第一个示例就是拦截递归强制删除这类破坏性命令。配置写在 ~/.claude/settings.json 或项目内的 .claude/settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "if": "Bash(YOUR_DANGEROUS_CMD *)",
            "command": "bash .claude/hooks/guard.sh"
          }
        ]
      }
    ]
  }
}

把 YOUR_DANGEROUS_CMD 换成你想拦的命令前缀即可。guard.sh 的逻辑很简单:从标准输入读取 hook 传来的 JSON,取出 tool_input.command,如果命中你列出的危险模式(递归删除目录、清空数据表、强制推送等),就把原因写到标准错误输出并以退出码 2 结束;没命中就以退出码 0 放行。

几个细节:

  1. matcher 匹配工具名,if 再匹配具体命令。 只有命中前缀的 Bash 命令才会启动脚本,跑测试这类普通命令不会进来,省掉每次起进程的开销。
  2. 拦截有两种写法。 一种是以退出码 0 结束,同时输出带 permissionDecision: "deny" 的 JSON;另一种是直接以退出码 2 结束。官方文档写得很明确:只有退出码 2 是“阻止”信号,其他非零退出码只算非阻塞错误,操作照样执行。不少人习惯写退出码 1,这样是拦不住的。

同样的思路可以用来保护文件,官方示例叫 “Block edits to protected files”:PreToolUse 的 matcher 设为 Edit|Write,脚本检查 tool_input.file_path,路径落在环境变量文件、锁文件或数据库文件上就以退出码 2 拒绝。

hook 只是其中一层防线,云平台 token 的权限范围、生产环境的删除保护同样要收紧。关于 AI 编程工具的隔离边界,可以参考 AI 编程工具的沙箱不是完整安全边界。

护栏二:PostToolUse 编辑后自动格式化

这个 hook 配置一次就能一直省事:Claude 每改一个文件,格式化工具就跑一遍。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/format.sh"
          }
        ]
      }
    ]
  }
}

format.sh 读取 tool_input.file_path,按扩展名分发:TS/JS 用 Prettier,Python 用 ruff format,Go 用 gofmt。这样 Claude Code 产出的代码始终是格式化过的,review 时不用再为缩进来回改。

另外,PostToolUse 和 PostToolUseFailure 的输入里有 duration_ms 字段,记录工具执行耗时(不含权限确认和 PreToolUse hook 的时间)。想知道哪些工具调用最慢,把它写进日志就能统计。

护栏三:上下文压缩后自动补回关键信息

长会话跑到一半上下文满了,Claude Code 会自动压缩(compact),把之前的对话浓缩成摘要。压缩之后模型经常“失忆”,项目规范、包管理器、手头的任务都可能忘掉。

解决办法是给 SessionStart 配一个 compact matcher:

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "compact",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Reminder: use pnpm, not npm. Run tests before commit.'"
          }
        ]
      }
    ]
  }
}

SessionStart hook 输出到标准输出的文本,会作为上下文重新交给 Claude。除了固定提醒,也可以换成脚本,输出最近的提交记录、当前分支、未完成的 TODO 等动态信息。

Claude Code Hooks 原文配图 3

进阶:prompt hook 和 MCP 工具 hook

除了 shell 命令,Hooks 还支持 http、prompt、agent 和 mcp_tool 几种类型。

prompt hook 把判断交给 LLM,适合规则写不死、但模型能判断的场景,比如提交前检查有没有硬编码的密钥。按官方当前文档,模型要返回 {"ok": true/false, "reason": "..."} 结构的 JSON,在 PreToolUse 上返回 ok: false 时会拒绝这次工具调用:

{
  "type": "prompt",
  "if": "Bash(git commit*)",
  "prompt": "Check whether the pending commit includes hardcoded secrets or API keys. Input: $ARGUMENTS. Return ok=false with a reason if it does."
}

$ARGUMENTS 会被替换成 hook 的输入 JSON,prompt hook 默认超时 30 秒。

MCP 工具 hook 在事件触发时直接调用已配置 MCP server 上的工具,server 和 tool 两个字段必填,input 里可以引用工具输入中的字段:

{
  "type": "mcp_tool",
  "server": "my_server",
  "tool": "security_scan",
  "input": { "file_path": "tool_input.file_path 的值" }
}

注意,会话启动时的 SessionStart 触发在 MCP server 连接之前,这时 mcp_tool hook 会被跳过。启动阶段要用的逻辑请改用 command 类型。

如果不想手写 JSON,Claude Code 官方插件里的 hookify 能用自然语言生成规则,安装方法见 Claude Code 官方插件使用教程。

限制与注意事项

  • 超时不会拦截:PreToolUse 上的 command、http、mcp_tool hook 超时后,工具调用不会被阻止,而是走正常的权限流程。想让失败也拦截,要设置 onFailure: "block"。
  • PermissionRequest 不认退出码 2:这个事件要返回 decision 对象来拒绝。
  • 字符串匹配有盲区:只按命令前缀匹配,换个参数顺序或者通过脚本间接执行就可能漏掉,生产环境一定要配合最小权限凭证。
  • 示例未实测:以上配置整理自原文和官方文档,上线前先在测试项目里验证。

总结

AI 编程工具的护栏要写成代码,光写在 prompt 里不够。CLAUDE.md 里的规则可能在边缘情况下被忽略,hook 装上之后每次都会执行,也不会因为上下文太长被压缩掉。如果你用 Claude Code 超过一个月还没写过 hook,可以先从危险操作拦截和自动格式化这两个开始。

想在国内网络环境下直接使用 Claude Code 和 Codex,可以先在 AIGoCode 上完成接入,再配置这些 Hooks。

参考链接