為什麼綠燈測試對 AI 程式碼代理而言還不夠#

Back
Category : News

過去幾個月,我花了更多時間與 AI 程式碼代理合作。它們速度快、實用,而且在許多情況下,它們能比手動操作更快地修復小錯誤或實作特定變更。

但在使用它們的過程中,我一直回到同一個問題:我如何知道圍繞修復的證據是否真的有意義?

很容易要求代理修改一些程式碼、執行測試,然後回傳綠燈結果。問題在於綠燈測試只告訴我最終版本通過了。它並不一定能告訴我該測試是否能夠偵測到原始的錯誤。

這個差異聽起來很小,但它非常重要。

想像一個 AI 代理修復了一個錯誤並新增了一個迴歸測試。最終測試執行通過,所以變更看起來不錯。現在想像我們拿完全相同的測試去對壞掉的程式碼版本執行,它在那裡也通過了。

在這種情況下,測試是綠燈的,但它並沒有真正證明錯誤已被修復。

這就是我想要更系統化處理的那種問題。



基本概念

原則很簡單:

壞掉的基準 + 迴歸探測 = 失敗

修復的結果 + 相同的迴歸探測 = 通過

如果相同的探測在壞掉的版本上失敗,而在修復的版本上通過,證據就會強得多。

如果它在兩個版本上都通過,那麼測試可能並沒有真正測試我們關心的錯誤。

這個想法成為我一直在建構的一個小型工具的基礎,叫做 AI Coding Agent Reliability Kit。

它是一個以 Linux 為優先的本地工作流程,用來以更強的機械化證據審查 AI 輔助的錯誤修復。

目標不是取代程式碼審查、安全審查或工程判斷。目標是讓圍繞 AI 生成修復所產生的證據更容易檢查且更難被誤解。



我希望從工作流程中獲得什麼

當我開始研究這個時,我不想要另一個只是執行指令並印出成功或失敗的包裝工具。

我想要一個能回答關於變更幾個問題的工作流程。

代理被允許修改什麼?

它是否停留在該範圍內?

最終驗證指令是否真的通過?

迴歸探測是否在壞掉的基準上失敗?

結果是否能在乾淨的重播中重現?

另一個人是否能在不需手動重建一切的情況下審查證據?

這些是基本問題,但一旦程式碼代理開始進行更大或更頻繁的變更,它們就會變得重要。



在代理開始前界定變更範圍

這個工具所做的事情之一,是在程式碼工作開始前定義任務和允許的路徑。

這很有用,因為代理有時能在修復請求問題的同時也修改不相關的檔案。最終結果可能仍然能運作,但變更範圍會變得比必要的大。

對於一個聚焦的錯誤修復,我通常想要一個清楚的邊界。

例如,如果任務只需要在一個來源檔案和一個迴歸測試中進行變更,那麼對不相關的 README、設定檔或其他套件的修改應該立即可見。

這個工具會追蹤宣告的範圍,並檢查最終變更是否停留在其中。

如果有東西在允許的邊界之外被改變,則會被回報,而不是默默接受結果。



只檢查最終結果是不夠的

一般的工作流程通常在類似以下的情況後結束:

pytest tests/test_bug.py

Enter fullscreen mode

Exit fullscreen mode

指令成功退出,所以任務被視為完成。

這很有用,但對於迴歸修復,我想要多一項資訊。

我想要知道當相同的探測對已知的壞掉基準執行時會發生什麼。

一個好的迴歸探測通常應該能區分這兩個狀態。

例如:

Broken version: FAIL
Fixed version:  PASS

Enter fullscreen mode

Exit fullscreen mode

這是比以下情況強得多的訊號:

Broken version: PASS
Fixed version:  PASS

Enter fullscreen mode

Exit fullscreen mode

第二個結果並不一定表示程式碼有錯。它表示證據很弱。

這個區別很重要。

工作流程不應該假裝一個測試證明了它並沒有證明的事情。



非區分性測試

這是我建構這個工具的主要原因之一。

一個測試看起來可能完全有效,卻仍然無法區分壞掉和修復的版本。

