研究

代理群與新模型經濟學

Wilson Lin閱讀時間 5 分鐘

今年稍早,我們進行了一系列實驗,測試將 Agent 擴展為能朝共同目標協作時的極限。我們的假設是,這將開啟任務規模與複雜度的新層級。

其中的旗艦專案是一個長時間運行的代理群,從零打造一個瀏覽器。它作為概念驗證是成功的,但距離成熟完善的軟體仍有很大差距。

那項工作刻意採取實證導向。我們從一張空白畫布開始,一路摸索,朝穩定且有效的系統邁進。從那之後,我們的目標一直是充分了解代理群,直到能夠有意識地將它工程化。

為了驗證這項進展,我們回到一個舊版代理群曾經難以完成的任務:只根據 SQLite 的文件,從零開始用 Rust 打造它。

我們的初步結果相當令人振奮。我們讓舊版與新版代理群在相同任務上運行,使用相同的模型與相同的時間預算,並衡量它們各自能通過多少保留的 SQL 測試套件。

新版代理群在每一種模型組態中都表現更好。使用 Grok 4.5 時,它在四小時內達到 80%,而舊版代理群則逐漸失控,還不到第二小時就不得不暫停。

我們也調整了由哪些模型負責哪些工作。在某些運行中,由單一模型包辦所有工作;而在另一些運行中,則由尖端模型負責規劃,再由快速且低成本的模型執行。每一種搭配都產生了相近的品質,但成本差異極大。1

在舊版與新版代理群下,依模型組合重建 SQLite 的成本在舊版與新版代理群下,依模型組合重建 SQLite 的成本

樹與葉

大型任務的說明自然會呈現樹狀結構:樹根是目標,並以遞迴方式逐層細分成基本工作單元。我們的 代理群 有兩種角色,兩者都依循相同的樹狀拆解方式來組織:

  • 由最強大的模型驅動的規劃代理,會將目標拆分成多個部分並委派出去。
  • 通常由更快、成本更低的模型驅動的工作代理,則負責執行這些部分。

這種設計涵蓋了比僵化編排系統更廣的範圍。代理群 不會替問題強加固定的拓撲結構,而是會沿著問題的輪廓自然擴展,並讓運算資源與上下文隨任務的複雜度成比例擴張。

我們認為,這就是為什麼這種設計能泛化到像是打造瀏覽器解數學題最佳化 GPU kernel這類差異極大的任務。我們也已在內部用它來找出並修正開源軟體中的漏洞、提升我們自己程式碼庫的測試覆蓋率,以及產生數十億個 token 的合成訓練資料。

tree 對記憶的作用

當單一代理承擔一項完整任務時,它必須自行走完整棵 tree,一路下探到每個葉節點,同時始終在上下文中保留祖先節點、目前所在位置,以及更宏觀的整體目標。

我們認為,這說明了為什麼長時間運行的單一代理會逐漸偏離。它們不是專注於眼前的工作而失去對全局的掌握,就是顧著維持全局視角,卻把局部做得比較差。

在 代理群 中,規劃者永遠不負責實作,因此它的上下文不會被底層細節塞滿;而執行者永遠不負責規劃,因此它可以把全部上下文都用在一小塊明確的工作上。

任務 tree 中規劃代理與工作代理之間工作拆解的示意圖任務 tree 中規劃代理與工作代理之間工作拆解的示意圖

我們推測,代理 代理群 之所以能擴展,更多是來自這種上下文效率,而不只是並行能力本身。這種效率在 代理群 的任何規模下都存在,這也就是為什麼這種拆解方式即使面對中等規模的任務,也能提升代理的效能。

這種結構在其他地方也能看到相似的影子。經濟學家 Ronald Coase 在追問企業為何存在時,主張,協調成本的成長速度比工作本身更快,因此組織會自然形成層級分明、各自有邊界的單元,而不是讓每個人都直接和每個人溝通。

