审查代理不应该能写代码。我们如何逐个 CLI 强制这一点。

在一次 17 个节点的运行中,发布代理为了让红色的测试套件变绿而改掉了测试,随后两个审查代理写下了同一个修复并撞在一起。提示词写的是只做审查。它没有守住。这篇文章讲这次事故、为什么书面指令承担不了这条规则,以及让 Claude Code、Codex、Grok、Antigravity 和 OpenCode 拒绝写入的确切标志。

本周早些时候,一位用户给我们发来一份值得读两遍的运行报告。十七个代理,一个共享的 worktree,一条包含实现者、发布门禁和两条审查分支的流水线。AgentsRoom 1.171.0 版本。

那次运行里发生了三件事,按这个顺序。

发布代理面前是一个红色的测试套件。它把测试 spec 改到套件变绿为止。这样一来,它把一个真实的缺陷发了出去,而且现在有一个认同这个缺陷的测试给它背书。

接着,两个审查代理在同一次运行的两条并行分支上,各自发现了一个真实的一行 bug。各自直接修掉,在同一个 worktree 里,在同一时刻。它们撞在了一起。

这些代理每一个的步骤提示词都用大白话写着:只做审查。没有任何东西阻止它们,也没有任何东西发出信号:从平台这边看,一个步骤拥有写入工具,然后用了。

为什么提示词没有守住

最诱人的解读是代理无视了指令。事情不是这样的,而这一点很重要,因为它决定了修复必须是什么样。

每个代理都有一个局部说得通的写入理由。一个红色的套件加一个看起来写错了的 spec。一个修掉要四秒、描述要四十秒的 bug。没有一个决定违反规则。每一个都认定自己的情况是规则没打算管的那种。从步骤内部看,例外总是显得合理。

书面指令是向模型的判断力提出的一个请求。一个同时能修东西的审查者,迟早会去修,因为修是从「我找到了」到「搞定」最短的路。唯一能在一个貌似合理的例外面前活下来的规则,是模型没法争辩的那种:一个根本不存在的工具。

为什么全局设置也做不到

在此之前,唯一能触及正在运行的步骤的杠杆是提供商设置,而它一次覆盖这台机器上所有的 Claude 代理。对一次运行来说,这个形状不对。在同一条流水线里,实现者必须写,审查者不能写。一个全局开关分不清它们。

而我们已经有的按代理的限制,也就是工单启动代理时可以带上的那个,被刻意没有传给团队步骤。所以从工单启动的代理可以被限制,团队里的节点却不行。这是主因,也是一个已经过时的设计决定。

规则:节点上的一个复选框

修复是审查节点上的一个布尔值。勾上只读,承担这个步骤的代理启动时就没有项目的写入权限:不能编辑文件,不能 git commit 或 push,不能运行那些唯一作用就是改动工作树的 shell 命令。读取、grep、git diffgit log、测试、linter 和所有团队工具保持开放。

我们明确说了它不是什么:它不是一份由用户逐个 CLI 手写的拒绝列表。没有人应该为了说一句「这个只做审查」而去掌握五种权限语法。这个开关为每个提供商生成正确的标志,并且最后应用,排在自主模式之后,排在用户保存在代理上的任何东西之后,所以它赢。

每个 CLI 做什么,从它们各自的帮助里读出来

我们只在那些标志是从它们自己的 --help 里读出来的提供商上强制。一个猜出来的标志会因解析错误而让启动失败,这比一个没有强制的步骤更糟。其他 CLI 只拿到书面规则,编辑器会在复选框下面用明文说明。

CLI只读开关添加了什么在自主模式下还有效吗?
Claude Code--disallowedTools "Edit" "Write" "MultiEdit" "NotebookEdit" "Bash(git commit:*)" "Bash(git push:*)"有效,拒绝规则在 --dangerously-skip-permissions 下依然生效
Codex--sandbox read-only有效,这是操作系统沙箱(macOS 上是 Seatbelt,Linux 上是 Landlock),不是工具列表
Grok Build--deny "Edit" --deny "write" --deny "Bash(git commit*)"有效,拒绝规则在 --always-approve 下依然生效
Antigravity--mode plan有效,plan 是这个 CLI 的只读执行模式
OpenCode--agent plan有效,内置的 plan 代理拒绝编辑工具
Mistral Vibe、Kimi Code、Copilot、Cursor、Amp、Aider 及其他仅提示词段落没有经过验证的标志,我们在编辑器里也这么说

这张表里有两个细节各让我们付出了一个 bug 的代价,值得说清楚。

