一個有效的確定性 AI 配對程式設計流程
AI 程式設計師雖然強大,但若無閘門控管,便會飄移不定。以下是包裝它的「規劃、建置、驗證、交付」管線,具備狀態檔案與計畫凍結功能。
將一個編碼任務交給 AI 助理,並附上「全部搞定」的指令,會以一種可預測的方式失敗。模型是有能力的,在它面前的狹窄領域中,其能力往往超出你的預期,但它不知道工作的終點在哪裡。它會在步驟之間失去上下文。它會偏離最初的目標,去修復它注意到的某些東西。它會寫出看起來正確的程式碼,然後在編寫和部署之間不做任何測試就直接交付。當出現問題時,你無法判斷是過去四十次編輯中的哪一次造成的,也無法乾淨地復原。
解決方案不是一個更好的提示,而是一個約束框架。你將 AI 的工作包裹在一個由人類設計和控制的確定性管線中:審查、實作、驗證、文件化、部署,每個階段都有一個明確的閘門,在失敗時會停止執行。模型在每個階段內部提供創造力。管線則在其周圍提供紀律。這篇文章描述了如何建構那個約束框架,為什麼每個階段都需要一個閘門,以及在何種特定情況下,完全跳過這一切才是正確的選擇。
為何無引導的 AI 會偏離
這些失敗模式相當一致,足以為其命名。理解這些模式,就能讓你明白該建立何種關卡。
首先是情境遺失。AI 助理在一個有限的近期對話視窗內運作。在一個長任務中,該視窗會被填滿,而最早的決策會被擠出視窗之外。當模型深入程式碼時,它已不再記得一開始同意的狹窄範圍。當它與先前的決策相矛盾時,它並不是在對你說謊。它是真的再也看不見那個決策了。
其次是範圍蔓延。要求修復一個錯誤,模型通常會注意到三個可以改進的相鄰事物,並加以改進,因為每一個改進在局部看來都很合理。每一個單獨的變更都是站得住腳的。結果是,你得到一個比你要求的大四倍的差異比對(diff),動到了你從未打算開啟的檔案,而真正的修復則被埋藏在其中。
第三是缺乏驗證,這點傷害最大。若置之不理,AI 會撰寫程式碼,並根據程式碼「看起來」正確,而非「執行起來」正確來回報成功。「看起來正確」和「實際上正確」的差異,恰好就是那些有趣的錯誤所在之處:差一錯誤(off-by-one)、錯誤的篩選器、一個在本地成立但在部署環境中失效的假設。若沒有一個能實際執行程式碼的關卡,模型的信心便與現實脫節。
可逆性不佳將前述幾點串連起來。當工作成果以一個龐大且未分化的變更形式呈現,且沒有記錄下決策內容或原因時,要回復(rolling back)就意味著要解開一個死結。你無法還原一個你從未寫下的決策。
這些都不是不信任模型原始能力的理由。它們是我們不該信任非結構化授權的理由。這股力量是真實的。指引和關卡,才能將其轉化為你能夠引以為傲、已交付的工作成果。
管線的五個階段
這個框架是一連串固定的階段。其順序並非僅為裝飾。每個階段都會產生下個階段所需的輸入,且每個階段都以一個閘門作結,必須通過該閘門才能繼續執行。
| 階段 | AI 的工作 | 閘門的檢查項目 | 失敗時的處理 |
|---|---|---|---|
| 審查 (Review) | 讀取上下文、撰寫計畫、指出風險 | 計畫具體、範圍有界、人類核准 | 停止,修訂計畫 |
| 實作 (Implement) | 僅根據已定案的計畫撰寫程式碼 | 程式碼可編譯、類型檢查通過 | 停止,修復或復原 |
| 驗證 (Verify) | 執行測試、檢查不變量 | 測試通過、不變量成立 | 停止,不繼續部署 |
| 文件記錄 (Document) | 記錄變更內容與原因 | 變更日誌與實際差異 (diff) 相符 | 停止,進行核對 |
| 部署 (Deploy) | 透過正常發布路徑交付 | 部署後的健康檢查通過 | 復原 |
「審查」是進行思考的環節,也是發現錯誤成本最低的地方。模型會讀取相關的程式碼,寫下它打算做什麼,並列出可能出錯的地方。人類會閱讀該計畫,並予以核准或退回。核准一份計畫遠比審查一份完成的差異 (diff) 來得便宜,因為計畫簡短而差異冗長,且在此處發現錯誤的計畫,其成本僅是幾分鐘,而非一次復原。
「實作」將核准的計畫轉化為程式碼,且僅限於已核准的計畫。「驗證」是將此管線與無引導的委派區分開來的閘門:它會執行程式碼,並在測試失敗時拒絕推進。「文件記錄」留下永久的紀錄,以便下一個人或下一個 AI 會話能夠重構其推理過程。「部署」會透過與人類相同的發布路徑進行交付,並進行相同的部署後檢查,在失敗時會進行復原,而不是留下半交付的狀態。
狀態儲存於檔案中,而非模型的記憶體內
AI 助理的工作記憶體是易失性的。關閉對話、超出上下文視窗,或遇到強制重啟的錯誤,模型所持有的所有內容都會消失。如果你的管線狀態只存在於該記憶體中,重置就意味著從頭開始,而在一個部署到一半的任務上從頭開始是危險的。
因此,管線會將其狀態寫入檔案。目前的階段、已做的決定、已完成的項目、剩餘的項目,所有這些都儲存在磁碟上,並隨著工作的進展而更新。模型在每一步開始時讀取此狀態,並在結束時寫入。易失性的上下文成為持久狀態的快取,而非事實的唯一來源。
pipeline-state:
task: "add rate limiting to the public API"
stage: verify # review -> implement -> verify -> document -> deploy
plan_frozen: true
decisions:
- "token bucket, 100 req/min per key, chosen over sliding window for simplicity"
- "limit stored in existing cache layer, no new dependency"
progress:
- [x] middleware written
- [x] unit tests for bucket refill
- [ ] integration test under concurrent load
failures:
- none
回報會在最糟的時刻顯現。一個對話在任務中途終止。新的對話開始時,沒有任何先前的上下文。它讀取狀態檔案,並確切地知道工作進度:計畫已凍結,實作已完成,還剩下一個驗證步驟。它從那裡繼續,而不是去猜測,或更糟的是,重做已完成的工作並撤銷某些正確的東西。將狀態儲存於檔案中,才能讓 AI 驅動的任務在其中斷後存活下來,而這類任務又特別容易發生中斷。
計畫凍結可阻止範圍蔓延
在審核階段產生核准的計畫後,管線會將其凍結。凍結意味著範圍被鎖定:在實作期間,AI 不得在計畫中新增或刪除項目。它會建構已同意的內容,不多也不少。
這直接針對範圍蔓延的失敗情況。若沒有凍結,模型注意到並修復相鄰事物的習慣會不受控制地運行,且 diff 會膨脹。有了凍結,同樣的本能會碰壁。如果模型在實作中途決定需要另一項變更,它不會直接進行變更。該變更必須作為計畫的明確修正案被記錄下來,而人類可以查看並核准或拒絕。
規則並非範圍永遠不能改變。實際工作會揭示真正的意外,而拒絕適應本身就是一種失敗。規則是範圍變更必須是可見且刻意的,而非無聲無息地累積。一個帶有簡短記錄修正案清單的凍結計畫是可稽核的。一個因意外而增長的未凍結計畫,只是一個無人選擇的大型 diff。
plan (frozen at review):
1. add token-bucket middleware
2. wire it into the public router
3. unit + integration tests
amendment (recorded during implement, approved):
4. add a config flag to disable the limit per environment
reason: staging load tests need it off
管線所觸及的每一件事物,都可以追溯到凍結的計畫或記錄的修正案。diff 中不會出現任何未經同意的內容。
停止規則可防止失敗惡化
AI 助理遇到錯誤時會重試。這通常是好事。但如果助理遇到相同的錯誤並嘗試相同的修復方法,它就會陷入迴圈,有時會持續很長時間,浪費精力,而且每次嘗試都可能讓情況變得更糟。管線需要有明確的規則來決定何時停止。
第一條規則是重複失敗的次數限制。如果同一步驟連續失敗兩次,管線就會停止,並將問題呈報給人類,而不是進行第三次嘗試。兩次相同的失敗意味著模型不理解問題,再多的自主嘗試也無法產生這種理解。它們只會空轉。在兩次失敗後停止,能將一個可能的無限迴圈轉變為一個有界限且有明確交接的迴圈。
第二條規則是為破壞性操作設立一個獨立的關卡。刪除資料庫、捨棄資料表、重寫歷史紀錄、force-pushing,任何難以或無法復原的操作,都不能像普通步驟一樣獲得相同的自動批准。它需要人類對該確切操作進行明確、具體的確認。其理由在於不對稱性:一個可逆的錯誤代價是回滾,而一個不可逆的錯誤可能讓你損失無法挽回的資料,因此其前的關卡必須更加嚴格。
on step failure:
record failure in state
if failures_of_this_step >= 2:
HALT and surface to human # no third attempt
else:
retry with adjusted approach
before any destructive action:
require explicit human approval for THIS specific action
# never covered by a blanket "yes, proceed"
這些規則共同限制了爆炸半徑。AI 可以在安全、可逆的工作中間環節自主行動,而管線會在自主性最危險的兩個時間點強制人類介入:當它卡住時,以及當它即將執行無法撤銷的操作時。
為何包裝器必須是確定性的
此流程核心的 AI 本質上是非確定性的。給它相同的請求兩次,你可能會得到兩種不同的答案、兩種不同的實作、兩種相同步驟但順序不同的排列。這種變異性並非缺陷。它正是使模型從一開始就變得有用的創造力的另一面。你會希望它探索解決方案空間。
圍繞它的流程則必須相反。各個階段以固定的順序運行。閘門每次都應用相同的檢查。狀態以相同的方式記錄在相同的位置。給定相同的狀態檔案,流程會從相同的點恢復並執行相同的事情。儘管其核心存在隨機性,但這正是使整個安排值得信賴的原因。
包裝器的確定性帶來了三項具體好處。可重現性:你可以重新運行流程,並讓流程的行為保持一致,即使模型在某個階段內的輸出有所不同。可稽核性:因為每個決策和每個階段轉換都被記錄下來,你可以在事後精確地重建發生了什麼以及為什麼發生。信任:你可以讓模型在一個階段內以真正的自主性行動,正是因為有一個可預測的閘門守在出口,在其工作成果被採納前進行檢查。
這種分工就是整個概念的核心。AI 掌握著創造力,也就是從變異和探索中受益的部分。流程則掌握著紀律,也就是絕不能改變的部分。兩者都不做對方的工作。一個有創造力的流程將會是一片混亂。一個有紀律的 AI 則不值得使用。
何時該跳過管線,直接提問
這套框架並非免費。它有實際的開銷:編寫計畫、維護狀態檔案、通過每個關卡、記錄文件。對於適合的任務,這些開銷是划算的。對於不適合的任務,它就成了扼殺一個兩分鐘工作的官僚程序。
當任務小、一次性且易於還原時,請跳過管線。在檔案中重新命名變數。草擬一個你會讀過即丟的拋棄式腳本。回答關於某段程式碼如何運作的問題。重新格式化一個區塊。對於任何這些情況,啟動一個包含凍結計畫和狀態檔案的五階段管線,純粹是浪費。直接問模型並閱讀結果即可。犯錯的成本不過是看一眼和一次復原。
當工作是可重複、有重大影響且難以還原時,請使用完整的管線,尤其是最終需要部署時。任何要上線到生產環境的東西。任何會動到資料庫結構或遷移資料的東西。任何隊友會在其基礎上繼續開發的東西。任何你日後需要解釋或稽核的東西。在這裡,開銷根本不是開銷。它是最便宜的保險,用以防範無引導的委派所引來的那些確切的失敗、情境遺失、範疇擴張、未經驗證的部署、不可逆的變更。
決定性的問題很簡單。一個錯誤的代價是什麼?如果答案是看一眼和一次復原,那就跳過框架,讓模型自由發揮。如果答案是生產環境事故、資料遺失,或是一個下午都在對無法追蹤的差異進行鑑識考古,那就把它包起來。讓流程的份量與後果的份量相匹配,並在風險允許的範圍內,讓 AI 盡可能地快速和寬鬆。