例如,想像一個函數應該要將兩個數字相乘,但目前卻是相加。

壞掉的程式碼可能看起來像這樣:

def calculate(a, b):
    return a + b

Enter fullscreen mode

Exit fullscreen mode

修復將它改成:

def calculate(a, b):
    return a * b

Enter fullscreen mode

Exit fullscreen mode

現在想像測試只檢查這個:

def test_calculate_exists():
    assert callable(calculate)

Enter fullscreen mode

Exit fullscreen mode

這個測試在修復後是綠燈的,但它在修復前也是綠燈的。

它證明了函數存在。它並沒有證明錯誤已被修復。

這種結果不應該被視為與真正的迴歸測試相同。

這個工具會偵測這種情況,並將證據回報為非區分性,而不是將最終的綠燈結果視為足夠。



範圍違規

我想要處理的另一種情況是,實際的程式碼變更是正確的,但允許範圍之外的東西被修改了。

這可能意外發生,或是因為自動化工作流程在執行期間碰觸到另一個檔案。

假設代理被允許修改:

src/formatter.py
tests/test_formatter.py

Enter fullscreen mode

Exit fullscreen mode

來源修復和測試可能都是正確的,但如果最終變更也修改了:

README.md

Enter fullscreen mode

Exit fullscreen mode

那麼工作流程應該另外回報這一點。

這裡的重要觀點是,正確性和範圍是不同的事情。

一個正確的來源修復並不會自動讓不相關的變更變得可接受。

這個工具會將這些關注點分開,讓審查者能清楚看到到底發生了什麼。



乾淨重播

我想要的另一個功能是能夠在需要時從乾淨狀態重現結果。

有時候變更會通過,是因為工作環境中留下了某些東西:產生的檔案、快取、本地狀態,或另一個未提交的變更。

乾淨重播有助於回答一個簡單的問題:

相同的結果是否能在不依賴目前工作目錄狀態的情況下被重現?

並非每個任務都需要完整的乾淨重播,但對於重現性很重要的變更,它能給審查者另一個有用的證據。



審查摘要

自動化的其中一個問題是它可能產生太多輸出。

原始日誌在調查某些事情時很有用,但它們並不總是審查變更的最佳方式。

這個工具會產生一個結構化的審查摘要,讓主要結果更容易檢查。

審查者可以看到以下項目:

Scope: PASS
Final verification: PASS
Broken baseline probe: FAIL
Fixed probe: PASS
Differential evidence: PASS
Replay: REPRODUCIBLE

Enter fullscreen mode

Exit fullscreen mode

如果有東西失敗,摘要應該清楚顯示,而不是隱藏在成功的最終指令後面。

這個想法是要減少審查者需要做的重建工作量。



證明包和完整性

工作流程也可以匯出證明材料並驗證其完整性。

當證據需要被移動、封存或稍後審查時,這很有用。

工作流程不僅僅說某個指令在某個時間點通過,它還能保留相關的結果記錄,並驗證預期的材料沒有改變。

這並不是要將工具變成安全認證系統。它只是一種讓每次執行的證據更易檢查和重現的方式。



READY_FOR_REVIEW 的意義

我想要保持非常清楚的一件事是最終狀態的意義。

當工具回報:

READY_FOR_REVIEW

Enter fullscreen mode

Exit fullscreen mode

它並不表示:

The code is definitely correct.

Enter fullscreen mode

Exit fullscreen mode

它也不表示:

The code is secure.

Enter fullscreen mode

Exit fullscreen mode

它同樣不表示:

This is automatically production-ready.

Enter fullscreen mode

Exit fullscreen mode

它的意思是設定的機械化證據閘門已通過,結果已準備好讓人類審查者檢查。

這個區別是故意的。

我不認為自動化工作流程應該默默地把機械化證據升級成語義化的工程判斷。

人類審查者仍然需要去看實際的變更,並決定它對系統來說是否正確。



為什麼這對程式碼代理來說更重要

人類開發者在進行變更時,通常腦中會有一些脈絡。他們知道自己碰了什麼、試圖修復什麼,以及他們做了什麼假設。