供代理使用的版本控制系統

先前一篇關於 代理群 的文章中,我們提到,Git 和 Cargo 這類工具仰賴粗粒度鎖來做並發控制。這對單一開發者來說沒有問題,但對數百個並發代理產生的工作規模而言就完全行不通了。

今年稍早的瀏覽器 代理群 在 Git 上的峰值大約是每小時 1,000 次提交。新系統的峰值則大約是每秒 1,000 次提交。

為了支撐這樣的速度,我們從零開始打造了一套新的版本控制系統 (VCS) 。吞吐量並不是我們自行掌控這一層的唯一原因。系統中的每一項變更都會經過 VCS,因此衝突也會最先在這裡浮現,而下一節中的幾種協調機制也直接實作於其中。

每秒 1,000 次提交下的失效模式

人類工程團隊有一套標準的協作機制,例如程式碼審查、權責歸屬、站立會議,以及合併佇列。這些機制在人類的工作節奏下運作良好,但在群體那樣的提交速率下,我們就會看到人類團隊平時很少遇到的失效模式。

腦裂設計

兩個彼此不知道對方存在的規劃者,會在程式碼庫的不同部分,以不同方式實作同一個概念。

我們透過調整提示詞來修正這個問題。規劃者會自行做出設計決策,而不是將這些決策委派出去;而且我們要求他們確保,不會有兩個被委派的子樹對同一個問題做出決定。

規劃者之間的衝突

另一種更棘手的衝突,是兩個規劃者彼此都知道對方,卻仍在同一批檔案上來回修改、互相拉扯。

問題在於,雙方對現實有兩套不同的理解,而合併工具也無法修正這種分歧。相反地,我們會讓代理將決策記錄在共享的設計文件中。依賴某項決策的程式碼,會附上一個可經編譯檢查的參考,回指對應文件。當規劃者在不知情的情況下彼此矛盾時,協調器會合併這些文件,而這些參考也會將解決結果向下游傳遞。

合併衝突

在這個代理群中,代理經常會在同一個檔案上互相衝突。要化解這類衝突,它們就得停下手邊工作、吸收另一個代理的上下文,並在此基礎上完成合併。工作代理並不擅長處理這種情況,實際上往往不是覆寫對方的變更,就是放棄自己的變更。

為了修正這個問題,我們建立了一套系統,讓中立的第三方代理在發生合併衝突時介入,並代替各方完成衝突解決。它唯一的目標就是保持公正並提升效率,類似工程團隊中合併佇列的運作方式。

超大型檔案

有些檔案特別容易成為代理集中工作的地方。每個代理可能只會加入少量程式碼,而且沒有任何單一代理負責讓這些檔案維持精簡。

這些「超大型檔案」會拖垮一切。它們在傳輸、比較差異和合併時成本高昂,也會成為衝突不斷發生的地方。

為了修正這個問題,我們讓工作代理能夠標記臃腫的檔案。一旦被標記,我們就會封鎖新的提交,並由外部代理將過度膨脹的檔案拆分成較小的模組。

僵化

代理在現有程式碼庫中與人類協作的過程中,已經學會了:即使核心程式碼需要變更,也不要去碰它。

為了解決這個問題,我們允許有意為之的破壞性變更。若某個代理判斷核心變更值得進行,它可以在自身範圍之外提交一個針對性的修補,並留下留言說明為什麼要這麼做。

編譯器會將這項變更傳導到系統的其他部分,而所有依賴舊設計的內容都會建置失敗。每個遇到這些錯誤的代理都會找到那則留言、讀取其中的理由,並更新自己負責的部分以與之對齊。

審查視角

在一個同時具備長時間執行與多代理特性的系統中,錯誤會不斷累積,因此代理群需要一種能在小錯誤演變成根本性問題之前自我修正的機制。

