包包baobaolin.com
date
entry
010
topic
tooling
rev

驗收 AI 寫的程式:報告是意圖,不是事實

把一塊實作委派給 coding agent,最後你收到的是一段完成報告:改了哪些檔、build 過了、測試綠了。那段文字描述的是它打算做的事。要知道它做了什麼,只有一個來源——diff。

委派本身很有效。定義清楚、邊界明確的實作——schema 已經在了只缺 API 接線、跨十個檔案的同模式改動、卡住之後想要第二輪診斷——丟給另一個 agent 做,比自己敲快得多。

問題出在收工那一刻。你拿到一段結構良好的報告,讀起來完全合理,而且通常大部分是對的。難處正在這裡:如果它整段都是錯的,你第一眼就會發現。

報告跟 diff 會差在哪

我實際遇過的落差,大致是這幾類:

  • 說改了 A,實際改到 B。同一個檔裡有兩個長得很像的 struct 初始化,它改了其中一個,報告寫的是另一個的名字
  • 順手改了沒交代的東西。brief 明說不要碰前端,但它「順便」統一了一個型別
  • 漏改。五個檔案裡有四個照做,第五個報告有寫、diff 裡沒有
  • 測試「通過」的方式是改斷言。這一類最貴,下面單獨談
  • build 結果是它上一次跑的。報告寫 all pass,但那是在最後一次修改之前跑的

這些都不是說謊。產生報告的過程跟執行的過程是兩條路徑——報告是從它的意圖與計畫生成的,而不是從檔案系統讀回來的。當計畫與實際執行之間有落差時,報告會忠實地反映計畫。

這跟把工具錯誤改寫成流暢完成報告是同一件事的溫和版本:輸出的流暢度與正確度沒有相關性,而人對流暢的文字有信任偏誤。

驗收流程

不管報告寫什麼,這幾步都跑一次。

1. 自己讀 diff,不讀摘要

git status -sb
git diff

讀的時候問四件事:

  1. 只動了該動的檔嗎?git status 裡有沒有 brief 沒提到的檔案
  2. 符合鄰近程式碼的寫法嗎?新欄位的 json tag、omitempty、nil 判斷有沒有跟隔壁幾行一致
  3. 改到對的那一處嗎?同一個檔案裡有多個同名或近似的結構時,逐處核對行號與所在函式
  4. 錯誤處理的層級對嗎?這裡失敗該中斷、還是只記一筆 warning——這是慣例問題,agent 猜不出來

2. 自己重跑驗收指令

不要拿它的輸出當證據。同樣的指令自己跑一次:

go build ./... 2>&1 | tail -5
go vet  ./... 2>&1 | tail -5
go test ./internal/service/ -run '<相關前綴>' -count=1 2>&1 | tail -8

-count=1 是必要的,否則你可能讀到快取的測試結果——那就又回到「相信別人跑過」的狀態了。

在受限的沙箱裡跑 Go 可能會因為預設快取目錄沒有寫入權限而失敗,這時候 export GOCACHE=/tmp/go-cache 就能過。這是環境問題,不要誤判成程式壞掉。

3. 特別看測試檔的 diff

如果這次委派包含「讓某個失敗的測試過」,那麼測試檔的 diff 要單獨拉出來看

git diff -- '*_test.go'

要分辨的是兩種情況:斷言被修正成合理的期望值(可以),或斷言被放寬到不再檢查原本要檢查的東西(假綠燈)。前者是修好了,後者是把溫度計拿走。

這件事人也會做,只是頻率低——因為人知道之後會被 review。所以真正的差別不在誰比較誠實,在於你有沒有在看

把問題分兩級

驗收完要下判斷,我分兩級處理:

  • 阻斷——編譯或測試掛掉、改到 brief 明說不要碰的範圍、pattern 與既有程式不一致。退回去重做,或自己修
  • 非阻斷——少一個 omitempty、空值回 null 而不是 [] 這類外觀問題。列出來,不擋

分級的用處是避免兩種極端:因為一個 cosmetic 問題退回整份工作,或因為「大致上可以」就把範圍失控的改動放行。

驗收的成本是在委派時決定的

上面的流程之所以跑得動,前提是你事先知道「該改哪些檔」。如果 brief 只寫「把這個功能做好」,那驗收時你沒有比對基準,只能重讀整份 diff 猜意圖——那還不如自己寫。

一份能被驗收的 brief 至少要有:

  • 逐項要做什麼,附檔案路徑(有行號更好)——這同時是驗收清單
  • 已知前提:哪些東西已經存在、不要重做(例如欄位已在 prod DB 與 model 裡,不要再生 migration)
  • 負面清單:明確不要碰的範圍。這條最常被省略,也最常被違反
  • 驗收門檻:要跑哪些指令。寫進 brief,讓它自己先跑一次——雖然你還是會重跑,但至少明顯的錯誤不會流到你這邊

另外,有些東西不該委派:金流與狀態機這類出錯就直接反映在金額上的邏輯,自己寫、逐行看,比事後驗收便宜。

這不是信任問題

需要澄清的是,這套流程跟「AI 可不可靠」沒什麼關係。人類同事送 PR 過來,你也不會因為描述寫得清楚就直接合併——你會看 diff、看 CI。

Code review 存在的理由從來不是懷疑作者,而是因為作者對自己改動的認知,跟改動實際的內容,是兩份可能會分歧的東西。這一點對人和對 agent 完全一樣,只是 agent 產出的量大很多,所以分歧的絕對次數也大很多。

如果只記得一件事

把「它說做了什麼」和「檔案裡有什麼」當成兩份獨立的資料,然後比對。 前者拿來知道該去哪裡看,後者才是驗收的依據。省掉比對這一步,你交付出去的就是一份沒人讀過的 diff。

修訂紀錄

  1. 首次發布