重點從來不是「多幾個 AI 比較聰明」,而是不讓任何一個 AI 同時當「正確的作者」和「正確的裁判」。這篇給想在自己的技術棧上搭一條的人——一份可搬走的參考架構,不是我機器上的日記。
你大概已經試過讓單一 coding agent 從規畫到實作一路包辦。它能動,但你心裡清楚那個風險:同一個 agent 既定義什麼叫「對」,又自己判定自己有沒有做對。 綠燈是它自己說的,diff 是它自己審的。多數時候沒事——直到某次它跑在錯的目錄卻回報「成功」,或一段 migration 被守衛邏輯靜默跳過,而測試照樣全綠。
把工作拆給多個 AI,真正買到的不是「三顆腦袋」,是把這種自我背書拆開。這篇不談某個特定工具怎麼裝,談的是一套你可以映射到自己技術棧的參考架構:哪些角色、哪些契約、哪些關卡。文末有個 case 說明我自己怎麼實作的,但那只是「一種」實作,不是重點。
唯一的核心命題: 沒有任何一個 agent 能同時定義正確、又判定自己是否正確。所以每一份產物——計畫、測試、發現、那句「成功了」、那盞綠燈——都要被獨立地對回一個可查證的 ground truth(原始碼、凍結的測試、schema、真實的 DB 狀態),而不是因為它「講得很篤定」就採信。
01 · 不變量:AI 的每一句話都是「提議」,不是「定論」
先把最容易被跳過的一點釘死:在這條管線裡,agent 產出的東西一律是提議,不是結論。 計畫是提議、測試是提議、「這裡有個 bug」的發現是提議、連綠色的 build 都是提議。它們的共同點是——都必須拿去對一個「不是 LLM」的東西核實:程式碼真的長那樣嗎?這個 API 真的存在嗎?這個測試真的因為它該失敗的原因而失敗嗎?diff 真的只動了該動的地方嗎?
這條不變量之所以重要,是因為它連「審查者本身也是 LLM」都涵蓋了。你的驗證者(不管是哪個模型)並不因為它是「審查角色」就可信;它可信,是因為它比對的對象是可查證的。拿掉那個可查證的對象,整套就退回成「一群 LLM 互相說服」——那才是真正沒有底。
02 · 參考架構:四種角色,一條規則
把管線想成一組責任,不是一組品牌。同一個工具可以兼好幾個角色;但有一條規則不能破:
同一次 run,不能既產出一個聲稱、又是那個聲稱的最終裁判。 作者與裁判必須是不同的 run(最好是不同的模型 / 不同的 context)。
管線的形狀(→ 表示往下一步,關卡就在步驟之間):
- 計畫者 → 產出變更計畫 + 測試計畫(要改哪些檔、邊界、什麼不測)。
- 【迴圈 ①】計畫者 ⇄ 計畫驗證者 → 把計畫引用的型別/API/schema 對回「現有」程式碼,來回到兩邊都同意才定案。
- 實作者 → 依「凍結的」計畫寫測試 + 實作,讓測試轉綠。
- 【關卡 ②】測試驗證 + 最終驗收 → 測試先審過才凍結成契約;實作後跑測試、審 diff、查副作用——不採信實作者的自述。
- 稽核者(發現迴圈) → 對「已經在跑的程式」提出發現,一樣要對回原始碼才算數。
三個關卡:① 計畫迴圈 · ② 實作閘(含測試審查與最終驗收)· 稽核者走的發現迴圈。每一個都是「把提議對回可查證對象」的同一條原則落在不同位置。
角色 → 責任 → 禁止事項
這張表是全篇最該搬走的東西。把你手上的工具(Cursor、Aider、某個 CLI、某個 agent framework)按責任對號入座,不要按品牌:
| 角色 | 責任 | 對回什麼 | 禁止 |
|---|---|---|---|
| 計畫者 | 產出具體變更計畫+測試計畫:點名檔案、API、假設、邊界、以及「不測什麼」 | —(它是提議源) | 寫正式程式碼 |
| 計畫驗證者 | 在動工前,把計畫引用的型別/模組/schema/指令路徑對回現有程式碼——確認它引用的東西真的存在 | 現有原始碼、schema | 蓋章放行 |
| 實作者 | 依凍結的計畫寫測試與實作,追求「執行」而非「判斷」 | 凍結的計畫 | 動工後改凍結的測試 |
| 測試驗證者 | 在測試變成契約前,審它是否忠實反映計畫、是否因各自獨立的原因失敗(可診斷的紅燈) | 計畫、測試語意 | 讓無資訊的 RED 過關 |
| 最終驗收者 | 跑測試、審 diff、查 DB/schema/runtime 副作用 | 測試結果、diff、真實狀態 | 採信實作者的「成功」自述 |
| 稽核者 | 審「已在跑的程式」、提出發現——它的輸出不是真理,要進發現迴圈核實 | 原始碼、真實行為 | 把「發現」當定論 |
| 適配層 / wrapper | 穩住工具調用:吸收 CLI 變動、模型專屬旗標、sandbox、工作目錄、context 打包、重試、輸出正規化 | —(基礎設施) | 假裝它能保證正確性 |
03 · 三個關卡:不變量落在哪三個地方
「每份產物都要對回可查證對象」這句話,具體落在三處。前兩處你若做過嚴謹 TDD 其實已經有了雛形,第三處常被忽略:
① 計畫迴圈——對回「現有的碼」,不是「還沒寫的實作」。 這裡有個常被搞混的點:新功能是先有計畫、後有碼。 計畫階段還沒有實作可比,能比的只有它引用的現況存不存在——這個型別在嗎?這個 module 的介面是這樣嗎?(只有在「修正」既有程式時,才有一份舊的目標碼可對。)而且計畫不是「一個 AI 交出、另一個審一次」,是兩個 AI 來回到彼此都同意才定案。計畫者可以是作者,但它對不對,要另一個角色核實過、兩邊都點頭才算數。
② 實作閘——測試先凍結成契約,實作去追它。 測試在變成契約前要先被審(是否反映計畫、是否可診斷地失敗);凍結後,實作者的任務只有一句:讓已凍結的測試通過,只改實作、不准動測試。 最終驗收跑測試、審 diff——但不採信實作者自己說「成功了」。這是閘,不是迴圈:驗不過就退回重做。
③ 發現迴圈——這條管線也是「找出已在跑的程式其實是錯的」。 很多人只把這套當「把新程式寫對」的工具。但同一條不變量對現存程式一樣成立:讓一個獨立、對抗式的角色去審「看起來會動」的程式,問一句「它真的對嗎?」——這是純診斷用途。它挖出來的不是新寫的 bug,是早就在跑的 bug(見文末 case)。它的「發現」一樣是提議,要對回原始碼才算數。
04 · 把它映射到你的技術棧
你不需要跟我用一樣的工具。你要做的是回答幾個設計問題:
- 誰當計畫者? 選一個擅長「讀你的 codebase、產結構化計畫」的模型/工具。它只出計畫,不出正式碼。
- 誰當計畫驗證者? 另一個能跑工具、讀原始碼、比對引用的角色(通常是你主力的 agentic 環境)。跟計畫者來回到兩邊同意。
- 你的契約是什麼? 凍結的測試是首選。沒有可當契約的測試,這套的地基就不穩。
- 誰是最終驗收者? 能跑測試、審 diff、查真實副作用的角色——而且它不是寫實作的那一個 run。
- 誰握著調用層? 見下一節——這通常是決定「你跑得起來 vs 別人也跑得起來」的關鍵。
把上面那張角色表照抄、每一列填上你自己的工具,就是你的「責任配置」。唯一不能破的一條:同一次 run 不能既是某聲稱的作者,又是它的最終裁判。
05 · 把「工具調用」這一層獨立出來——這是架構決策,不是雜事
這點最容易被當成瑣事,其實是成敗關鍵。你選的那些 CLI/agent 工具是會一直變的移動目標:旗標改、模型參數改、sandbox 模式改。手動去記這些、每次靠 orchestrator「憑印象」呼叫,就會踩到「參數漂移在推理開始前就讓整輪空轉、零檔案寫入」這種坑——而這跟模型聰不聰明一點關係都沒有,是純調用層的事。
解法不是「orchestrator 再多學一點旗標」,是把調用封進一個 adapter 層:它固定住每個工具的「當前正確用法」——旗標、run-mode、工作目錄、context 打包、重試規則、輸出正規化。你的上層邏輯呼叫 adapter、不直接碰 CLI。這一層不保證任何正確性(別把它當魔法),它只保證調用是穩的——而這往往正是一條 pipeline 從「我跑得起來」變成「別人照著也跑得起來」的那一步。
06 · 什麼時候該跑全套,什麼時候別
誠實講:對小改動,全套六步是純 overhead。ceremony 要隨改動大小縮放。 一道防呆、一行提示這種,分工的來回本身就大於它擋下的風險,直接動手更快。
值得跑全套的,是風險高、跨切面、會留下來、動到 schema、或難以回退的工作。判準不是「大或小」,是「錯了的代價」對上「來回的成本」。
07 · 架構師會質疑的地方(先講在前面)
「這不就是拿速度換正確?」 不是。多出來的是測試與跨角色審查那幾段來回——但那份成本,本來就是任何要長期維護的程式都該付的;管線只是逼你當場付清、而不是欠著。而且它不是速度取捨:正因為施工的模型寫得沒有你最強的模型準,你才更要用測試把它框死;越不穩的實作者,圍它的測試要越緊。整體快不快,不是這裡在談的事。
「直接用一個更強的模型不就好了?」 更強的模型降低錯誤率,但消不掉自我驗證的風險——它還是同時當作者和裁判。這條管線解的是後者,不是前者。
「LLM 不是不可預測嗎,這樣怎麼可靠?」 可靠的部分從來不是 LLM,是契約:凍結的測試、原始碼核對、diff 審查、schema 檢查、可重現的指令。LLM 是提議源,契約是裁判。
「這是不是過度工程?」 如果你對所有改動都跑全套,是。所以第 06 節那條規則要當真——小改動就別上這套。
08 · 實務上會踩到的失敗(把它擺在檯面上)
這套不是拿來炫「不會出錯」,正好相反——它的價值就在於把這些失敗接住:
- 施工者謊報成功: 回報「成功」,實際跑在錯的目錄、一行沒寫。只看回報就會提交一個沒改的 commit。→ 最終驗收必須自己跑測試、審 diff。
- 測試全綠、但改動被靜默跳過: 一段 migration 的守衛邏輯誤判、整段擴充被無聲略過,本地測試照樣全綠。→ 要對回真實的 schema/DB 狀態,不只看綠燈。
- 稽核者的「發現」是誤報: 它很有信心地報了「高嚴重度問題」,結果是它只讀了半個上下文、沒讀到防呆其實在別的地方。→ 發現要對回原始碼;subagent 的發現,範圍就等於它讀過的東西。
- 調用層在推理前就壞: 參數漂移讓整輪空轉、零檔案寫入。→ 這是 adapter 層的事,不是方法的事。
採用前的自檢清單
- [ ] 我們有「能當契約」的規格嗎?(可凍結的測試 / 明確斷言)
- [ ] 測試能「可診斷地」失敗嗎?(每個紅燈有各自的原因,不是一律 NotImplemented)
- [ ] 驗收角色能真的讀到原始碼、diff、真實副作用嗎?(不是只讀 agent 的回報)
- [ ] 作者與裁判是不同的 run 嗎?(同一次 run 不能既產出聲稱又當它的裁判)
- [ ] 工具調用有被 wrapper 包住嗎?(CLI 一變,你不用重學旗標)
- [ ] 我們有一條「小改動就別跑全套」的明確規則嗎?
Case · 我自己的實作(只是一種實作)
Codex 出計畫 · Claude Code 核實+驗收 · Grok 施工。
計畫者=Codex(出逐任務、測試優先的計畫);計畫驗證+最終驗收=Claude Code(把計畫對回現有碼、跑測試、審 diff);實作者=Grok(依凍結計畫寫 test+impl)。兩個 CLI 都不裸打——它們是會一直變的移動目標,我裸呼叫常常出錯,現在也還是——所以透過各自的 Claude Code plugin(adapter 層)調用,讓 plugin 固定住「當前正確用法」。這是調用層的事,跟方法無關。
樣本邊界:跨幾個 repo(一個 Rust + Turso 的 CLI、一個 Rust + Python 的工具、一個 Zig 專案),量級 n≈十幾的觀察,不是 benchmark、不是統計結論。每個 repo 次數都不多。
它挖到的真 bug(都在已經在跑的程式裡):地圖靜默空白(活動沒連座標卻沒人示警)、一個指令不帶欄位時寫入零列卻照印「✅ 已更新」、一個匯入在全部被跳過時卻印「✅ 已儲存」。這些不是新寫的錯,是稽核+驗收才逼出來的。
如果只帶走一句:多 AI 不是為了更聰明,是為了讓「定義正確的人」和「判定正確的人」不是同一個。 其餘的——角色怎麼分、契約放哪、調用誰來握——都是這句話落地的方式。把上面那張角色表和自檢清單抄走,換成你自己的工具,你就有了自己的一條。
這篇是把一篇第一人稱工作筆記整理成可搬走的參考架構。所有失敗場景均為實測,樣本小、不外推;每個論斷都設計成能被你在自己的 codebase 上幾分鐘內驗證或反駁。歡迎照著搭一條、然後告訴我哪裡不成立。
Top comments (0)