[控场AI]
· 4 分钟阅读· 2,445 字

用Claude Code Mods管住18个AI代理:禁止推送主分支与访问密钥

用Claude Code Mods管住18个AI代理:禁止推送主分支与访问密钥

Claude Code Mods 用事件钩子为多代理协作加上可强制执行的安全边界。

一位开发者针对 18 个 AI 代理分散在 7 个代码仓库中自主运行的场景,提出了 Claude Code Mods 方案:通过 Claude 在执行动作前触发的事件钩子,实现对操作的放行、修改或阻断。文章详述了五类钩子的分工,涵盖把代理锁在各自 worktree、独立于权限规则地封死 push 到 main、拦截对 .env 与 keystore 等敏感文件的访问、自动注入仓库/分支/ticket 上下文,以及追踪 token 与缓存命中以控制成本。其关键洞察是:被阻断时 Claude 会读取理由并调整策略,而非直接失败;同时必须警惕 hook 崩溃默认放行的陷阱,需为每个钩子添加 .catch 返回拒绝来兜底。

当18个AI代理分散在7个代码仓库中自主工作时,一次误操作就可能把代码推到main分支,或让代理读取到环境变量中的密钥。一位开发者在rss中分享了他的解法:Claude Code Mods,通过事件钩子在代理执行动作前进行拦截。

Claude Code Mods 事件拦截机制示意

这套方案的核心思路并不复杂:Claude在每次即将执行动作时,会先触发一个事件,你的hook脚本在动作发生前运行,可以选择放行、修改或直接阻断。

五个关键事件钩子如何分工

开发者围绕tool.call、tool.check、prompt.submit和turn.step等事件,分别配置了不同职责的拦截逻辑。

tool.call:把每个代理锁在自己的工作区

第一个钩子处理的是代理越界问题。当某个代理试图编辑其worktree之外的代码时,该操作会被暂停并等待人工确认。如果没有得到回应,系统默认拒绝执行。这相当于给每个代理划定了活动边界——它们只能在各自负责的仓库区域内工作。

worktree 指 git worktree 创建的独立工作目录。与克隆多个仓库副本不同,worktree 与主仓库共享同一个 .git 对象库,只是在文件系统上拥有各自独立的工作树和检出分支,因此创建成本极低、切换近乎瞬时。在多代理场景中,让每个代理独占一个 worktree 既能避免它们在同一份文件上互相覆盖,又能让各代理的分支、暂存区保持隔离,出问题时可单独丢弃而不影响他人。正因如此,「代理只能在自身 worktree 内操作」才成为一条可强制执行且边界清晰的约束。

tool.check:从机制上封死推送main的路径

第二个钩子专门针对git push。它会检查当前分支,如果目标是main分支则直接拒绝,即便权限规则本身允许这次推送。这里的巧妙之处在于:它不依赖权限配置的正确性,而是在权限之上再加一道独立检查。对于多代理协作场景,这意味着即使某个代理被赋予了较宽的git权限,也无法绕过这道关卡。

tool.call:屏蔽对敏感文件的读取与编辑

第三个钩子保护的是密钥类文件。它会拦截对.env、keystore以及.p8文件的任何读取或编辑操作。这一层防护直接阻断了代理意外接触敏感凭据的可能性。

prompt.submit:自动注入上下文信息

第四个钩子解决的是重复输入的麻烦。每次提交prompt时,系统会自动附加当前仓库、分支和ticket编号,开发者不需要再手动重复这些上下文。对于同时管理多个仓库和代理的场景,这能显著减少沟通成本。

turn.step:追踪token消耗与缓存命中

第五个钩子记录每次模型调用的token用量和缓存命中情况。虽然不涉及安全拦截,但对于控制18个代理的运行成本来说,这种可观测性是必要的。

Claude Code 的 hook 机制是 Anthropic 官方为 Claude Code(其终端内的编码代理工具)提供的一套事件驱动的扩展点。开发者可以在 settings.json 中以 JSON 结构声明在特定生命周期事件前后运行的 shell 命令,Claude Code 会在对应时机同步调用这些脚本,并通过脚本的退出码与 stdout 决定后续行为:退出码为 0 表示放行,非 0 表示阻断,stdout 中的内容会作为反馈回传给模型。这一设计借鉴了 Git hooks 与 Web 中间件的思路,把「模型想做什么」与「系统允许做什么」解耦,使安全策略可版本化、可审计,而不必依赖提示词层面的约束。

prompt caching(提示缓存) 是 Anthropic API 提供的一项能力:当请求前缀(例如系统提示、CLAUDE.md、注入的仓库上下文)与近期请求高度重合时,这部分内容会被标记为命中缓存,按显著低于常规输入 token 的价格计费,并降低首 token 延迟。在多代理、长会话场景下,前缀往往反复出现,缓存命中率直接决定账单量级。因此观测缓存命中情况,本质上是在检查上下文注入设计与会话复用策略是否合理,而不只是单纯的性能指标。

为什么拦截优于简单报错

这套机制真正有效的原因在于Claude对阻断原因的处理方式。当一个hook阻断某个动作时,Claude会读取被阻断的理由,并据此调整策略,而不是简单地失败退出。这意味着代理能在被拒绝后尝试其他合规路径,而不是卡死或反复重试同一个被禁操作。

退出码与 stderr 反馈是 Claude Code 与 hook 通信的主要通道:hook 通过非零退出码表示拒绝,并把解释性文字写入 stderr(或按版本约定写入 stdout),这些文字会作为工具调用结果回灌给模型。正因为模型能看到「为什么被拒」,它才能把这次拒绝当作一次观察来修正计划——换用合规分支、改写命令路径或转而请求人工介入。相比之下,单纯返回一个通用错误会让模型缺少可行动的线索,只能盲目重试同样的动作,既浪费 token 也可能反复触碰同一条红线。

一个必须注意的陷阱

开发者特别指出一个容易踩的坑:如果hook脚本本身崩溃了,Claude Code会跳过该hook并继续执行原动作。也就是说,hook失败时的默认行为是放行,而非拦截。

解决办法是给每个hook添加.catch分支,在捕获异常时返回拒绝。这样即使脚本出错,也能保证安全策略不被绕过。

开发者用一句话总结了这套体系的定位:CLAUDE.md说明你想要什么,Mods决定什么被允许。 前者是意图表达,后者是强制约束。对于运行多个自主代理的团队来说,这种分层设计提供了一种可落地的安全边界方案。

#Claude Code Mods#AI代理安全#Claude Code hooks#多代理管理#代码仓库权限控制#git push拦截#AI编程安全
分享:

相关推荐