「測試可以證明 bug 存在,但永遠無法證明 bug 不存在。」
Edsger Dijkstra 在 1969 年的一場軟體工程會議上說過這句話,隔年又寫進了他的《結構化程式設計筆記》。他的意思是:測試再多也只是抽查,要確定程式正確,得靠數學推理和證明。
業界後來沒有走上全面證明的路,倒是發展出一套很依賴測試的習慣,叫回歸測試。每次改完程式,就把以前寫過的測試全部重跑一次,確認新的改動沒有把原本好好的功能改壞。它證明不了程式沒有 bug,能確認的是一件更實際的事:昨天會的,今天還會。
Day 6 談的測試,對象是 agent 寫出來的程式碼。這篇要測的,是 agent 本身。
前面十幾天談的 CLAUDE.md、skills、hooks、subagent 的設定,都在控制 agent 的行為。它們用自然語言寫成,做的事情卻跟程式一樣:輸入一個任務,決定 agent 怎麼處理。
改程式的時候有編譯器、有測試、有 CI;改 CLAUDE.md 的時候,通常什麼都沒有。加了一條規則想修正某個壞習慣,另一個原本好好的行為可能就變了,而且不會有任何錯誤訊息。換模型也一樣,新模型在這個專案上是變好還是變差,沒有測試就只能憑感覺。
agent evals 想補上的,就是這一塊。
回歸測試的類比到這裡會斷掉。函式的測試是確定的,同樣的輸入每次得到同樣的結果,紅燈就代表壞了。agent 不是這樣,同一題跑三次,可能兩次成功一次失敗,而那次失敗也許跟這次改動毫無關係。
Anthropic 在 2026 年初的一篇工程文章裡,把每一次執行稱為一次 trial,並區分了兩種算法。pass@k 是跑 k 次裡至少成功一次的機率,pass^k 則是 k 次全部成功的機率。假設一個任務單次成功率是七成,跑十次的時候,pass@k 幾乎是百分之百,pass^k 只剩大約百分之三。同一個 agent,看哪一個數字,結論完全相反。
所以 agent 的回歸測試,看的是機率的變化,判斷時要拿改動前的分數來比。掉了一次不一定是退步,同一題多跑幾次、跟改動前比,才看得出是真的變差還是本來就不穩。
文章另一個提醒是,該檢查的是結果,不是過程。agent 經常用設計測試的人沒想到、但完全正確的方式完成任務,規定它一定要先讀 A 檔再改 B 檔,等於把一個正確的新做法判成失敗。
Claude Code 從 v2.1.269 起有一個 claude plugin eval 指令,用來測 plugin,包括放在 plugin 裡的 skills。每個測試案例是一句使用者真的會打的 prompt,加上一個或幾個評分條件。不想從零寫起,claude plugin eval init 會先讀過 plugin、問清楚好的結果長什麼樣子,再提出案例和評分條件,試跑之後寫成檔案。
評分條件分成兩類。一類直接從執行紀錄判斷,例如回覆裡有沒有出現某段文字、有沒有呼叫某個工具、有沒有新建某個檔案,這些不用額外花錢。另一類交給另一個模型,照著寫好的標準判斷回覆合不合格。Anthropic 的文章建議的順序也是如此:能用程式判斷的先用程式,比較難界定的才交給模型。
每個案例預設跑三次取平均,因為跑一次說明不了什麼。同樣的次數還會在不載入 plugin 的情況下再跑一輪,作為基準線,兩邊分數相減,就是 plugin 實際的貢獻。分數很高不代表 plugin 有幫上忙,Claude 本來就做得到的事,plugin 拿滿分也沒有意義。
案例的分數低於門檻時,指令會以失敗結束,可以放進 CI,官方文件有在 CI 裡執行需要的旗標和範例。門檻預設是 1.0,也就是載入 plugin 時,每個案例三次都要全對才算過。這符合回歸測試「以前會的都要會」的標準,但對本來就不太穩定的任務太嚴格了,這時可以調低門檻或增加執行次數。
要注意的是,這裡的基準線是「沒有 plugin」,不是「改動前的版本」。想知道這次改動有沒有讓分數變差,得自己把上一版的結果留下來對照。費用也不能小看:基準線加上本身,一個案例預設就是六次完整的 agent 執行,每次執行裡又有多次模型呼叫,用模型評分的條件還要另外算評審的呼叫,全部都會算進方案用量或 API 帳單。
不在 plugin 裡的設定,例如個人或專案層級的 skill,可以用 skill-creator 這個 plugin 跑類似的比較。專案的 CLAUDE.md 則得自己動手:用 claude -p 跑一組固定的任務,再檢查結果。要注意兩件事。--bare 模式會跳過 CLAUDE.md,等於沒測到要測的東西;不加的話,又會混進這台電腦上的個人設定和記憶,最好放在乾淨的環境裡跑。另外,Day 16 的 structured output 可以用來取得格式固定的回報,但那是 agent 自己說它做了什麼。真正的判斷,要看它實際改了哪些檔案、跑了哪些指令。
Anthropic 的建議很務實:一開始不需要很大的測試集,從二十到五十個真實發生過的失敗開始就夠了。剛開始調整時,每次改動的影響通常很明顯,少量的案例就看得出差別。
最好的案例來源,是每一次糾正 agent 的時候。Day 9 舉過一個例子:告訴 agent 這個專案的測試不要 mock database。Claude Code 會把這類糾正寫進個人的記憶,但記憶只在自己的電腦上,乾淨的測試環境讀不到。要讓它變成能測、也能跟團隊共享的規則,就把它寫進專案的 CLAUDE.md,再寫一個對應的測試案例:prompt 是「替訂單模組補一個整合測試」,評分條件是新建的測試檔裡不能出現 mock 資料庫的寫法。之後每次改設定、換模型,這個案例都會確認 agent 還守著這條規則。
Dijkstra 那句話放到 agent 身上一樣成立。一組 eval 全部通過,不代表 agent 在所有情況下都會做對;它能說明的,只有這些曾經出過錯的地方,現在沒有再出錯。
回歸測試從來沒有承諾更多,它只負責守住已經做對的事。五十多年來被守住的一直是程式碼,現在多了一樣要守的東西:寫給 AI 的那些指令。
延伸閱讀