智能编码进化论

给AI定规矩:Hooks与权限工程

约 22 分钟驭光者首发于微信公众号「羲和Lab」

信任但验证——给AI员工装上门禁、监控和自动化流程

阅读时长约14分钟 | 教程型 | 智能编码进化论 07/10


导读

前六篇我们给AI配齐了一整套"员工装备":岗位说明书、结构化任务、工具箱、外线电话、编外帮手。AI越来越能干了,但问题也随之而来——它会不会误删重要文件?会不会把.env推到远程仓库?会不会在生产环境执行危险命令?

这些担忧不是杞人忧天。任何有经验的团队leader都知道,员工能力越强,越需要清晰的规则和监控。不是不信任,是信任但要验证。

Hooks和权限系统就是Claude Code的安全体系。一句话区分:权限是门禁卡——决定AI能进哪些门;Hooks是监控摄像头——在关键节点检查发生了什么。 两者配合,让AI在安全边界内自由发挥。

阅读地图:Hooks和权限是什么关系

这篇文章讲两件事:Hooks和权限。它们都管安全,但管法不同。

权限管的是"能不能"——像门禁卡,控制AI能进哪些门。配置一次,每次都按规则执行,不需要写代码。

Hooks管的是"在什么时候做什么检查"——像监控摄像头,在关键节点自动触发。可以写shell脚本,也可以用LLM判断。

两者的关系:

  • 权限是静态规则(匹配就生效)
  • Hooks是动态脚本(在特定事件触发)
  • 执行顺序:权限先判断 → Hooks再检查 → 操作执行 → Hooks记录

你不需要一次记住所有细节。先通读一遍建立印象,需要时回来查具体配置。

一、Hooks:关键节点的自动化守卫

Hooks是什么

Hooks是在特定事件发生时自动触发的自定义操作。像给办公室装的监控摄像头——有人进出时自动记录,有人试图闯入时自动报警。

Claude Code事件流:
  用户输入 → AI思考 → 调用工具 → 生成响应 → 通知用户
                ↑         ↑                    ↑
            Hook可以    Hook可以             Hook可以
            拦截/修改   拦截/修改            发通知

核心机制:事件触发 → Hook执行 → 根据返回结果决定继续/阻止/修改。

第一个Hook:桌面通知

最实用的入门Hook——当AI需要你输入时,发一个桌面通知。

{
  "hooks": {
    "Notification": [
      {
        "type": "command",
        "command": "osascript -e 'display notification \"$CLAUDE_EVENT\" with title \"Claude Code\"'"
      }
    ]
  }
}

配置位置:~/.claude/settings.json(个人级)或 .claude/settings.json(项目级)。

当你离开终端去做别的事,AI需要你确认时,macOS会弹出系统通知。不用一直盯着终端了。

以上为macOS版本。Linux用户替换为 notify-send "Claude Code" "$CLAUDE_EVENT",Windows用户可用PowerShell的 New-BurntToastNotification 或省略此Hook。

Hook的配置结构

Hooks的配置是三层嵌套:

事件名(如 PreToolUse)
  └── matcher组(过滤哪些工具)
        └── hook处理器(执行什么操作)

事件:覆盖Claude Code完整生命周期的各种事件,且持续新增中。最常用的6个:

事件触发时机典型用途
PreToolUse工具调用前拦截危险操作、自动格式化
PostToolUse工具调用后审计日志、结果验证
Notification需要用户注意时桌面通知
StopAI停止工作时质量检查、防止遗漏
UserPromptSubmit用户输入后输入预处理
SessionStart会话开始时环境初始化

matcher:过滤条件。比如 PreToolUse 事件可以限定只对 Edit 和 Write 工具触发:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "your-command-here"
          }
        ]
      }
    ]
  }
}

Hook处理器类型:5种执行方式

