系列: 與 AI 一起學寫程式

Gemini 審查了 Claude 的 PR。三十六則留言之後,程式碼變得更好了。

多供應商審查在上線前抓到兩個執行緒安全臭蟲。

不同的模型,不同的盲點 橫跨八個子系統的跨供應商審查 Claude 撰寫 Gemini 審查 36 則留言 抓到 2 個真正的臭蟲 執行緒安全問題,差點就上線 由人類決定 — 不是 Gemini,也不是 Claude
Claude 撰寫。Gemini 審查。人類合併。臭蟲落敗。

一個總是同意作者的審查者不是審查者。讓你的 AI 寫的程式碼過一遍對手 AI 的審查,盲點就會亮起來——包括那些差點就部署到正式環境的執行緒安全臭蟲。

我一再回頭採用的模式,就是由 Claude 寫程式碼、由 Gemini 審查。不同的訓練。不同的盲點。對於什麼是站得住腳的模式、什麼是壞味道,有不同的看法。這個週六,是我認為這個模式正確的最具體例證。

前一週,一批第二階段端點擴充的 PR 已陸續落在 API 表面上。八個獨立子系統各自擴充了公開介面。實作大多由 Claude 撰寫。在合併任何一個之前,我先讓它們全部過一遍 Gemini 的審查。Gemini 針對整批回覆了三十六則具體留言。

我逐一處理了每一則。這篇文章要談的是 Gemini 抓到了什麼,以及這些發現屬於那種類型本身意味著什麼。

八個子系統是什麼

這些 PR 涵蓋了需要第二階段擴充的八個 API 路由模組:動畫、網路、音訊、AI 感知、場景建構實體幾何(CSG)、輸入動作與遊戲手把繫結、XR 錨點姿態型別,以及腳本 Lua VM 生命週期。每個 PR 新增了十幾到四十個端點,附完整的請求/回應結構描述、輔助函式與測試。

這些 PR 一點也不低調。每一個都是公開介面的大幅擴充。合起來相當於數週的設計工作,由代理在大約兩個週末內完成實作。

Gemini 抓到了什麼

我想講得具體一點,因為類別很重要。

動畫與網路:混合樹參照與輔助函式。Gemini 注意到動畫 API 中的混合樹輔助函式與網路 API 中的拓撲輔助函式,在處理缺失參照時採用了微妙不同的慣例。動畫回傳等同 None 的值,讓呼叫端自行決定。網路則拋出例外。兩種都是有效的模式。但它們在相隔一天內落地的兩個 PR 之間並不一致。解法是把它們對齊;我們選了明確拋例外的路線,因為它會在 API 邊界就暴露缺失的參照,而不是讓它以沉默的 null 一路傳播。

音訊:回應模型的預設值與輔助函式。Gemini 發現數個音訊回應模型對選填欄位的預設值不一致。有的預設為空字串,有的是 None,有的是明確的 null。這種不一致會在客戶端繫結層產生令人困惑的行為(不同語言對每種選項的序列化方式各不相同)。解法是選定單一慣例(Python 型別中用 None,線路上用 null)並一致地套用。

AI 感知:控制代碼對映與繫結。Gemini 對感知子系統的控制代碼對映(handle map)提出了執行緒安全疑慮:該對映在背景執行緒被修改的同時,也在 API 請求執行緒被讀取,卻沒有鎖。在高負載下,這會產生間歇性的對映損毀臭蟲,非常難以診斷。解法是加上讀寫鎖,並針對常見情況最佳化讀取路徑(查詢次數遠多於插入)。

場景:控制代碼對映與 CSG 回應。與 AI 感知的發現屬於同一族的臭蟲。Gemini 在場景子系統的 CSG 控制代碼對映中抓到了同樣的執行緒安全疑慮。解法形態相同:讀寫鎖。這正是那種一雙受過訓練的眼睛會到處標記的臭蟲,因為它見過這個模式;Claude 在兩個地方寫了同樣的模式而沒有察覺。

輸入:動作與遊戲手把繫結。Gemini 發現動作繫結 API 對無效動作 ID 使用魔術數字慣例(-1),而遊戲手把繫結 API 卻使用哨兵結構值。這種不一致會讓同時使用兩個 API 的開發者在不小心用錯無效標記時產生微妙的臭蟲。解法是在兩個 API 中引入型別化的 ActionId::Invalid 常數,並把所有魔術數字遷移過去。

