Codex CLI(在新視窗中開啟) 是我們的跨平台的本機軟體智慧體,旨在安全且高效地在你的機器上運作,同時產出高品質、可靠的軟體變更。自從 4 月首次推出 CLI 以來,我們已學到許多如何打造一流軟體智慧體的知識。為了深入探討這些見解,我們將推出一系列文章,而這是第一篇。我們將在其中探討 Codex 的運作方式的各個面向,以及得之不易的寶貴經驗。(若要更詳細地瞭解 Codex CLI 的建置方式,請查看我們的開源程式碼庫:https://github.com/openai/codex(在新視窗中開啟)。若要深入瞭解,請在 GitHub issue 和拉取要求中查看我們的設計決策詳情。)
首先,我們將聚焦於 智慧體的運作循環,這是 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 會將 HTTP 要求傳送至 Responses API(在新視窗中開啟) 以執行模型推論。我們將探討資訊如何在 Codex 中流動,而 Codex 使用 Responses API 來驅動智慧體的運作循環。
Codex CLI 使用的 Responses API 端點是 可配置的(在新視窗中開啟),因此能與任何實作 Responses API(在新視窗中開啟)的端點一起使用:
- 使用 Codex CLI 透過 ChatGPT 登入時(在新視窗中開啟),它會使用
https://chatgpt.com/backend-api/codex/responses作為端點 - 使用 API 金鑰認證(在新視窗中開啟)存取 OpenAI 託管模型時,它會使用
https://api.openai.com/v1/responses作為端點 - 使用
--oss執行 Codex CLI 以在 Ollama 0.13.4+(在新視窗中開啟) 或 LM Studio 0.3.39+(在新視窗中開啟) 中使用 gpt-oss 時,預設會存取本機上執行的http://localhost:11434/v1/responses - Codex CLI 可與由 Azure 等雲端供應商託管的 Responses API 一起使用
一起來探索 Codex 如何在對話中為第一次推理呼叫建立提示詞。
作為終端使用者,在查詢 Responses API 時,你不會逐字指定用來採樣模型的提示詞。相反地,你會在查詢中指定各種輸入類型,而 Responses API 伺服器會決定如何將這些資訊結構化為提示詞,以供模型使用。你可以將提示詞視為「項目清單」;本節將解釋你的查詢如何轉換成該清單。
在初始提示詞中,清單中的每個項目都與一個角色相關聯。role 用來表示相關內容的重要程度,其可取以下其中一種值 (依優先順序由高至低):system、developer、user、assistant。
Responses API(在新視窗中開啟) 接收包含多個參數的 JSON 承載。我們將重點擺在以下三者:
instructions(在新視窗中開啟):系統 (或開發者) 訊息插入到模型的上下文中tools(在新視窗中開啟):模型在產生回應時可能呼叫的工具清單input(在新視窗中開啟):提供給模型的文字、圖像或檔案輸入清單
在 Codex 中, instructions 欄位會從 model_instructions_file(在新視窗中開啟) (位於 ~/.codex/config.toml 中)讀取(若有指定);否則,會使用 base_instructions (在新視窗中開啟)(與模型相關聯的指令)。特定模型的指令位於 Codex 儲存庫中,並隨 CLI 一併打包(例如:gpt-5.2-codex_prompt.md(在新視窗中開啟))。
tool 欄位是工具定義的清單,這些工具定義符合 Responses API 所定義的結構描述。對 Codex 而言,這包括由 Codex CLI 提供的工具、由 Responses API 提供且應提供給 Codex 使用的工具,以及由使用者提供的工具 (通常是透過 MCP 伺服器):
最後,JSON 承載內容的 input 欄位是一個項目清單。Codex 將下列項目插入(在新視窗中開啟)到 input 中,然後再新增使用者訊息:
1.一則 role=developer 訊息,用來描述僅適用於 tools 區段中所定義、由 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) 內,從
cwd的 Git/專案根目錄 (如果存在) 開始,逐一查看每個資料夾直到cwd本身:加入任何AGENTS.override.md的內容,AGENTS.md,或project_doc_fallback_filenames在 config.toml 中指定的任何檔名 - 若已設定任何技能(在新視窗中開啟):
- 關於技能的簡短前言
- 每個技能的技能中繼資料(在新視窗中開啟)
- 關於如何使用技能(在新視窗中開啟)的區段
4. 一則 role=user 的訊息,描述智慧體目前運行的本地環境。它指定目前的工作目錄和使用者的 shell(在新視窗中開啟):
一旦 Codex 完成上述所有運算以初始化 input,便會附加使用者訊息以開始對話。
前面的範例著重於每則訊息的內容,但請注意,input 的每個元素都是一個 JSON 物件,包含 type、role(在新視窗中開啟) 和 content,如下所示:
一旦 Codex 建立好要傳送至 Responses API 的完整 JSON 承載內容後,便會根據 ~/.codex/config.toml 中的 Responses API 端點設定,發出包含 Authorization 標頭的 HTTP POST 要求 (若有指定,則會加入額外的 HTTP 標頭與查詢參數)。
OpenAI Responses API 伺服器收到要求時,會使用 JSON 來推導出模型的提示詞,如下所示 (為求嚴謹,Responses API 的自訂實作可能會採用不同的方法):
如你所見,提示詞中前三個項目的順序是由伺服器 (而非用戶端) 決定。也就是說,在這三項中,只有系統訊息的內容也由伺服器控制,因為 tools 和 instructions 是由用戶端決定的。接著會使用 JSON 承載內容中的 input 來完成提示詞。
既然已經有了提示詞,就可以開始對模型進行採樣。
此 HTTP 要求會向 Responses API 發出,並在 Codex 中啟動對話的第「回合」。伺服器會以 Server-Sent Events (SSE(在新視窗中開啟)) 串流回應。每個事件的 data 是一個 JSON 承載內容,具有一個以 「response」 開頭的 「type」,可能會像這樣 (完整事件清單請見我們的 API 文件(在新視窗中開啟)):
Codex 接收事件串流(在新視窗中開啟)並將其重新發布為可供用戶端使用的內部事件物件。像 response.output_text.delta 這類事件用於支援 UI 中的串流,而其他像 response.output_item.added 這類事件則會轉換為物件,並附加到 input 中,以供後續的 Response API 呼叫使用。
假設對 Responses API 的第一個要求包含兩個 response.output_item.done 事件:一個是 type=reasoning,另一個是 type=function_call。當我們使用工具呼叫的回應再次查詢模型時,這些事件必須在 JSON 的 input 欄位中呈現:
用來在後續查詢中採樣模型的提示詞如下:
特別注意,舊的提示詞是新提示詞的精確前綴。這是有意為之,因為這樣能讓後續要求更有效率,讓我們能夠利用提示詞快取 (我們會在下一節的效能區段討論)。
回顧第一張智慧體運作循環,可以看到推論與工具呼叫之間可能會有多次反覆。提示詞可能會持續增長,直到我們最終收到一則助理訊息,表示回合結束:
在 Codex CLI 中,我們會向使用者呈現助理訊息,並將焦點移至編輯器,以向使用者表示現在輪到他們的「回合」繼續對話。如果使用者回覆,必須將上一回合的助理訊息和使用者的新訊息附加到 Responses API 要求中的 input,以開始新的回合:
再次重申,由於我們正在延續對話,傳送到 Responses API 的 input 不斷增長:
一同檢視這個不斷成長的提示詞對效能有何影響。
你可能會問自己:「等等,整段對話中傳送到 Responses API 的 JSON 數量,難道不會讓智慧體運作循環呈現二次方的成長嗎?」你是對的。雖然 Responses API 確實支援選用的 previous_response_id(在新視窗中開啟) 參數來緩解此問題,但 Codex 目前尚未使用它,主要是為了讓要求完全無狀態,並支援零資料保留 (ZDR) 設定。
避免使用 previous_response_id 可以簡化 Responses API 提供者的工作,因為這確保了每個要求都是無狀態的。這也讓支援選擇加入零資料保留 (ZDR)(在新視窗中開啟) 的客戶變得更為簡單,因為儲存支援 previous_response_id 所需的資料會與 ZDR 的原則相牴觸。請注意,ZDR 客戶不會犧牲從先前回合的專有推理訊息中受益的能力,因為相關的 encrypted_content 可以在伺服器上解密。(OpenAI 會保留 ZDR 客戶的解密金鑰,但不會保留他們的資料。)查看 PR #642(在新視窗中開啟) 和 #1641(在新視窗中開啟),瞭解 Codex 為支援 ZDR 所做的相關變更。
一般來說,模型採樣的成本通常超過網路流量的成本,因此採樣成為我們提高效率的主要目標。這就是為什麼提示詞快取如此重要,因為它讓我們重複使用先前推論呼叫的運算。快取命中時,對模型進行採樣是線性的,而不是二次方的。我們的提示詞快取(在新視窗中開啟)說明文件對此有更詳細的說明:
快取命中只可能在提示詞內進行精確前綴比對時發生。為了實現快取效益,將指令和範例等靜態內容放在提示詞的開頭,並將可變內容 (例如使用者特定資訊) 放在最後。這也適用於圖片和工具,它們在不同的要求中必須完全相同。
考量到這一點,我們來探討在 Codex 中,哪些操作類型可能會導致「快取未命中」:
- 在對話進行中途變更模型可用的
tool。 - 變更作為 Responses API 要求目標的
模型(實際上,這會變更原始提示詞中的第三項,因為其中包含模型特定的指示)。 - 變更沙箱設定、核准模式或目前工作目錄。
Codex 團隊在 Codex CLI 中導入可能危及提示詞快取的新功能時,必須保持謹慎。舉例來說,我們最初對 MCP 工具的支援引入了一個錯誤:我們未能以一致的順序列舉工具(在新視窗中開啟),導致快取未命中。請注意,MCP 工具可能特別棘手,因為 MCP 伺服器可以透過 notifications/tools/list_changed(在新視窗中開啟) 通知,隨時變更其提供的工具清單。在一段冗長對話進行到一半時處理此通知,可能會導致昂貴的快取未命中。
在可能的情況下,當對話中途發生設定變更時,我們會將一則新訊息附加到 input,以反映該變更,而不是修改先前的訊息:
- 如果沙箱設定或核准模式變更,我們會插入(在新視窗中開啟)一則新的
role=developer訊息,其格式與原始<permissions instructions>項目相同。 - 如果目前的工作目錄變更,我們會插入(在新視窗中開啟)一則新的
role=user訊息,格式與原始<environment_context>相同。
我們竭盡所能確保快取命中,以提升效能。我們也必須管理另一項關鍵資源:上下文視窗。
我們的一般策略是當 token 數量超過某個門檻時,將對話壓縮,以避免耗盡上下文視窗。具體而言,我們將 input 替換為一個新的、更精簡的項目清單,該清單代表對話內容,使智慧體在理解目前為止發生的事情的情況下繼續進行。早期的壓縮實作(在新視窗中開啟)需要使用者手動執行 /compact 命令,該命令會使用現有對話和自訂指令來查詢 Responses API 以進行總結(在新視窗中開啟)。Codex 使用包含摘要的結果助理訊息 作為新的 輸入(在新視窗中開啟),用於後續的對話回合。
從那時起,Responses API 已演進以支援一個特殊的 /responses/compact 端點(在新視窗中開啟),更有效率地執行壓縮。它會傳回項目清單(在新視窗中開啟),可用來取代先前的 input,以繼續對話,同時釋放上下文視窗。此清單包含一個特殊的 type=compaction 項目,以及一個不透明的 encrypted_content 項目,保留模型對原始對話的潛在理解。現在,當超出 auto_compact_limit(在新視窗中開啟) 時,Codex 會自動使用此端點來壓縮對話。
我們已經介紹了 Codex 智慧體運作循環,並詳細說明 Codex 在查詢模型時如何建構及管理其上下文。在此過程中,我們強調了適用於任何在 Responses API 上建置智慧體運作循環的人的實務考量與最佳做法。
雖然智慧體運作循環為 Codex 奠定了基礎,但這僅是個開端。在接下來的文章中,我們將深入探討 CLI 的架構,探索工具使用的實作方式,並更仔細地研究 Codex 的沙箱模型。


