Codex CLI(在新視窗中開啟) 是我們的跨平台本地軟件智能代理,旨在於你的機器上安全高效地運行,並產生高品質、可靠的軟件變更。自從我們在 4 月首次推出 CLI 以來,我們已經學到大量關於如何打造世界級軟件智能代理的知識。為了深入剖析這些見解,這是持續系列的第一篇文章,我們將探討 Codex 的各個運作層面,以及一些得來不易的經驗教訓。(如需更詳細地了解 Codex CLI 的構建方式,請查看我們的開源程式碼庫,網址為 https://github.com/openai/codex(在新視窗中開啟)。如果你想了解更多,我們設計決策的許多細節都記錄在 GitHub 的問題和拉取請求中。)
首先,我們會聚焦於智能代理迴圈,這是 Codex CLI 中的核心邏輯,負責協調用戶、模型,以及模型援引的工具以執行有意義的軟件工作。希望這篇文章能讓你清楚了解我們的智能代理(或「harness」)在使用 LLM 過程中所扮演的角色。
在我們開始之前,先簡單說明一下術語:在 OpenAI,「Codex」涵蓋一系列智能代理產品,包括 Codex CLI、Codex Cloud 和 Codex VS Code 擴充套件。本文重點介紹 Codex harness,其提供核心智能代理迴圈和執行邏輯,這些是所有 Codex 體驗的基礎,並透過 Codex CLI 呈現。為方便起見,我們會將「Codex」和「Codex CLI」互為使用。
每個 AI 智能代理的核心稱為「智能代理迴圈」。智能代理迴圈的簡化說明如下:
首先,智能代理會從用戶取得輸入,並將其納入為模型準備的文字指示中,稱為提示詞。
下一步是透過傳送指示來查詢模型,並要求它產生回應,這個過程稱為推論。在推論期間,文字提示詞會先被轉換成輸入 Token(在新視窗中開啟) 序列,這些整數用於索引至模型的詞彙表。這些 Token 隨後用於對模型進行取樣,產生新的輸出 Token 序列。
輸出 Token 翻譯回文字後,成為模型的回應。由於 Token 是逐步產生的,因此翻譯可以在模型運行時進行,這就是為甚麼許多基於 LLM 的應用程式會顯示串流輸出。實際上,推論通常被封裝在一個對文本進行操作的 API 背後,抽象化處理 Token 化的細節。
作為推論步驟的結果,模型會 (1) 對用戶的原始輸入產生最終回覆,或 (2) 要求工具調用,智能代理預期需要執行該工具調用(例如:「執行 ls 並回報輸出」)。在 (2) 的情況下,智能代理執行工具調用,並將其輸出附加到原始提示詞。此輸出用來產生新的輸入,用於重新查詢模型;智能代理可以將這些新資訊納入考慮,然後再嘗試一次。
此過程會重複,直至模型停止發出工具調用,並改成為用戶產生一則訊息(在 OpenAI 模型中稱為助理訊息)。在很多情況下,這則訊息會直接回應用戶的初始請求,但亦可能是用戶的跟進問題。
由於智能代理可以執行修改本地環境的工具調用,所以其「輸出」不僅限於助理訊息。在許多情況下,智能代理的主要輸出是它在你的電腦上撰寫或修改的代碼。不過,每一回合總是會以一則助理訊息作結,例如「我已加入你要求的 architecture.md」,這表示智能代理迴圈中的終止狀態。從智能代理的角度來看,工作已完成,控制權交還給用戶。
圖中所示,從用戶輸入到智能代理回覆的過程,稱為一次對話的「回合」(在 Codex 中稱為一個對話串)。一個對話回合之中,模型推論與工具調用之間,可能會進行多次反覆互動。每當你在既有對話中傳送新訊息時,整段對話記錄都會納入新一個回合的提示詞內容之中,包括先前回合的訊息與工具調用記錄:
這意味著隨著對話增加,用於抽樣模型的提示詞長度亦會增加。該長度很重要,因為每個模型都有一個上下文視窗,即它在一次推論調用中可使用的最大 Token 數量。請注意,此窗口包含輸入和輸出 Token。正如你可能所想像,智能代理可能會決定在單一回合中進行數百次工具調用,從而可能耗盡上下文窗口。因此,上下文窗口管理是智能代理眾多職責之一。現在,讓我們深入了解 Codex 如何運行智能代理迴圈。
Codex CLI 會向 Responses API(在新視窗中開啟) 發送 HTTP 請求,以進行模型推論。我們將探討資訊如何在 Codex 中流動,Codex 會使用 Responses API 來驅動智能代理迴圈。
Codex CLI 使用的 Responses API 端點是可配置(在新視窗中開啟)的,因此可以與任何實現 Responses API(在新視窗中開啟) 的端點一起使用:
- 使用 ChatGPT 登入(在新視窗中開啟)配合 Codex CLI 時,會使用
https://chatgpt.com/backend-api/codex/responses作為端點 - 使用 API-key 驗證(在新視窗中開啟)配合 OpenAI 託管的模型時,會使用
https://api.openai.com/v1/responses作為端點 - 當使用
--oss運行 Codex CLI 以使用 gpt-oss 與 ollama 0.13.4+(在新視窗中開啟) 或 LM Studio 0.3.39+(在新視窗中開啟) 時,預設為在你電腦上本機運行http://localhost:11434/v1/responses - Codex CLI 可與由雲端供應商(例如 Azure)託管的 Responses API 一起使用
讓我們探索 Codex 如何在對話中為首次推論調用建立提示詞。
作為終端用戶,當你查詢 Responses API 時,你不會逐字指定用於取樣模型的提示詞。相反,你在查詢中指定各種輸入類型,而 Responses API 伺服器決定如何將這些資訊結構化為提示詞,以便模型能夠使用。你可以將提示詞視作「項目清單」;本部分將說明你的查詢如何轉換為該清單。
在初始提示詞中,清單中的每個項目都與一個角色相關。角色表示相關內容應有多大權重,並且是以下其中一個值(按優先順序由高至低排列):系統、開發人員、用戶、助理。
Responses API(在新視窗中開啟) 會接收包含多個參數的 JSON 負載。我們將專注於這三個方面:
指示(在新視窗中開啟):插入到模型上下文中的系統(或開發人員)訊息工具(在新視窗中開啟):模型在產生回覆時可能會調用的工具清單輸入(在新視窗中開啟):提供給模型的文字、圖像或檔案輸入清單
在 Codex 中,指示欄位會從 model_instructions_file(在新視窗中開啟) 中的 ~/.codex/config.toml 讀取(如有指定);否則,會使用與模型相關的 base_instructions(在新視窗中開啟)。特定於模型的指示位於 Codex 儲存庫中,並已打包到 CLI 中(例如:gpt-5.2-codex_prompt.md(在新視窗中開啟))。
工具欄位是一個工具定義清單,符合由 Responses API 定義的結構定義。對於 Codex,這包括由 Codex CLI 提供的工具、由 Responses API 提供且應提供給 Codex 的工具,以及由用戶提供的工具(通常透過 MCP 伺服器):
最後,JSON 負載的輸入欄位是一個項目列表。Codex 會將以下項目插入(在新視窗中開啟)到輸入中,然後再加入用戶訊息:
1. 一則寫有 role=developer 的訊息,描述僅適用於工具部分中定義的 Codex 提供的 shell 工具的沙盒。也就是說,其他工具(例如由 MCP 伺服器提供的工具)不受 Codex 沙盒保護,並須負責執行其本身的防護措施。
此訊息是由一個範本建立,其中關鍵內容片段來自隨 Codex CLI 一併打包的 Markdown 片段,例如 workspace_write.md(在新視窗中開啟) 和 on_request.md(在新視窗中開啟):
2.(可選)一則帶有 role=developer 的訊息,其內容是從用戶的 config.toml 文件中讀取的 developer_instructions 值。
3.(可選)一則帶有 role=user 的訊息,其內容為「用戶指示」,這些指示並非來自單一檔案,而是彙整自多個來源(在新視窗中開啟)。一般來說,較具體的指示會在之後出現:
$CODEX_HOME中AGENTS.override.md和AGENTS.md的內容- 受限於限制(預設為 32 KiB),查看從 Git/項目根目錄(如果存在)以至
cwd本身的每個資料夾:加入任何AGENTS.override.md的內容,AGENTS.md,或config.toml 中 project_doc_fallback_filenames指定的任何檔案名稱 - 如果已設定任何技能(在新視窗中開啟):
- 一段有關技能的簡短說明
- 每個技能的技能元資料(在新視窗中開啟)
- 有關如何使用技能(在新視窗中開啟)的部分
4. 一則帶有 role=user 的訊息,描述智能代理目前運行的本地環境。這指定目前的工作目錄和用戶的 shell(在新視窗中開啟):
一旦 Codex 完成上述所有計算以初始化輸入,便會附加用戶訊息以開始對話。
之前的例子著重於每則訊息的內容,但請注意,輸入的每個元素都是一個 JSON 物件,具有種類、角色(在新視窗中開啟)和內容,如下所示:
一旦 Codex 構建好要發送到 Responses API 的完整 JSON 負載,便會根據 ~/.codex/config.toml 中的 Responses API 端點配置方式,發出帶有授權標題的 HTTP POST 請求(如果有指定,則會添加額外的 HTTP 標題和查詢參數)。
當 OpenAI Responses API 伺服器收到請求時,它會使用 JSON 來推導出模型的提示詞,如下所示(為免生疑問,Responses API 的自訂實施可能會作出不同的選擇):
如你所見,提示詞中首三個項目的順序是由伺服器決定,而非客戶端。儘管如此,在這三個項目中,只有系統訊息的內容亦由伺服器控制,因為工具和指示是由客戶端決定的。後面會接上來自 JSON 負載的輸入,以完成提示詞。
現在我們已經有了提示詞,可以開始對模型進行取樣。
此 HTTP 請求會向 Responses API 發出,並在 Codex 中啟動對話的第一個「回合」。伺服器會以伺服器傳送事件 (SSE(在新視窗中開啟)) 串流回應。每個事件的資料是一個 JSON 負載,具有一個以「回應」開頭的「種類」,內容可能如下(完整事件列表可在我們的 API 文件(在新視窗中開啟)中找到):
Codex 消耗事件串流(在新視窗中開啟),並將其重新發佈為可供用戶端使用的內部事件物件。response.output_text.delta 等事件用於支援 UI 中的串流,而 response.output_item.added 等其他事件則會轉換為物件,並附加到輸入,以供後續的 Responses API 調用。
假設對 Responses API 的第一個請求包含兩個 response.output_item.done 事件:一個是 type=reasoning,另一個是 type=function_call。當我們使用工具調用的回應再次查詢模型時,這些事件必須在 JSON 的輸入欄位中表示:
作為後續查詢的一部分,所得出用於模型取樣提示詞如下所示:
具體而言,注意舊提示詞是新提示詞的精確前綴。這是故意的,因為這樣可令後續請求更有效率,原因是這讓我們能夠善用提示詞快取(我們會在下一節的效能部分討論)。
回顧我們的第一張智能代理迴圈圖表,我們可以看到在推理與工具調用之間可能會有多次迭代。提示詞可能會繼續增長,直至我們最終收到助理訊息,表示回合結束:
在 Codex CLI 中,我們會向用戶顯示助理訊息,並聚焦輸入框,以向用戶表明這是他們的「回合」以繼續對話。如果用戶回應,上一回合的助理訊息和用戶的新訊息都必須附加到 Responses API 請求的輸入中,以開始新一個回合:
特此重申,由於我們正在繼續對話,傳送到 Responses API 的輸入長度不斷增加:
讓我們檢視這個不斷增長的提示詞對效能的意義。
你可能會問自己:「等等,智能代理迴圈在整段對話期間傳送到 Responses API 的 JSON 數量不是二次方的嗎?」你是對的。雖然 Responses API 確實支援可選的 previous_response_id(在新視窗中開啟) 參數來緩解此問題,但 Codex 目前並未使用該參數,主要是為了保持請求完全無狀態,並支援零資料保留 (ZDR) 配置。
避免使用 previous_response_id 可以簡化回覆 API 提供者的流程,因為這確保了每個請求都是無狀態。這同時令支援選擇加入零資料保留 (ZDR)(在新視窗中開啟)的客戶變得直接,因為儲存支援 previous_response_id 所需的資料會與 ZDR 相矛盾。請注意,ZDR 客戶不會失去從先前回合的專有推理訊息中獲益的能力,因為相關的 encrypted_content 可以在伺服器上解密。(OpenAI 會保留零資料保留 (ZDR) 客戶的解密金鑰,但不會保留其資料。)查看 PR #642(在新視窗中開啟) 和 #1641(在新視窗中開啟) 以了解 Codex 為支援 ZDR 所做的相關更改。
一般來說,模型取樣的成本通常超過網絡流量的成本,因此取樣成為我們提高效率的主要目標。這就是提示詞快取如此重要的原因,因為能使我們可重用先前推論調用的計算。當我們取得快取命中時,對模型的取樣是線性而非二次方。我們的提示詞快取(在新視窗中開啟)文件詳細說明了這一點:
快取命中僅在提示詞中精確匹配前綴時才可能發生。為了實現快取效益,將指示和範例等靜態內容放在提示詞的開端,並將可變內容(例如用戶特定資訊)放在末端。這亦適用於圖片和工具,必須在每次請求之間完全相同。
考慮到這一點,讓我們考慮在 Codex 中,哪些類型的操作可能會導致「快取未命中」:
- 在對話中途更改模型可用的
工具。 - 變更作為 Responses API 請求目標的
模型(實際上,這會變更原始提示詞中的第三項,因為它包含特定於模型的指示)。 - 變更沙盒配置、核准模式或當前工作目錄。
Codex 團隊在 Codex CLI 中引入可能會影響提示詞快取的新功能時,必須保持謹慎。舉例來說,我們對 MCP 工具的初步支援引入了一個錯誤,未能以一致的順序列舉工具(在新視窗中開啟),導致快取未命中。請注意,MCP 工具可能特別棘手,因為 MCP 伺服器可以透過 notifications/tools/list_changed(在新視窗中開啟) 通知即時變更其提供的工具清單。在長時間對話中途遵從此通知可能會導致昂貴的快取未命中。
在可行的情況下,我們會在輸入的末端附加一則新訊息來反映變更,以處理在對話期間發生的配置變更,而非修改較早的訊息:
- 如果沙盒配置或核准模式出現變更,我們會插入(在新視窗中開啟)一則新的
role=developer訊息,其格式與原本的<permissions instructions>項目相同。 - 如果目前工作目錄出現變更,我們會插入(在新視窗中開啟)一則新的
role=user訊息,其格式與原本的<environment_context>相同。
我們不遺餘力確保快取命中,以提升效能。我們還需要管理另一個關鍵資源:上下文視窗。
我們的一般策略是避免耗盡情境窗口,當 Token 數量超過某個閾值時,將對話進行壓縮。具體而言,我們會以一個新的、更精簡且能代表對話內容的項目清單取代輸入,讓智能代理能在理解目前為止發生事情的情況下繼續進行。早期壓縮實施(在新視窗中開啟)需要用戶手動援引 /compact 指令,該指令會使用現有對話及自訂指示來查詢 Responses API 以進行摘要(在新視窗中開啟)。Codex 使用包含摘要的助理訊息作為後續對話回合的新輸入(在新視窗中開啟)。
自此之後,Responses API 已進化為以支援一個特殊的 /responses/compact 端點(在新視窗中開啟),可更高效地執行壓縮。它會傳回項目清單(在新視窗中開啟),可用來取代先前的輸入,以繼續對話,同時釋放上下文視窗。此清單包括一個特殊的 type=compaction 項目,以及一個不透明的 encrypted_content 項目,其保留模型對原始對話的潛在理解。現在,當超過 auto_compact_limit(在新視窗中開啟),Codex 會自動使用此端點來壓縮對話。
我們已介紹 Codex 智能代理迴圈,並詳細說明 Codex 在查詢模型時如何建立與管理其情境。在此過程中,我們強調了實用考量和最佳實務,適用於任何在 Responses API 上構建智能代理迴圈的人。
雖然智能代理迴圈為 Codex 提供基礎,但這僅僅是開始。在接下來的文章中,我們會深入探討 CLI 的架構,探索工具使用的實現方式,並進一步了解 Codex 的沙盒模型。


