在過去的五個月裡,我們的團隊進行了一項實驗:建構並發布一個內部測試版的軟體產品,其中不包含一行手寫程式碼。
該產品有內部日常使用者和外部的 Alpha 測試者。它會發布、部署、出錯並修復。不同之處在於,每一行程式碼——應用程式邏輯、測試、CI 設定、文件、可觀察性以及內部工具——都是由 Codex 編寫的。我們估計,與手動編寫程式碼相比,我們以大約十分之一的時間完成了這項工作。
我們特意選擇這個限制,以便能夠以數個數量級的速度提升工程效率。我們只有幾週的時間來完成最終達到一百萬行程式碼的專案。為此,我們需要理解當軟體工程團隊的主要職責不再是編寫程式碼,而是設計環境、明確意圖,並建立回饋迴路以讓 Codex 代理人能夠可靠地工作時,會發生什麼變化。
這篇文章將探討我們透過一個全新的產品和一個代理人團隊建構過程中學到的東西——哪些地方出了問題,哪些地方產生了複利效應,以及如何最大化我們唯一真正稀缺的資源:人類的時間和注意力。
首次提交到一個空的儲存庫是在 2025 年 8 月下旬。
初始框架——儲存庫結構、CI 設定、格式規則、套件管理器設定和應用程式框架——是由 Codex CLI 使用 GPT-5 生成的,並由一組小型現有範本指導。甚至連指導代理人如何在儲存庫中工作的初始 AGENTS.md 文件本身也是由 Codex 編寫的。
沒有預先存在的人類編寫程式碼來錨定系統。從一開始,儲存庫就是由代理人塑造的。
五個月後,儲存庫包含大約一百萬行程式碼,涵蓋應用程式邏輯、基礎設施、工具、文件和內部開發人員工具。在此期間,由僅三名工程師驅動 Codex 開啟並合併了約 1,500 個提取請求。這相當於每位工程師每天平均處理 3.5 個 PR,令人驚訝的是,隨著團隊成長到七名工程師,處理量反而增加了。重要的是,這並非為了產出而產出:該產品已被數百名內部使用者使用,包括日常的內部重度使用者。
在整個開發過程中,人類從未直接貢獻任何程式碼。這成為團隊的核心理念:不手寫程式碼。
缺乏實際動手編寫程式碼的人類引入了一種不同類型的工程工作,專注於系統、框架和槓桿。
早期進展比我們預期的要慢,不是因為 Codex 無法勝任,而是因為環境的規格不足。代理人缺乏取得進展所需的工具、抽象和內部結構來達成高層次目標。我們工程團隊的主要職責變成了賦能代理人執行有用的工作。
實際上,這意味著深度優先工作:將較大的目標分解為較小的構件(設計、程式碼、審查、測試等),提示代理人構建這些構件,並利用它們來解鎖更複雜的任務。當出現問題時,修復方法幾乎從不是「更努力」。因為讓 Codex 完成工作的唯一方法是,人類工程師總是介入任務並詢問:「缺少什麼功能,以及如何讓它對代理人來說既易於理解又可強制執行?」
人類幾乎完全透過提示與系統互動:工程師描述一個任務,運行代理人,並讓它開啟一個提取請求。為了完成一個 PR,我們指示 Codex 在本地審查其變更,請求額外的特定代理人審查(本地和雲端),回應任何人類或代理人給予的回饋,並在一個迴圈中迭代,直到所有代理人審查者都滿意為止(實際上這是一個 Ralph Wiggum Loop (opens in a new window) )。Codex 直接使用我們的標準開發工具(gh、本地腳本和儲存庫內嵌技能)來收集上下文,而無需人類複製貼上到 CLI。
人類可能會審查提取請求,但並非必需。隨著時間的推移,我們已將幾乎所有的審查工作轉移給代理人之間處理。
隨著程式碼產出的增加,我們的瓶頸變成了人類 QA 容量。由於固定的限制是人類的時間和注意力,我們努力透過讓應用程式 UI、日誌和應用程式指標本身對 Codex 可讀來增加代理人的功能。
例如,我們讓應用程式可以針對每個 git worktree 啟動,以便 Codex 可以啟動並驅動每個變更的一個實例。我們還將 Chrome DevTools Protocol 整合到代理人運行時,並創建了處理 DOM 快照、螢幕截圖和導航的技能。這使得 Codex 能夠直接重現錯誤、驗證修復並推理 UI 行為。
我們對可觀察性工具也做了同樣的事情。日誌、指標和追蹤透過本地可觀察性堆疊暴露給 Codex,該堆疊對於任何給定的 worktree 都是臨時的。Codex 在一個完全隔離的應用程式版本上工作——包括其日誌和指標,這些日誌和指標在該任務完成後會被銷毀。代理人可以使用 LogQL 查詢日誌,並使用 PromQL 查詢指標。有了這些上下文,像「確保服務啟動在 800 毫秒內完成」或「這四個關鍵使用者旅程中的任何跨度都不要超過兩秒」這樣的提示就變得可行。
我們經常看到單個 Codex 運行在單個任務上工作長達六個小時(通常在人類睡覺時)。
上下文管理是在讓代理人有效處理大型複雜任務時面臨的最大挑戰之一。我們學到的最早的教訓之一很簡單:給 Codex 一張地圖,而不是一本 1000 頁的使用手冊。
我們嘗試了「一個大的 AGENTS.md (opens in a new window) 」方法。它以可預見的方式失敗了:
因此,我們不再將 AGENTS.md 視為百科全書,而是將其視為目錄。
儲存庫的知識庫位於結構化的 docs/ 目錄中,該目錄被視為記錄系統。一個簡短的 AGENTS.md(約 100 行)被注入到上下文中,主要作為地圖,並指向其他地方更深入的真實來源。
儲存庫內知識儲存佈局。
設計文件被分類和索引,包括驗證狀態和一套定義代理人優先操作原則的核心信念。架構文件 (opens in a new window) 提供了網域和套件分層的頂層地圖。一份品質文件對每個產品網域和架構層進行評級,並追蹤隨時間推移的差距。
計劃被視為一級工件。臨時的輕量級計劃用於小型變更,而複雜的工作則被捕獲在執行計劃 (opens in a new window) 中,並包含檢查到儲存庫中的進度和決策日誌。活動計劃、已完成計劃和已知技術債務都經過版本控制並共置,允許代理人在不依賴外部上下文的情況下運行。
這實現了漸進式揭露:代理人從一個小型、穩定的入口點開始,並被告知下一步該查找何處,而不是一開始就被淹沒。
我們透過機械方式強制執行此規則。專用的 linter 和 CI 作業驗證知識庫是最新的、交叉鏈接的且結構正確。一個定期的「文件園丁」代理人會掃描過時或陳舊的文件,這些文件未能反映真實的程式碼行為,並開啟修復提取請求。
隨著程式碼庫的演變,Codex 的設計決策框架也需要隨之演變。
由於儲存庫完全由代理人生成,因此它首先針對 Codex 的可讀性進行了優化。就像團隊旨在提高新工程師的程式碼導航能力一樣,我們人類工程師的目標是讓代理人能夠直接從儲存庫本身推理整個業務網域。
從代理人的角度來看,任何它在運行時無法在上下文中訪問的東西實際上並不存在。存在於 Google Docs、聊天線程或人們腦中的知識對系統來說是無法訪問的。儲存庫本地、版本化的工件(例如程式碼、markdown、架構、可執行計劃)是它唯一能看到的。
我們了解到,隨著時間的推移,我們需要將越來越多的上下文推入儲存庫。Slack 上那個讓團隊就架構模式達成一致的討論?如果代理人無法發現它,那麼它的可讀性就如同三個月後加入的新員工不知道它一樣。
為 Codex 提供更多上下文意味著組織和公開正確的信息,以便代理人能夠對其進行推理,而不是用臨時的指令淹沒它。就像您在產品原則、工程規範和團隊文化(包括表情符號偏好)方面對新隊友進行入職培訓一樣,為代理人提供這些信息可以產生更好的輸出。
這種框架釐清了許多權衡。我們傾向於那些可以完全在儲存庫中內化和推理的依賴項和抽象。通常被描述為「無聊」的技術由於其可組合性、API 穩定性和在訓練集中的表示,通常更容易被代理人建模。在某些情況下,讓代理人重新實現功能子集比處理來自公共庫的不透明上游行為更划算。例如,我們沒有引入一個通用的 p-limit 風格的套件,而是實現了自己的帶並發映射的助手:它與我們的 OpenTelemetry 儀表化緊密集成,具有 100% 的測試覆蓋率,並且行為完全符合我們運行時的預期。
將系統的更多部分拉入代理人可以直接檢查、驗證和修改的形式,可以增加槓桿作用——不僅對 Codex,也對其他正在處理程式碼庫的代理人(例如 Aardvark )而言。
僅靠文件無法使完全由代理人生成的程式碼庫保持一致。透過強制執行不變性,而不是微觀管理實現,我們讓代理人能夠快速發布,而不會破壞基礎。例如,我們要求 Codex 在邊界處解析數據形狀 (opens in a new window) ,但並不規定如何實現(模型似乎喜歡 Zod,但我們沒有指定這個特定的庫)。
代理人在具有嚴格邊界和可預測結構的環境中效率最高 (opens in a new window) ,因此我們圍繞著一個嚴格的架構模型構建了應用程式。每個業務網域被劃分為一組固定的層,具有嚴格驗證的依賴方向和有限的允許邊緣集。這些約束透過自定義 linter(當然也是由 Codex 生成的!)和結構測試進行機械強制執行。
下圖顯示了規則:在每個業務網域(例如 App Settings)內,程式碼只能透過一組固定的層「向前」依賴(Types → Config → Repo → Service → Runtime → UI)。橫切關注點(身份驗證、連接器、遙測、功能標誌)透過單一的顯式接口進入:Providers。其他任何內容都將被禁止並透過機械方式強制執行。
這種架構通常是您在擁有數百名工程師後才會推遲的。對於編碼代理人來說,這是一個早期先決條件:約束是實現速度而不衰減或架構漂移的關鍵。
實際上,我們使用自定義 linter 和結構測試來強制執行這些規則,再加上一組小型的「品味不變性」。例如,我們透過自定義 linter 在靜態上強制執行結構化日誌記錄、架構和類型的命名約定、文件大小限制以及特定平台的可靠性要求。由於 linter 是自定義的,我們編寫了錯誤消息,將補救說明注入代理人上下文。
在以人為中心的工作流程中,這些規則可能會顯得過於瑣碎或限制。對於代理人來說,它們成為了乘數:一旦編碼,它們就會同時應用於所有地方。
同時,我們明確了約束條件何時重要,何時不重要。這類似於領導一個大型工程平台組織:在中心強制執行邊界,在本地允許自主。您非常關心邊界、正確性和可重複性。在這些邊界內,您允許團隊——或代理人——在解決方案的表達方式上有很大的自由度。
生成的程式碼並不總是符合人類的風格偏好,這沒關係。只要輸出是正確的、可維護的且對未來的代理人運行是可讀的,它就符合標準。
人類的品味會持續不斷地反饋到系統中。審查評論、重構提取請求和使用者面臨的錯誤會被捕獲為文件更新或直接編碼到工具中。當文件不足時,我們將規則提升到程式碼中。
隨著 Codex 產出的增加,許多傳統的工程規範變得適得其反。
儲存庫以最少的阻塞合併門控運行。提取請求的壽命很短。測試的波動性通常透過後續運行來解決,而不是無限期地阻止進度。在代理人產出遠超人類注意力的系統中,修正成本很低,而等待成本很高。
在低產出環境中,這將是不負責任的。在這裡,這通常是正確的權衡。
當我們說程式碼庫是由 Codex 代理人生成的時,我們指的是程式碼庫中的一切。
人類始終處於循環中,但工作在比以前更高的抽象層次上。我們優先處理工作,將使用者回饋轉化為驗收標準,並驗證結果。當代理人遇到困難時,我們將其視為一個信號:確定缺失的部分——工具、護欄、文件——並將其反饋到儲存庫中,始終由 Codex 本身編寫修復。
代理人直接使用我們的標準開發工具。它們獲取審查回饋,內聯回應,推送更新,並經常自行壓縮和合併提取請求。
隨著開發循環的更多部分直接編碼到系統中——測試、驗證、審查、回饋處理和恢復——儲存庫最近跨越了一個有意義的閾值,Codex 可以端對端地驅動一個新功能。
給定一個單一提示,代理人現在可以:
這種行為很大程度上取決於此儲存庫的特定結構和工具,並且在沒有類似投資的情況下不應假設其普遍適用——至少目前還不行。
完全的代理人自主性也帶來了新的問題。Codex 會複製儲存庫中已存在的模式——即使是參差不齊或不理想的模式。隨著時間的推移,這不可避免地會導致漂移。
起初,人類手動解決了這個問題。我們的團隊曾經每週五(佔一週的 20%)花時間清理「AI 垃圾」。不出所料,這並沒有擴大規模。
相反,我們開始將我們稱為「黃金原則」的內容直接編碼到儲存庫中,並建立了一個定期的清理流程。這些原則是主觀的、機械化的規則,可以保持程式碼庫對未來代理人運行是可讀和一致的。例如:(1)我們傾向於使用共享的工具包而不是手寫的助手來集中管理不變性,並且(2)我們不「隨意」探測數據——我們驗證邊界或依賴類型化的 SDK,這樣代理人就不會意外地建立在猜測的形狀之上。定期,我們有一組後台 Codex 任務來掃描偏差,更新質量等級,並開啟有針對性的重構提取請求。其中大部分可以在一分鐘內審查並自動合併。
這就像垃圾收集。技術債務就像高利率貸款:持續以小額增量償還幾乎總比讓其複利然後痛苦地一次性處理要好。人類的品味被捕獲一次,然後在每一行程式碼上持續執行。這也使我們能夠每天捕捉和解決不良模式,而不是讓它們在程式碼庫中傳播數天或數週。
到目前為止,這項策略在 OpenAI 的內部發布和採用方面都取得了成功。為真實用戶構建真實產品有助於將我們的投資紮根於現實,並引導我們走向長期的可維護性。
我們尚不知道的是,在一個完全由代理人生成的系統中,架構一致性是如何隨著時間的推移而演變的。我們仍在學習人類判斷在哪裡能產生最大的槓桿作用,以及如何編碼這種判斷使其能夠複利。我們也不知道隨著模型能力的持續增強,這個系統將如何演變。
已經很清楚的是:構建軟體仍然需要紀律,但紀律更多地體現在框架上,而不是程式碼本身。保持程式碼庫一致性的工具、抽象和回饋迴路越來越重要。
我們目前最艱鉅的挑戰集中在設計環境、回饋迴路和控制系統,以幫助代理人實現我們的目標:大規模構建和維護複雜、可靠的軟體。
隨著像 Codex 這樣的代理人承擔軟體生命週期中越來越大的部分,這些問題將變得更加重要。我們希望分享一些早期經驗教訓,能幫助您推理出在哪裡投入精力,以便您可以專注於構建事物。
特別感謝 Victor Zhu 和 Zach Brock 對本文的貢獻,以及構建這個新產品的整個團隊。