Skip to content

English Version →

本篇代碼示例:code/ 實戰練習:Project 05. 讓 agent 自己檢查自己做的對不對

第九講. 防止 agent 提前宣告完成

你讓 agent 實現"密碼重置"功能。它改了數據庫 schema、寫了 API 端點、加了郵件範本,跑了單元測試(全部通過),然後自信地告訴你"做完了"。你實際一跑——密碼重置鏈接發不出去(郵件服務配置缺失)、數據庫遷移半途失敗(schema 不一致)、端到端流程根本沒走過一遍。

這種感覺你一定不陌生——考試時把卷子寫得滿滿當當,信心滿滿地第一個交卷,結果成績出來不及格。卷子寫滿了,不代表做對了。

這不是偶然事件。Guo 等人 2017 年在 ICML 上的經典論文證明:現代神經網絡系統性地過度自信——模型自報的置信度顯著高於實際準確率。AI 編碼 agent 也一樣:它"覺得"做完了,但實際上差得遠。你的 harness 必須用外部化的、基於執行的驗證來替代 agent 的"感覺"。

滑坡效應

過早完成聲明幾乎總是一樣的套路:代碼看著還行——語法正確、邏輯似乎合理,靜態檢查沒有明顯錯誤。但 harness 沒有強制要求全面執行驗證,agent 跳過了實際運行或只跑了部分測試。跑了單元測試但跳過集成測試,跑了測試但沒檢查覆蓋率。最後"代碼看起來沒問題"就被當作了"功能已完成"的證據。交卷了。

每一步都在丟失信息。從任務規範到代碼實現到運行時行為,每次轉換都可能引入偏差,而每次跳過的驗證都加劇了信息不對稱。

三層終止檢查

核心概念

  • 過早完成聲明:agent 斷言任務完成,但實際上存在未滿足的正確性規範。核心問題:agent 依據代碼層面的局部信心做判斷,系統級正確性需要全局驗證。
  • 置信度校準偏差:agent 自報的完成信心與實際完成質量之間的系統性差距。對複雜多檔案任務,這個偏差顯著為正——agent 總是比實際做得更自信。就像學生考完試估分總是偏高。
  • 終止標準:一組明確的、可執行的判定條件,定義在 harness 裡。agent 必須滿足所有條件才能聲明完成。"完成"從主觀判斷變成了客觀判定。
  • 驗證-確認雙閘門:第一層驗證檢查"代碼是否正確實現了指定行為";第二層確認檢查"系統級行為是否滿足端到端需求"。兩層都通過才算完成。
  • 運行時反饋信號:來自程序執行的日誌、進程狀態、健康檢查。這是 harness 判定完成質量的客觀基礎。
  • 完成優先級約束:先驗證功能正確性,再處理性能,最後管風格。核心功能沒驗證通過之前,不許做重構。

單元測試通過 ≠ 任務完成

這是最常見的陷阱,也是最危險的一個。agent 寫了代碼,跑了單元測試,全部綠色,然後說"做完了"。但單元測試的設計哲學——隔離被測單元、模擬依賴——恰好使其無法檢測跨組件問題:

接口不匹配:渲染進程傳給預加載腳本的檔案路徑是相對路徑,但預加載腳本期望絕對路徑。各自的單元測試都用了 mock,都通過了。只有端到端跑通時才發現問題。就像樂隊裡每個樂手各自練習都完美,但合在一起才發現定調不一樣。

狀態傳播錯誤:數據庫遷移改了表結構,但 ORM 的緩存層還持有舊結構的緩存條目。單元測試每次都是全新的 mock 環境,不會暴露這種跨層狀態不一致。

環境依賴性:代碼在測試環境(一切 mock)行為正確,在真實環境因配置差異、網絡延遲、服務不可用而失敗。就像在排練室唱得很好,上臺演出時音響設備出了問題。

"順便重構"是完成判定的毒藥

Claude Code 有一個常見行為模式:在核心功能還沒驗證通過時就開始重構代碼、優化性能、改進風格。Knuth 說的"過早優化是萬惡之源"在 agent 場景中有了新含義——重構會改變已完成驗證和未完成驗證之間的邊界,可能破壞之前隱式正確的代碼路徑。就像你數學大題還沒做完,就跑去把前面選擇題的答案重新抄一遍格式——浪費時間不說,還可能抄錯了。

自我評價的系統性偏差

Anthropic 在 2026 年的研究中發現了一個更深層的失敗模式:當 agent 被要求評估自己的工作時,它系統性地過度正面評價——即使人類觀察者認為質量明顯不達標。 這就像讓學生自己給自己判卷——對自己的答案總是特別寬容。

這個問題在主觀任務(如設計審美)上尤其嚴重——"佈局是否精緻"是一個判斷題,agent 可靠地偏向正面。即使在有可驗證結果的任務上,agent 也會因為判斷失誤而影響表現。