使用 AI 程式碼代理時,更多的工作可以自動且快速地發生。

這種速度很有用,但這也意味著更容易僅僅因為代理說任務完成且最終測試是綠燈就接受結果。

實作變得越快,就越有必要讓驗證流程變得明確。

我並不把這視為反對程式碼代理的論點。我有在使用它們。

重點是,如果代理要為我們進行更多變更,那麼圍繞這些變更的證據也應該變得更好。



以 Linux 為優先且本地化

工具的第一個版本在環境假設上刻意保持簡單。

它被設計用於 Linux,並使用本地工作流程,包含:

  • Python 3.10+
  • Git
  • 一個本地的專案儲存庫

我希望第一個版本能保持專注,而不是宣稱支援我還沒有適當驗證的平台。

Windows 和 macOS 不是已驗證的 V1 環境的一部分。

這可能在之後會改變,但我寧願明確說明實際測試過的內容。



這個工具不是什麼

這個工具並不試圖證明一個程式是正確的。

它不會取代單元測試、整合測試、靜態分析、安全審查,或良好的人類程式碼審查。

它也不會自動讓弱測試變強。

如果迴歸探測設計不佳,工作流程可以偵測某些類型的弱證據,例如一個在壞掉和修復版本上都通過的探測,但它無法理解每個專案的每一個語義需求。

它所做的是提供一個圍繞現有證據的更嚴格流程。

對我來說,那就是有用的部分。



實際工作流程

一個典型流程大致看起來像這樣:

  1. 定義任務和允許的路徑。
  2. 為程式碼代理產生一個有邊界的簡報。
  3. 讓代理實作修復。
  4. 執行宣告的最終驗證。
  5. 檢查產生的變更是否符合允許的範圍。
  6. 對壞掉的基準執行相同的迴歸探測。
  7. 如果探測無法區分壞掉和修復,則拒絕該證據。
  8. 在需要時執行乾淨重播。
  9. 產生審查摘要。
  10. 如果需要,匯出並驗證證明材料。
  11. 將結果交給人類審查者。

它刻意不是一個完全自主的核准系統。

最後一步仍然是審查。



一個小例子

假設錯誤出在一個 slug 函數。

壞掉的實作是:

def slugify(text):
    return text.strip().lower().replace(" ", "")

Enter fullscreen mode

Exit fullscreen mode

對於這個輸入:

Hello World

Enter fullscreen mode

Exit fullscreen mode

壞掉的結果是:

helloworld

Enter fullscreen mode

Exit fullscreen mode

要求的結果是:

hello-world

Enter fullscreen mode

Exit fullscreen mode

修復可能是:

def slugify(text):
    return "-".join(text.lower().split())

Enter fullscreen mode

Exit fullscreen mode

而迴歸探測是:

def test_slugify():
    assert slugify("Hello World") == "hello-world"
    assert slugify("Two Words") == "two-words"

Enter fullscreen mode

Exit fullscreen mode

現在有用的部分不僅是這個測試在修復後通過。

更強的證據是:

Broken baseline: FAIL
Fixed result:    PASS

Enter fullscreen mode

Exit fullscreen mode

這告訴審查者,探測實際上偵測到了正在被修正的行為。



我為什麼建構它

主要原因是,我想要更多而不是更少地使用程式碼代理。

但如果我打算委派更多實作工作給它們,我想要在「代理說它完成了」和「有足夠證據讓我認真審查結果」之間有更好的界線。

這就是這個專案試圖填補的差距。

它仍然是第一個版本,我預期隨著我在更多專案上使用它並發現更多邊緣案例,工作流程會繼續演進。

目前,焦點很窄:有邊界的變更、更強的迴歸證據、重現性,以及人類審查。

如果這聽起來對你使用程式碼代理的方式有幫助,我已在這裡釋出第一個版本:

https://sovereign-system.itch.io/ai-coding-agent-reliability-kit

https://dev.to/rabeh_arch/why-green-tests-are-not-enough-for-ai-coding-agents-app

https://www.worldprogramming.org/posts/why-green-tests-are-not-enough-for-ai-coding-agents-h3x7u8