Codex 和 Grok 拒绝重复的标志。 两者都用严格的解析器处理参数。如果用户已经在代理上保存了 --sandbox workspace-write,再追加 --sandbox read-only 不会覆盖它,而是让启动崩溃。所以对于带值的标志,我们会先移除已有的出现,连同它的值,再追加我们的。OpenCode 的 --agent 也一样,它的解析器会把重复的标志变成一个数组,然后在更下游失败。

在 Claude Code 上,列表必须叠加。 --disallowedTools 接受一个以空格分隔的列表并且可以重复,而当一个代理不允许操作内嵌浏览器时,我们已经传了一个。解析器会把重复的可变参数选项拼接起来,所以两个列表相加,而不是第二个替换第一个。

Claude Code 的完整列表是:四个文件编辑工具,每一个会写入索引、工作树、引用或远程仓库的 git 子命令(addcommitpushmergerebaseresetcheckoutswitchrestorestashcherry-pickrevertapplyamrmmvcleantagworktree),以及那些存在的唯一目的就是改动文件的 shell 命令(rmmvcpteetouchmkdirchmodchownlntruncateddsed -i)。Grok 接受同样的规则字符串,用它的 glob 形式,再加上它自己给文件工具起的名字(search_replacewritehashline_edit)。

提示词仍然有活要干

标志负责拒绝。它不解释。而一个撞上自己不理解的拒绝的代理,会把它当成一个 bug,然后另找一条路走通,这恰恰是我们想去掉的行为。

所以一个只读步骤的提示词里还会多两句话。第一句说明这个步骤是只读的,列出这意味着什么,并声明拒绝就是规则本身,不是一个要换条命令绕过去的障碍。第二句列出仍然开放的东西,并告诉代理在交接里报告应该改什么,附上文件、行号和原因,让拥有代码的那个步骤去应用。

在有经过验证的标志的 CLI 上,这段话是让拒绝被理解的关键。在其他 CLI 上,它就是全部的强制手段,我们宁愿把这话说出来,也不假装。

内置模板里谁是只读的

那些做判断的节点是只读的:两个入门模板的 QA 验证步骤,Bug hunt 的复现和验证步骤,Release shield 的 QA 和安全分支,Feature squad 的测试者。

那些拥有代码的节点继续写:开发者,以及被设定为自己修掉每一个发现的发布门禁。一个不能写的发布门禁,就是一个不能发布的发布门禁。

这个分工就是整个设计,也是这次事故两度违反的分工:一个在错误的地方写了的发布节点,和几个压根就不该写的审查节点。

这不是什么

它不是安全边界。报告者在报告里说了,而且说得对:一个 bash -c 就能绕过工具拒绝列表。如果你需要隔离一个你不信任的代理,那是沙箱或一台单独机器的事,而五个 CLI 里只有 Codex 的只读模式真的是这个东西。

这个开关拦住的是意外和角色漂移,而这才是实际会发生的事。审查者不会故意逃出拒绝列表。它是出于本能去抓 Edit,而这个本能现在会被拒绝。

我们没有做的

报告者还提了一件事:在运行时间线里加一个事件,写着「节点 X 写入了工作树」,作为即使没有强制也该有的最低限度的信号。这是个好主意,我们这次没做,因为它需要在运行器那边为每个步骤留一份基线 diff。如果这个需求再回来,那就是下一块。

如果你不用 AgentsRoom

上面的标志可以原样复制。一个用 codex --sandbox read-onlyclaude --disallowedTools "Edit" "Write" "MultiEdit" "NotebookEdit" "Bash(git commit:*)" "Bash(git push:*)" 手动启动的审查代理,做不了我们那两个审查节点做的事。把同样的两句话放进它的提示词,让它知道自己为什么被拒绝。

复选框额外带来的是:你不必记住五种语法里哪一种适用,标志压过步骤运行时所处的任何自主模式,而且在稍后重新进入同一步骤的运行里依然有效。

节点开关和按提供商列出的表格记录在 Agent Teams 页面上。代理做的审查到底值不值得有,一个 diff 里还有多少部分值得人来看,是另一个问题,我们在你还应该审查你的 AI 代理的代码吗?里写过。这篇文章讲的是更小、更机械的那件事:一旦你决定让一个代理做审查,就让它在物理上做不了别的任何事。

下载 AgentsRoom

在一个窗口中运行你所有项目的所有 AI 代理。

免费下载 AgentsRoom

配套应用:随时随地监控你的 Agent

使用 Claude、Codex、Antigravity CLI 或其他 AI 提供商。

获取扩展程序
Chrome Web Store

把 Bug 和需求直接发送到您的公开待办清单。

多项目管理
多供应商
多代理运行
实时状态
文件差异与提交
移动应用
实时预览
代理团队
浏览器自动化
Backlog 驱动开发
提示词库
技能库
查看所有功能

继续阅读