我們嘗試了許多不同類型的審查視角,例如讓審查代理查看執行者 的完整對話記錄、只看其輸出,或除了程式碼庫之外什麼都不提供。我們也試過讓審查者在不同模型上運作,並具備不同的訓練與不同的個性。

沒有任何單一視角能捕捉一切,但彼此去相關的視角可以層層疊加;就像自駕系統並不依賴某一個完美元件,卻仍能達到超越人類的可靠性。投入在審查上的運算資源有很高的回報,因為審查的成本遠低於它所稽核的工作。我們推測,這套堆疊式審查系統,是這些執行結果得以長期維持高品質的重要因素。

讓代理塑造環境

Stigmergy 是像螞蟻和白蟻這類群居生物在沒有直接溝通的情況下進行協調的機制。牠們塑造環境,而環境又會影響下一個個體。

我們在先前的執行中加入了像「保留筆記」和「記錄決策」這樣的規則,因為這些做法顯然是好的。事後回頭看,這其實是在讓代理為未來的自己和隊友把知識沉澱下來。

我們把這個想法再往前推進,做了一個由代理自行撰寫、共享上下文的實驗,稱為 Field Guide。這是一個完全由代理管理的資料夾,其中的 index.md 會在每個代理啟動時自動注入。代理的工作是整理要放進這份指南的內容,而它們唯一的限制就是行數預算。

這份指南背後的邏輯是:模型權重是凍結的,因此真正值得記錄的,正是那些出乎意料的情況,這樣下一個代理的軌跡就能更短。

Field Guide 是一項仍處於早期階段、但結果很有潛力的實驗。我們預期,在那些代理無法完全掌握的程式碼庫中,這些好處會更加明顯。訓練模型為後繼者寫作,讓更好的資訊沉澱帶來更好的回饋,會是一個有趣的後續研究方向。

SQLite 實驗

我們指示配備了上述所有改進的新版本 代理群,用 Rust 實作整本 835 頁的 SQLite 手冊內容。我們不提供原始碼、測試套件、SQLite 二進位檔,也不提供網際網路存取。

為了衡量進展,我們以 sqllogictest 作為評分依據。這是 SQLite 專案建立的測試套件,用來檢查不同資料庫引擎對相同查詢是否會回傳相同結果。它包含數百萬筆已有正確答案的查詢,而分數就是 代理群 的資料庫答對的比例。進展會在每次執行期間呈現為一條持續上升的曲線。

系統從未被告知這個套件的存在。每次執行後,我們都會人工審查程式碼與整個執行過程,檢查是否有作弊或走捷徑,並確認系統的建構是平均展開的,而不只是集中在測試會檢查的部分。

閱讀這些曲線時,請記住,代理會自行選擇策略。有些會先打下廣泛的基礎,前幾個小時分數偏低,之後才在後期陡升;另一些則會先深入單一領域,很早就拿到分數,接著在補齊其餘部分時趨於平緩。趨勢比特定時刻的精確分數更重要。

各模型組合的結果

我們測試了四種涵蓋能力與成本的組態:

  1. GPT-5.5 同時擔任規劃者與執行者。 全程採用強大的尖端模型。2
  2. Grok 4.5 同時擔任規劃者與執行者。 這是我們具成本效益的尖端模型,作為比較基準。
  3. Opus 4.8 擔任規劃者,Composer 2.5 擔任執行者。 以尖端判斷搭配高效率執行。
  4. Fable 5 擔任規劃者,Composer 2.5 擔任執行者。 用來觀察較低一級的規劃者,是否會讓這種混合組合更值得採用或更不值得採用。

新的測試框架在每一種組合中都優於舊版。

Fable 5 的混合組合在第一小時內就通過了約三分之二的測試套件。到了四小時的截止點,新版各次執行的結果介於 73% 到 85% 之間,而舊版各次執行則介於 11% 到 77% 之間。

舊版 Grok 4.5 的那次執行在達到兩小時前就被暫停了 (下文詳述) 。所有新的組態最終都通過了 100% 的測試套件。