类型说明适用场景
command执行shell命令最常用,格式化、通知、检查
httpPOST到HTTP端点集成外部系统、审计日志
prompt用LLM判断需要语义理解的场景
agent用子代理多轮验证复杂审查(实验性)
mcp_tool调用MCP工具复用已有MCP连接

Hook的输入输出

输入:通过stdin接收JSON,包含事件上下文(工具名、参数、会话信息等)。

输出:通过exit code控制行为:

Exit Code含义效果
0成功继续执行
2阻止阻止当前操作
其他错误忽略错误,继续执行

关键:exit code 2 是"阻止"信号。PreToolUse返回2,工具调用被阻止;Stop返回2,AI继续工作。

也可以通过stdout返回JSON来精细控制:

{
  "decision": "deny",
  "reason": "不允许修改生产环境配置文件"
}

二、实战:四个最实用的Hook

Hook 1:自动格式化代码

每次AI编辑文件后,自动运行格式化工具。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "npx prettier --write \"$CLAUDE_FILE_PATH\" 2>/dev/null || true"
          }
        ]
      }
    ]
  }
}

AI编辑完文件,Prettier自动格式化。你看到的代码永远是规范格式。

Hook 2:保护敏感文件

阻止AI修改.env、数据库配置等敏感文件。

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "FILE=$(echo '$CLAUDE_INPUT' | jq -r '.file_path // .path // empty'); case \"$FILE\" in *.env|*.env.*|*production*|*secrets*) echo '不允许修改敏感文件: '$FILE; exit 2;; esac"
          }
        ]
      }
    ]
  }
}

AI试图编辑 .env 文件时,操作被阻止并显示原因。exit code 2 = 拦截。

这段命令提取AI要编辑的文件路径,检查是否匹配 .env 或包含 production/secrets 关键词。匹配就返回2(阻止),不匹配就返回0(放行)。注意:命令中的单引号是JSON字符串的一部分,实际执行时会正确展开变量。直接在终端测试需去掉单引号。

Hook 3:压缩后重新注入上下文

会话过长时Claude Code会自动压缩上下文,但可能丢失重要信息。用Hook在压缩后自动重新注入关键规则:

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "compact",
        "hooks": [
          {
            "type": "command",
            "command": "echo '记住:本项目使用TypeScript strict模式,测试覆盖率要求>80%。'"
          }
        ]
      }
    ]
  }
}

压缩后,核心规则自动重新注入到上下文中。matcher: "compact" 表示只在上下文压缩后重新开始时触发,普通新会话不触发。Claude Code后来也新增了专门的 PostCompact 事件,效果相同。

Hook 4:审计日志

记录所有文件修改操作到日志文件,用于回溯审计。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "echo \"[$(date '+%Y-%m-%d %H:%M:%S')] Edit: $(echo '$CLAUDE_INPUT' | jq -r '.file_path // .path // \"unknown\"')\" >> .claude/edit-log.txt"
          }
        ]
      }
    ]
  }
}

每次文件编辑都记录时间、文件路径。出问题时可以回溯。

三、进阶:Prompt-based Hooks

用LLM做判断

前四个Hook用shell搞定,但"检查代码是否包含硬编码密码"——这种需要语义理解的判断,shell无能为力,LLM却很擅长。这就是Prompt Hooks的用武之地。

Prompt-based Hooks用LLM做决策:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit",
        "hooks": [
          {
            "type": "prompt",
            "prompt": "检查即将写入的代码是否包含硬编码的API密钥或密码。如果包含,返回 {\"ok\": false, \"reason\": \"检测到硬编码的敏感信息\"}。否则返回 {\"ok\": true}。"
          }
        ]
      }
    ]
  }
}

AI每次编辑文件前,另一个LLM会先检查新代码是否包含敏感信息。发现就阻止。

响应格式:

{"ok": true}                  // 通过,继续执行
{"ok": false, "reason": "..."} // 阻止,显示原因

适用场景

场景为什么用prompt示例
安全检查需要理解代码语义检测硬编码密钥、SQL注入
代码规范需要判断风格是否符合检查命名、结构
内容审核需要理解自然语言检查提交信息是否合规