XR:錨點姿態型別與處理器重用。Gemini 抓到 XR API 在不同端點暴露了兩種微妙不同的姿態型別:一種是世界座標,一種是錨點區域座標。這個差異真實存在且對使用者很重要,但端點並未清楚記載這個差異。Gemini 建議把型別拆開,讓型別系統強制這個區別。解法是引入 WorldPoseAnchorPose 兩個互不隱式轉換的獨立型別。

腳本:Lua VM 生命週期與洩漏。Gemini 發現 Lua VM 是逐請求配置的,卻沒有明確的拆卸路徑。在持續負載下,這會讓 VM 狀態洩漏到位址空間,直到 API 伺服器倒下。解法是引入以伺服器執行緒為單位的 VM 池,帶有明確的取得與釋放語意,並加入一條無論成功或失敗都在請求結束時執行的拆卸路徑。

我對這批發現作為一個類別的觀察

三點觀察。

大多數發現是一致性問題。Gemini 三分之二的留言是「這個慣例和你剛落地的另一個子系統採用的慣例不同」。這正是單一模型不擅長抓的問題,因為每個 PR 是孤立落地的,寫它的模型並沒有把其他 PR 放在上下文裡。而一個橫跨整批工作的審查者,看得到作者腦中沒有的那些不一致。

少數發現是真正的臭蟲。控制代碼對映上的執行緒安全發現是真臭蟲。它們本來會上線。它們會是間歇性的、難以診斷的。Gemini 抓到了整批中的兩個實例(一個在 AI 感知、一個在場景 CSG),因為它具備「共享可變對映沒有鎖=執行緒安全疑慮」的模式辨識能力。不同的模型、不同的訓練、被校準去標記的東西也不同。

少數是風格問題,並引發了討論。不是每一則 Gemini 留言都對。有幾則是風格偏好,我要嘛反駁了,要嘛交由人類判斷。有些留言被否決這件事並不削弱這個模式;反而強化了它。一個總是同意作者的審查者不是審查者。

這個模式能做什麼、不能做什麼

它能做的:抓到單一模型審查會漏掉的一類臭蟲。特別是跨 PR 的一致性臭蟲,以及那種「用不同資料訓練出來的審查者標記了作者的訓練看不到的東西」的模式比對式發現。

它不能做的:取代人類審查。Gemini 的留言是第一輪。每一則我都讀過。有些我否決了。大多數我接受了。最終的合併決定是我做的。這個模式是「Claude 撰寫,Gemini 審查,人類決定」。不是「Gemini 決定」。

它對 AI 實驗室的啟示:該最佳化的指標不是「模型自己的程式碼能否通過自己的審查」,而是「模型的程式碼能否通過另一家供應商模型的審查」。在跨供應商指標上表現好的代理,才是我在正經工作上信任的代理。

合作夥伴與開發者該從中學到什麼

如果你在運行代理驅動的工作流程,而且還沒有讓 PR 審查過一遍另一家供應商的模型,下一批試試看。設置成本很小。抓蟲率不容小覷。今天這一批就抓到了兩個差點上線的真正執行緒安全臭蟲。

如果你是 AI 實驗室,還沒有把「以另一家供應商的模型做 PR 審查」當成程式設計代理的最佳化目標,請考慮一下。這個指標是誠實的。訊號是真實的。在這上面得分高的代理,才是認真的團隊會採用的。

如果你正在評估一個引擎作為合作對象,多供應商審查模式是我會詢問的紀律訊號之一。一個對每個有意義的 PR 都跑跨供應商審查的團隊,和不這麼做的團隊是兩種團隊。程式庫會反映出這個差異。

三十六則留言、八個子系統、一個週六。這一批比今天早上更好了。這個模式再次證明了自己的價值。

回去繼續打造。

AI 實驗室用來審查——並在其上打造的執行環境

RakuAI 是 LLM 打造者對準出貨的空間執行環境。跨供應商審查、誠實的指標、合作夥伴級的紀律。看看你的模型在真實世界中的位置。

← 所有文章