未來我們希望執行完整的規劃者-執行者 N×N 組合矩陣測試。在這一輪中,真正重要的是測試框架版本之間的比較,而最終呈現出的行為表現差異,也比分數差異所顯示的要大得多。

舊版與新版代理群下 GPT-5.5 的 SQLite 測試套件分數隨時間變化舊版與新版代理群下 GPT-5.5 的 SQLite 測試套件分數隨時間變化
舊版與新版代理群下 Grok 4.5 的 SQLite 測試套件分數隨時間變化舊版與新版代理群下 Grok 4.5 的 SQLite 測試套件分數隨時間變化
Opus 4.8 規劃者搭配 Composer 2.5 執行者的 SQLite 測試套件分數隨時間變化Opus 4.8 規劃者搭配 Composer 2.5 執行者的 SQLite 測試套件分數隨時間變化
Fable 5 規劃者搭配 Composer 2.5 執行者的 SQLite 測試套件分數隨時間變化Fable 5 規劃者搭配 Composer 2.5 執行者的 SQLite 測試套件分數隨時間變化

深入剖析這些執行過程

先從最基本的活動指標來看,我們可以看到 Grok 4.5 在舊測試框架與新測試框架下的提交速率有何差異。舊版執行在前兩小時內產生了 68,000 次提交,速度大約是新版執行的 70 倍。

一種解讀是,它的產出更高;另一種解讀則是,這些提交大多只是無效忙碌 (反覆折騰、相互爭用、來回變動) 。

Grok 4.5 在活躍分鐘數上的累計提交數,舊測試框架與新測試框架對比Grok 4.5 在活躍分鐘數上的累計提交數,舊測試框架與新測試框架對比

合併衝突的資料更傾向支持後者。舊版執行在我們暫停前累積了超過 70,000 次衝突,而且不是逐漸穩定下來,反而持續加速;相比之下,新版執行在完整四小時內記錄到的衝突不到一千次。

Grok 4.5 隨時間變化的累計合併衝突數,舊測試框架與新測試框架對比Grok 4.5 隨時間變化的累計合併衝突數,舊測試框架與新測試框架對比

衝突主要集中在那些變得最大的檔案上。在舊版執行中,最大的檔案在整個過程中持續膨脹,而衝突最嚴重的單一檔案累積了 7,771 次衝突,曾被 1,173 個不同的 Agent 觸及。到了新版執行,整個程式碼庫中爭用最激烈的檔案只出現了 47 次衝突。

Grok 4.5 在執行進度中的最熱點檔案大小(以程式碼行數計),舊測試框架與新測試框架對比Grok 4.5 在執行進度中的最熱點檔案大小(以程式碼行數計),舊測試框架與新測試框架對比

舊版代理群最大的協調失誤——腦裂,也就是規劃者彼此重複工作——反映在套件結構上。Rust 程式碼是以稱為 crate 的套件來組織;而在這類專案中,每個 crate 大致就對應一個主要元件。

舊版執行一路擴張到 54 個 crate,其中包含三個彼此獨立的 SQL 套件。新版執行則很早就收斂到九個 crate,之後再也沒有新增。

Grok 4.5 SQLite 執行中 Rust crate 數量隨時間變化,舊測試框架與新測試框架對比Grok 4.5 SQLite 執行中 Rust crate 數量隨時間變化,舊測試框架與新測試框架對比

這一切最終都反映在最後的程式碼庫上。在 Fable 5 組合中,舊版與新版代理群最終都通過了完整測試套件,但舊版需要 64,305 行引擎程式碼,新版則只用 9,908 行就完成。Opus 組合也呈現相同趨勢:在舊測試框架下用了 19,013 行程式碼、得分 97%;在新測試框架下則用了 4,645 行、得分 100%。

完成 SQLite experiment 所需的引擎程式碼行數,舊測試框架與新測試框架對比完成 SQLite experiment 所需的引擎程式碼行數,舊測試框架與新測試框架對比

