背壓:AI 驅動軟體工程中缺失的一環
編碼代理(coding agents)的興起讓開發者面臨一個令人沮喪的兩難困境。一方面是「無人值守」的方法:讓 LLM 在儲存庫中自由發揮,這雖然速度快,但往往會導致大量低品質的 PR 和回歸錯誤(regression bugs)。另一方面則是「自動完成」的方法:將代理視為一種高級的建議工具,人類必須審查每一行程式碼。第二種方法雖然安全,但實際上削弱了委派的初衷,因為人類仍然是主要的瓶頸。
為了超越這些極端情況,我們需要引入系統工程中的一個概念:背壓(backpressure)。在傳統系統中,背壓是一種機制,由下游組件向傳送端(upstream producer)發出訊號,表示其無法再接收更多工作,從而迫使傳送端減速或修正其輸出。在 AI 編碼的語境下,背壓意味著建立自動化護欄,拒絕讓代理繼續前進,直到程式碼符合特定的品質與正確性標準。
自動化背壓的演進
從歷史上看,軟體工程一直在將「拒絕」的權限從人類身上移開。我們從手動審查開始,接著加入了編譯器、Linter、型別系統,最後是 CI/CD 流水線。每一層都增加了一種新的背壓形式,確保在人類審查者看到 PR 之前,程式碼在語法上已經是正確的、型別安全的,並且通過了基礎測試。
當傳送端是一個寫程式碼速度比任何人類閱讀速度還快的 LLM 時,人類往往會成為預設的背壓。這對昂貴的人類認知資源是一種低效的使用。目標是停止讓人類成為主要的過濾器,轉而建立一個「機器對機器」的反饋迴圈,讓代理在人類看到結果之前,先針對自動化檢查進行迭代。
在實踐中實施背壓
建立有效的背壓迴圈需要從簡單的提示詞(prompts)轉向結構化、多階段的工作流。以下機制可以整合到代理的迴圈中,以確保高品質的輸出:
1. 迭代階段:快速反饋
在核心開發迴圈中,代理不應只是寫完一個補丁(patch)就停止。它應該被要求在每一次迭代中執行一組品質檢查:
- Linting 與測試: 標準測試套件和 Linter 應該是第一道防線。必須指示代理在每次提交補丁後執行這些檢查,並且在結果為綠燈之前不繼續前進。
- 基準測試(Benchmarking): 對於效能敏感的應用程式,整合具有結構化輸出的基準測試工具,可以讓代理立即偵測到回歸錯誤。
- 審查代理(Review Agents): 使用獨立的 LLM 實例作為「審查代理」,可以捕捉到決定性測試(deterministic tests)所遺漏的、主觀的品質問題——例如可讀性、過度的複雜度或鬆散的型別定義。
2. 後迭代階段:真實世界驗證
一旦自動化測試通過,代理應該進入一個模擬真實世界使用的驗證階段:
- 透過工具進行手動測試: 教導代理執行
docker-compose、使用cURL進行 API 測試,或使用 Playwright 進行瀏覽器驗證,可以確保程式碼在實際環境中運行,而不僅僅是在模擬測試中。 - 視覺設計審查: 對於前端工作,可以要求代理擷取實作的螢幕截圖,並使用特定的對齊與間距啟發式演算法(heuristics)將其與設計規範(例如 Figma 檔案)進行比較。
3. 規劃與監控階段
背壓應該存在於寫下第一行程式碼之前,以及 PR 開啟之後:
- 規劃審查: 在實作之前,代理會產出一個輕量級的架構規劃。必須由一個審查子代理(reviewer sub-agent)核准此規劃,以防止代理走向根本性的錯誤路徑。
- PR 監控: 在開啟 PR 後,代理應該監控 PR 達 24 小時,自動處理 CI 失敗、合併衝突或來自其他人類/AI 審查者的評論。
關鍵觀點與權衡
雖然「背壓」方法提供了一種通往真正委派的途徑,但它並非沒有爭議。社群討論突顯了幾個關鍵挑戰:
- Token 成本: 執行多個審查代理和迭代測試迴圈在 API 成本方面可能非常昂貴。正如一位評論家所言,當自動化從容器啟動到整合測試的所有流程時,「token 消耗得非常快」。
- 決定性 vs. 非決定性: 有人認為依賴另一個 LLM 進行審查是多餘的。相反,他們建議使用 hooks(例如 pre-commit hooks 或 CI gates)來重新引入決定性。透過將檢查放在測試框架(harness)中而非提示詞中,你可以確保代理不會「忘記」執行其測試。
- 過度工程化: 存在一種風險,即維護驗證層的成本變得比實際編碼的工作量還要大。評論家認為,對於許多專案而言,一個簡單的、由規範驅動的規劃與幾次手動迭代,比複雜的代理式流水線更有效率。
結論
無論這些護欄是透過複雜的代理技能還是簡單的 git hooks 實施,軟體工程的發展趨勢是明確的:我們必須將正確性的負擔從人類身上移轉到系統中。正如原論文作者所言:「任何依賴人類來捕捉機器錯誤的系統,都會受到人類能力的限制,而非機器的能力。」