解決方案不是讓 agent "更客觀"——同一個模型既生成又評估,內在地傾向對自己慷慨。解決方案是把"幹活的人"和"檢查的人"分開。 就像考試不能讓學生自己批改自己的卷子——得有個獨立的閱卷老師。

一個獨立的評估 agent,經過專門調校為"挑剔"之後,比讓生成 agent 自我評估有效得多。Anthropic 的實驗數據:

架構運行時長成本核心功能是否可用
單 agent 裸跑20 分鐘$9否(遊戲實體無法響應輸入)
三 agent(planner + generator + evaluator)6 小時$200是(遊戲可以正常遊玩)

這是同一個模型(Opus 4.5),同一段提示詞("做一個 2D 復古遊戲編輯器")。區別只在 harness——從"裸奔"到"planner 擴展需求 → generator 逐功能實現 → evaluator 用 Playwright 實際點擊測試"。

來源:Anthropic: Harness design for long-running application development

怎麼防止提前交卷

1. 外部化終止判定

完成判定不應該由 agent 自己做。harness 獨立執行終止校驗,輸入是運行時信號,不是 agent 的置信度。在 CLAUDE.md 裡寫清楚:

## 完成定義
- 功能完成 = 端到端驗證通過,不是"程式碼寫完了"
- 必須執行的驗證層級:
  1. 單元測試通過
  2. 集成測試通過
  3. 端到端流程驗證通過
- 在第 1 层没通過時,不许进入第 2 层
- 在第 2 层没通過時,不许进入第 3 层

2. 構建三層終止校驗

  • 第一層:語法與靜態分析。成本最低,信息量最小,但必須通過。這是最低限度的檢查——字都沒寫錯才能往下看。
  • 第二層:運行時行為驗證。測試執行、應用啟動檢查、關鍵路徑驗證。這是核心完成證據。不僅要寫了,還要能跑。
  • 第三層:系統級確認。端到端測試、集成驗證、用戶場景模擬。防止過早聲明的最後一道防線。不僅要能跑,還要跑對。

3. 為 agent 設計好的"紅筆批註"

OpenAI 在 Codex 實踐中提出了一個特別有效的模式:給 agent 寫的錯誤消息要包含修復指導。不要像閱卷老師只畫個大紅叉,要像好老師一樣在旁邊寫上"這裡應該怎麼改"。不要用 "Test failed",而用 "Test failed: POST /api/reset-password returned 500. Check that the email service config exists in environment variables. The template file should be at templates/reset-email.html." 這種具體的、可操作的反饋讓 agent 能自我修正,而不需要人類介入。

4. 捕獲運行時信號

有效的運行時信號包括:

  • 應用是否成功啟動並達到就緒狀態?
  • 關鍵功能路徑在運行時是否執行成功?
  • 數據庫寫入、檔案操作等副作用是否正確?
  • 臨時資源是否被清理?

實際案例

任務:實現用戶密碼重置功能。涉及數據庫操作、郵件發送和 API 端點修改。

提前交卷路徑:agent 修改數據庫 schema、編寫 API 端點、添加郵件範本、跑單元測試(通過)、聲明完成。卷子寫得滿滿當當。

實際扣分項:(1) 端到端流程未測試——重置鏈接的實際發送和驗證未確認。(2) 數據庫遷移在部分執行後失敗,導致 schema 不一致。(3) 郵件服務配置在目標環境中缺失。

harness 介入:終止校驗強制執行——(1) 啟動完整應用驗證重置端點可訪問;(2) 執行完整重置流程;(3) 驗證數據庫狀態一致性。所有缺陷在會話內被發現,節省了 5-10 倍的後續修復成本。獨立閱卷老師批出了真正的問題。

關鍵要點

  • agent 系統性地過度自信——置信度校準偏差是客觀存在的。卷子寫滿了不代表做對了。
  • 完成判定必須外部化——harness 獨立驗證,不信任 agent 的"感覺"。不能讓學生自己批自己的卷子。
  • 三層校驗缺一不可——語法通過、行為通過、系統通過,層層遞進。
  • 錯誤消息要像好老師的紅筆批註——包含具體修復步驟,讓 agent 能自我修正。
  • 核心功能驗證通過之前不許重構——完成優先級約束是防止過早優化的關鍵。

延伸閱讀

練習

  1. 終止校驗函數設計:為一個涉及數據庫遷移和 API 修改的任務設計完整的終止校驗。列出需要的運行時信號和每個信號的通過/失敗標準。在一個實際任務上運行,記錄它發現了哪些隱藏問題。

  2. 校準偏差測量:選 10 個不同類型的編碼任務,記錄 agent 的自報完成信心和實際完成質量。計算偏差值,分析它和任務複雜度的關係。

  3. 多層防禦實驗:對同一組任務跑三種配置——(a) 僅靜態分析,(b) 加單元測試,(c) 完整三層校驗。比較過早完成聲明的比例和未捕獲缺陷的數量。