審查代理不應該能寫程式碼。我們如何逐個 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 diff、git 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 子命令(add、commit、push、merge、rebase、reset、checkout、switch、restore、stash、cherry-pick、revert、apply、am、rm、mv、clean、tag、worktree),以及那些存在的唯一目的就是改動檔案的 shell 命令(rm、mv、cp、tee、touch、mkdir、chmod、chown、ln、truncate、dd、sed -i)。Grok 接受同樣的規則字串,用它的 glob 形式,再加上它自己給檔案工具起的名字(search_replace、write、hashline_edit)。
提示詞仍然有活要幹
標誌負責拒絕。它不解釋。而一個撞上自己不理解的拒絕的代理,會把它當成一個 bug,然後另找一條路走通,這恰恰是我們想去掉的行為。
所以一個唯讀步驟的提示詞裡還會多兩句話。第一句說明這個步驟是唯讀的,列出這意味著什麼,並宣告拒絕就是規則本身,不是一個要換條命令繞過去的障礙。第二句列出仍然開放的東西,並告訴代理在交接裡報告應該改什麼,附上檔案、行號和原因,讓擁有程式碼的那個步驟去應用。
在有經過驗證的標誌的 CLI 上,這段話是讓拒絕被理解的關鍵。在其他 CLI 上,它就是全部的強制手段,我們寧願把這話說出來,也不假裝。
內建模板裡誰是唯讀的
那些做判斷的節點是唯讀的:兩個入門模板的 QA 驗證步驟,Bug hunt 的復現和驗證步驟,Release shield 的 QA 和安全分支,Feature squad 的測試者。
那些擁有程式碼的節點繼續寫:開發者,以及被設定為自己修掉每一個發現的發布門禁。一個不能寫的發布門禁,就是一個不能發布的發布門禁。
這個分工就是整個設計,也是這次事故兩度違反的分工:一個在錯誤的地方寫了的發布節點,和幾個壓根就不該寫的審查節點。
這不是什麼
它不是安全邊界。報告者在報告裡說了,而且說得對:一個 bash -c 就能繞過工具拒絕列表。如果你需要隔離一個你不信任的代理,那是沙箱或一台單獨機器的事,而五個 CLI 裡只有 Codex 的唯讀模式真的是這個東西。
這個開關攔住的是意外和角色漂移,而這才是實際會發生的事。審查者不會故意逃出拒絕列表。它是出於本能去抓 Edit,而這個本能現在會被拒絕。
我們沒有做的
報告者還提了一件事:在執行時間線里加一個事件,寫著「節點 X 寫入了工作樹」,作為即使沒有強制也該有的最低限度的訊號。這是個好主意,我們這次沒做,因為它需要在執行器那邊為每個步驟留一份基線 diff。如果這個需求再回來,那就是下一塊。
如果你不用 AgentsRoom
上面的標誌可以原樣複製。一個用 codex --sandbox read-only 或 claude --disallowedTools "Edit" "Write" "MultiEdit" "NotebookEdit" "Bash(git commit:*)" "Bash(git push:*)" 手動啟動的審查代理,做不了我們那兩個審查節點做的事。把同樣的兩句話放進它的提示詞,讓它知道自己為什麼被拒絕。
核取方塊額外帶來的是:你不必記住五種語法裡哪一種適用,標誌壓過步驟執行時所處的任何自主模式,而且在稍後重新進入同一步驟的執行裡依然有效。
節點開關和按提供商列出的表格記錄在 Agent Teams 頁面上。代理做的審查到底值不值得有,一個 diff 裡還有多少部分值得人來看,是另一個問題,我們在你還應該審查你的 AI 代理的程式碼嗎?裡寫過。這篇文章講的是更小、更機械的那件事:一旦你決定讓一個代理做審查,就讓它在物理上做不了別的任何事。
下載 AgentsRoom
在一個視窗中執行你所有專案的所有 AI 代理。
配套應用:隨時隨地監控你的 Agent
使用 Claude、Codex、Antigravity CLI 或其他 AI 提供商。
把 Bug 和需求直接傳送到您的公開待辦清單。
繼續閱讀
AI 代理迴圈:自我糾錯的編碼代理如何把活幹完
AI 代理迴圈把「提示加修補」變成一個自我糾錯的閉環:代理先寫計劃,再動手實現,然後對照計劃檢查自己的成果,迴圈往復直到完成。看看這個迴圈在 Claude Code、Codex、Antigravity CLI、Cursor 和 Ralph loop 中是怎麼運轉的。
閱讀全文Codex、Claude Code、Cursor 還是 Copilot:按工作方式來選
對比 Codex、Claude Code、Cursor、GitHub Copilot 和 AgentsRoom,不看功能清單,而是按工作方式來選:一個人寫程式碼、同時跑多個 agent,還是和團隊一起交付。
閱讀全文10個代理同時跑了同一個型別檢查。解決辦法是一個目錄。
同一個檢出目錄裡17個編碼代理,同時跑著10個tsc,load average 37,空閒記憶體只剩87 MB。一次九十秒的型別檢查花了7分36。這裡是我們的測量資料,機器為什麼根本沒在計算,以及那個解決了問題的小小共享鎖。可以直接抄進任何儲存庫。
閱讀全文