Prompt hooks有延迟(需要LLM推理),不适合高频操作。对低频但重要的检查(如文件编辑前的安全审查)最合适。

四、权限系统:AI能做什么不能做什么

权限模式是整体策略(大方向控制),权限规则是精细化配置(具体操作控制)。模式决定默认行为,规则做例外处理。

四种权限模式

权限模式控制AI的自主程度——从"事事请示"到"放手去做":

模式自动批准需要确认适用场景
default只读操作Bash命令、文件修改日常开发(推荐)
acceptEdits读取+编辑+常用命令Bash复杂命令快速迭代
plan只读一切修改审查代码、调研
auto大部分操作高风险操作(需Max/Team账号)高信任环境
dontAsk只读+预批准规则其他一切CI/CD
bypassPermissions全部仅rm -rf / 和 rm -rf ~隔离容器

切换方式:终端按 Shift+Tab 循环切换。

最常用的组合:

  • 日常开发用 default——安全,每一步都有确认
  • 重构时用 acceptEdits——AI可以自由编辑文件,减少中断
  • 代码审查用 plan——AI只能看不能改

auto模式的智能分类器

auto模式不是"什么都允许"。它有一个分类器模型审查每个操作:

默认阻止:下载执行代码、发送敏感数据、生产部署、批量删除、IAM授权、force push。

默认允许:本地文件操作、安装依赖、读取.env、只读HTTP、推送到当前分支。

连续3次被阻止或总共20次被阻止后,auto模式会自动暂停,让你重新评估。

权限规则:精确控制

在 permissions 字段中配置规则,三层优先级:deny > ask > allow。

{
  "permissions": {
    "deny": [
      "Bash(rm -rf *)",
      "Bash(git push --force)",
      "Edit(./.env)"
    ],
    "allow": [
      "Bash(npm run *)",
      "Bash(git status)",
      "Bash(git diff *)",
      "Read(*)"
    ]
  }
}

工具特定规则:

工具规则语法示例
BashBash(命令模式)Bash(npm run *) 匹配所有npm scripts
Read/Editgitignore风格路径Edit(./.env) 匹配项目根的.env
WebFetchWebFetch(domain:xxx)WebFetch(domain:github.com)
MCPmcp__服务器__工具mcp__puppeteer__*
AgentAgent(名称)Agent(Explore)

通配符 * 匹配任意字符串。Bash(npm run *) 匹配 npm run build、npm run test 等。

受保护路径

某些路径默认受保护,即使权限模式允许,修改这些文件也会触发额外确认:

.git、.vscode、.idea、.claude(部分)、.bashrc、.zshrc、.mcp.json、.claude.json 等。

这是安全底线——AI不应该随意修改版本控制配置、IDE设置或自身配置文件。

五、Hooks与权限的配合

双层防护架构

Hooks和权限系统是两个互补的安全层:

操作请求 → 权限规则检查(deny/ask/allow)→ Hook执行(PreToolUse)→ 执行操作 → Hook执行(PostToolUse)

权限规则是静态的、确定性的——"这个模式匹配吗?匹配就允许/拒绝"。适合明确的黑白名单。

Hooks是动态的、可编程的——"看看这次操作的具体内容,判断是否安全"。适合需要上下文判断的场景。

配合模式

模式1:权限做白名单 + Hook做深度检查

{
  "permissions": {
    "allow": ["Bash(npm run *)"]
  },
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [{
          "type": "command",
          "command": "CMD=$(echo '$CLAUDE_INPUT' | jq -r '.command'); if echo \"$CMD\" | grep -q 'rm\\|drop\\|delete'; then echo '检测到危险关键词'; exit 2; fi"
        }]
      }
    ]
  }
}

权限允许 npm run *,但Hook会额外检查命令内容是否包含危险关键词。双层防护。

模式2:权限做黑名单 + Hook做审计