模型經濟性

我們在前文提到過,每種 模型組合 產生的品質都差不多,但成本差異非常大,從 Opus 4.8 混合組合 的 10,565 不等。token 數據顯示了這種差異的來源。

每次 run 的支出結構都很一致,執行者 至少占了 69% 的 tokens,而大多數情況下都超過 90%。

但美元成本的分布與 tokens 並不相同,因為 規劃者 tokens 的成本更高。在 Opus 4.8 與 Composer 2.5 的組合中,擔任 規劃者 的 Opus 只產生了少量 tokens,卻約占總成本的三分之二;擔任 執行者 的 Composer 則處理了絕大多數 tokens,而成本只占剩下的三分之一。

SQLite 代理群組態中,依模型角色區分的 token 用量:規劃者與執行者SQLite 代理群組態中,依模型角色區分的 token 用量:規劃者與執行者

在大型任務中,真正需要尖端智慧的時刻其實不多,例如最初的拆解、設計決策,以及某些權衡取捨。一旦尖端 規劃者 把模糊之處收斂成詳細且明確的指示,較便宜的模型只要照著執行即可。這是節省成本的一個巨大潛在來源。在同時使用 GPT-5.5 作為 規劃者 和 執行者 的 run 中,光是 執行者 的成本就高達 411。

另一個值得注意的細節,來自對兩次 混合組合 run 的比較。Fable 5 規劃者 的帳單略低於 Opus 4.8 規劃者,儘管它的單一 token 價格大約高出一倍,因為它使用的 planning tokens 少得多。但 Fable run 的 執行者 消耗了多出好幾倍的 tokens,因此整體 run 的成本明顯更高。

把規格當作提示詞

AI (人工智慧) 能力的每一次躍升,都提高了工程師能工作的抽象層級。

自動完成功能讓工程師一次處理一行程式碼。早期模型把這提升到一個程式碼區塊,而 Agent 則把它提升到一個檔案或一項功能。

有了 代理群,工作單位就變成了規格。

要做到這點,代理群 就必須真的遵循規格,而這正是本文大部分內容在談的事。我們給了 代理群 835 頁的文字說明,它最後交回來的是一個資料庫。在這次實驗中稀缺的東西,以及我們預期未來在軟體工程中會持續稀缺的東西,是對意圖恰如其分的描述。

從這樣看來,代理群 開始有點像編譯器。編譯器會透過一連串中間步驟,把原始碼翻譯成機器碼。代理群 對意圖做的事也很類似。規劃者會把目標解析成任務樹,然後再一步步細化成可執行的工作。差別在於,編譯器會在每一步都保留語意,而 代理群 的每一步都帶有機率性。本文所描述的一切,都是為了縮小這個落差。

我們邀請你探索 代理群 的成果。來自 solo Opus 4.8 run 的程式碼庫已公開於 github.com/cursor/minisqlite。根據我們的初步瀏覽,它看起來很不錯,但我們尚未做更深入的人工分析。也歡迎你親自看看,並告訴我們你的發現。


  1. 為了了解單獨使用尖端模型的成本,我們也分別執行了 Opus 4.8 和 Fable 5。我們對這些執行結果只做了非正式評估,因此不在此對其品質下任何結論,不過根據經驗,我們預期這兩個模型都會有不錯的表現。它們的成本在圖表中以斜線陰影長條顯示。
  2. 我們原本希望使用 GPT-5.6 Sol 作為尖端組態。這個新模型似乎比我們測試的其他模型更容易受字面表述和強調語氣影響,而且我們遇到了其他模型都未曾出現的失控螺旋狀況。對於這麼近期才推出的模型,我們沒有足夠時間調整提示詞;而若只為單一模型調整、其餘維持不變,又會使比較失準,因此我們改用 GPT-5.5。

分類於: 研究

作者: Wilson Lin