{
  "permissions": {
    "deny": ["Edit(./.env)", "Bash(git push --force)"]
  },
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [{
          "type": "command",
          "command": "echo \"[$(date)] Edit: $(echo '$CLAUDE_INPUT' | jq -r '.file_path // .path')\" >> .claude/edit-log.txt"
        }]
      }
    ]
  }
}

权限直接拦截明确禁止的操作,Hook记录所有文件修改的审计日志。

六、避坑指南

坑一:Hook命令执行失败导致操作被阻止

Hook脚本有bug或依赖缺失,exit code非0非2,理论上应该忽略。但某些情况下可能误阻止操作。

解法:Hook命令加上错误处理。确保正常路径返回0,明确要阻止时才返回2。不确定时用 || true 确保不阻止。

坑二:Hook无限循环

PostToolUse的Hook触发了新的工具调用,新调用又触发PostToolUse——无限循环。

解法:Stop和SubagentStop hook内置了 stop_hook_active 标志,连续被阻止后会强制结束。但最好在设计时就避免。PostToolUse Hook尽量只做通知和日志,不做工具调用。

坑三:权限规则优先级搞混

以为allow规则能覆盖deny规则——不能。deny永远优先于allow。

{
  "permissions": {
    "deny": ["Bash(*)"],
    "allow": ["Bash(git status)"]
  }
}

这个配置下,git status 也会被拒绝,因为 Bash(*) 在deny中匹配了一切。

解法:deny规则要精确,不要用宽泛的通配符。需要阻止特定操作时写具体模式,不要一刀切。

坑四:受保护路径的误判

修改 .claude/settings.json 被拦截——这是受保护路径。但你确实需要修改它。

解法:受保护路径的拦截是额外确认,不是阻止。终端会弹出确认提示,你确认后可以继续。

坑五:auto模式不是万能的

auto模式有分类器保护,但它基于概率判断,不是100%准确。高风险操作(生产部署、数据库迁移)不应该依赖auto模式。

解法:高风险操作用 default 或 plan 模式,确保每一步都有人工确认。auto模式适合日常开发,不适合关键操作。

结语

七篇文章下来,AI"员工"从单打独斗进化到了有工具、有帮手、有规矩的完整团队:

  • CLAUDE.md是岗位说明书——告诉AI公司规矩
  • Skills是工具箱——按需加载专业知识
  • MCP是外线电话——连接外部系统
  • Subagent是编外帮手——并行处理辅助任务
  • Hooks是监控和门禁——自动化检查和拦截
  • 权限是门禁卡——控制AI能进哪些门

五个关键要点:

Hooks是自动化守卫——在关键节点自动执行检查、通知、拦截,不用人工盯防。

Exit code 2是阻止信号——PreToolUse返回2阻止操作,PostToolUse用于审计和通知。

Prompt Hooks处理复杂判断——需要语义理解的检查交给LLM,简单的用shell脚本。

权限规则deny优先于allow——先配黑名单(deny),再配白名单(allow)。

Hooks和权限互补——权限做静态黑白名单,Hooks做动态上下文判断。

下一篇,我们组建AI项目组——多会话与CI/CD实战。如果说Hooks是给员工装了门禁,多会话和CI/CD就是把员工从单兵作战升级到项目组协同——并行开发、自动化流水线、代码审查一体化。

互动话题

你的项目中,哪些操作需要自动化保护?是防止误删生产配置、自动格式化代码,还是审计所有文件修改?来评论区分享你的Hooks和权限配置经验。


智能编码进化论 | 第7篇/共10篇

上一篇:给AI招帮手:Subagent深度定制

下一篇:AI项目组:多会话与CI/CD实战


本文内容仅供学习交流,所述方法需结合实际场景灵活运用。 AI工具输出结果仅供参考,关键业务决策请结合人工判断。

本文由AI辅助创作,经人工审核编辑后发布。

本文首发于微信公众号「羲和Lab」,公众号内可留言交流

阅读公众号原文