跳到主要內容
OpenAI

2026年1月23日

工程

展開 Codex 智能代理迴圈

作者:Michael Bolin,技術團隊成員

正在載入...

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 智能代理的核心稱為「智能代理迴圈」。智能代理迴圈的簡化說明如下:

標題為「智能代理迴圈」的圖表,說明 AI 系統如何處理用戶請求、調用工具、觀察結果、更新其計劃,並傳回輸出。箭頭連接各個步驟,例如用戶輸入、模型推理、工具操作,以及最終回應。

首先,智能代理會從用戶取得輸入,並將其納入為模型準備的文字指示中,稱為提示詞

下一步是透過傳送指示來查詢模型,並要求它產生回應,這個過程稱為推論。在推論期間,文字提示詞會先被轉換成輸入 Token(在新視窗中開啟) 序列,這些整數用於索引至模型的詞彙表。這些 Token 隨後用於對模型進行取樣,產生新的輸出 Token 序列。

輸出 Token 翻譯回文字後,成為模型的回應。由於 Token 是逐步產生的,因此翻譯可以在模型運行時進行,這就是為甚麼許多基於 LLM 的應用程式會顯示串流輸出。實際上,推論通常被封裝在一個對文本進行操作的 API 背後,抽象化處理 Token 化的細節。

作為推論步驟的結果,模型會 (1) 對用戶的原始輸入產生最終回覆,或 (2) 要求工具調用,智能代理預期需要執行該工具調用(例如:「執行 ls 並回報輸出」)。在 (2) 的情況下,智能代理執行工具調用,並將其輸出附加到原始提示詞。此輸出用來產生新的輸入,用於重新查詢模型;智能代理可以將這些新資訊納入考慮,然後再嘗試一次。

此過程會重複,直至模型停止發出工具調用,並改成為用戶產生一則訊息(在 OpenAI 模型中稱為助理訊息)。在很多情況下,這則訊息會直接回應用戶的初始請求,但亦可能是用戶的跟進問題。

由於智能代理可以執行修改本地環境的工具調用,所以其「輸出」不僅限於助理訊息。在許多情況下,智能代理的主要輸出是它在你的電腦上撰寫或修改的代碼。不過,每一回合總是會以一則助理訊息作結,例如「我已加入你要求的 architecture.md」,這表示智能代理迴圈中的終止狀態。從智能代理的角度來看,工作已完成,控制權交還給用戶。

圖中所示,從用戶輸入智能代理回覆的過程,稱為一次對話的「回合」(在 Codex 中稱為一個對話串)。一個對話回合之中,模型推論工具調用之間,可能會進行多次反覆互動。每當你在既有對話中傳送新訊息時,整段對話記錄都會納入新一個回合的提示詞內容之中,包括先前回合的訊息與工具調用記錄:

標題為「多回合智能代理迴圈」的圖表,顯示 AI 智能代理如何迭代接收用戶輸入、產生操作、查詢工具、更新狀態,並傳回結果。包括帶有標籤的步驟、箭頭,以及顯示智能代理推理週期的範例工具輸出。

這意味著隨著對話增加,用於抽樣模型的提示詞長度亦會增加。該長度很重要,因為每個模型都有一個上下文視窗,即它在一次推論調用中可使用的最大 Token 數量。請注意,此窗口包含輸入輸出 Token。正如你可能所想像,智能代理可能會決定在單一回合中進行數百次工具調用,從而可能耗盡上下文窗口。因此,上下文窗口管理是智能代理眾多職責之一。現在,讓我們深入了解 Codex 如何運行智能代理迴圈。

模型推論

Codex CLI 會向 Responses API(在新視窗中開啟) 發送 HTTP 請求,以進行模型推論。我們將探討資訊如何在 Codex 中流動,Codex 會使用 Responses API 來驅動智能代理迴圈。

Codex CLI 使用的 Responses API 端點是可配置(在新視窗中開啟)的,因此可以與任何實現 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 伺服器):

JavaScript

1
[
2
// Codex's default shell tool for spawning new processes locally.
3
{
4
"type": "function",
5
"name": "shell",
6
"description": "Runs a shell command and returns its output...",
7
"strict": false,
8
"parameters": {
9
"type": "object",
10
"properties": {
11
"command": {"type": "array", "description": "The command to execute", ...},
12
"workdir": {"description": "The working directory...", ...},
13
"timeout_ms": {"description": "The timeout for the command...", ...},
14
...
15
},
16
"required": ["command"],
17
}
18
}
19

20
// Codex's built-in plan tool.
21
{
22
"type": "function",
23
"name": "update_plan",
24
"description": "Updates the task plan...",
25
"strict": false,
26
"parameters": {
27
"type": "object",
28
"properties": {"plan":..., "explanation":...},
29
"required": ["plan"]
30
}
31
},
32

33
// Web search tool provided by the Responses API.
34
{
35
"type": "web_search",
36
"external_web_access": false
37
},
38

39
// MCP server for getting weather as configured in the
40
// user's ~/.codex/config.toml.
41
{
42
"type": "function",
43
"name": "mcp__weather__get-forecast",
44
"description": "Get weather alerts for a US state",
45
"strict": false,
46
"parameters": {
47
"type": "object",
48
"properties": {"latitude": {...}, "longitude": {...}},
49
"required": ["latitude", "longitude"]
50
}
51
}
52
]

最後,JSON 負載的輸入欄位是一個項目列表。Codex 會將以下項目插入(在新視窗中開啟)輸入中,然後再加入用戶訊息:

1. 一則寫有 role=developer 的訊息,描述僅適用於工具部分中定義的 Codex 提供的 shell 工具的沙盒。也就是說,其他工具(例如由 MCP 伺服器提供的工具)不受 Codex 沙盒保護,並須負責執行其本身的防護措施。

此訊息是由一個範本建立,其中關鍵內容片段來自隨 Codex CLI 一併打包的 Markdown 片段,例如 workspace_write.md(在新視窗中開啟)on_request.md(在新視窗中開啟)

純文字

1
<permissions instructions>
2
- description of the sandbox explaining file permissions and network access
3
- instructions for when to ask the user for permissions to run a shell command
4
- list of folders writable by Codex, if any
5
</permissions instructions>

2.(可選)一則帶有 role=developer 的訊息,其內容是從用戶的 config.toml 文件中讀取的 developer_instructions 值。

3.(可選)一則帶有 role=user 的訊息,其內容為「用戶指示」,這些指示並非來自單一檔案,而是彙整自多個來源(在新視窗中開啟)。一般來說,較具體的指示會在之後出現:

4. 一則帶有 role=user 的訊息,描述智能代理目前運行的本地環境。這指定目前的工作目錄和用戶的 shell(在新視窗中開啟)

純文字

1
<environment_context>
2
<cwd>/Users/mbolin/code/codex5</cwd>
3
<shell>zsh</shell>
4
</environment_context>

一旦 Codex 完成上述所有計算以初始化輸入,便會附加用戶訊息以開始對話。

之前的例子著重於每則訊息的內容,但請注意,輸入的每個元素都是一個 JSON 物件,具有種類角色(在新視窗中開啟)內容,如下所示:

JSON

1
{
2
"type": "message",
3
"role": "user",
4
"content": [
5
{
6
"type": "input_text",
7
"text": "Add an architecture diagram to the README.md"
8
}
9
]
10
}

一旦 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 文件(在新視窗中開啟)中找到):

純文字

1
data: {"type":"response.reasoning_summary_text.delta","delta":"ah ", ...}
2
data: {"type":"response.reasoning_summary_text.delta","delta":"ha!", ...}
3
data: {"type":"response.reasoning_summary_text.done", "item_id":...}
4
data: {"type":"response.output_item.added", "item":{...}}
5
data: {"type":"response.output_text.delta", "delta":"forty-", ...}
6
data: {"type":"response.output_text.delta", "delta":"two!", ...}
7
data: {"type":"response.completed","response":{...}}

Codex 消耗事件串流(在新視窗中開啟),並將其重新發佈為可供用戶端使用的內部事件物件。response.output_text.delta 等事件用於支援 UI 中的串流,而 response.output_item.added 等其他事件則會轉換為物件,並附加到輸入,以供後續的 Responses API 調用。

假設對 Responses API 的第一個請求包含兩個 response.output_item.done 事件:一個是 type=reasoning,另一個是 type=function_call。當我們使用工具調用的回應再次查詢模型時,這些事件必須在 JSON 的輸入欄位中表示:

JavaScript

1
[
2
/* ... original 5 items from the input array ... */
3
{
4
"type": "reasoning",
5
"summary": [
6
"type": "summary_text",
7
"text": "**Adding an architecture diagram for README.md**\n\nI need to..."
8
],
9
"encrypted_content": "gAAAAABpaDWNMxMeLw..."
10
},
11
{
12
"type": "function_call",
13
"name": "shell",
14
"arguments": "{\"command\":\"cat README.md\",\"workdir\":\"/Users/mbolin/code/codex5\"}",
15
"call_id": "call_8675309..."
16
},
17
{
18
"type": "function_call_output",
19
"call_id": "call_8675309...",
20
"output": "<p align=\"center\"><code>npm i -g @openai/codex</code>..."
21
}
22
]

作為後續查詢的一部分,所得出用於模型取樣提示詞如下所示:

標示為「Snapshot 2」的圖表,顯示工具調用後的 AI 智能代理。模型接收工具觀察,並產生新的思維和行動。箭頭連接輸入、觀察和輸出,以說明智能代理如何迭代其推理迴圈。

具體而言,注意舊提示詞是新提示詞的精確前綴。這是故意的,因為這樣可令後續請求更有效率,原因是這讓我們能夠善用提示詞快取(我們會在下一節的效能部分討論)。

回顧我們的第一張智能代理迴圈圖表,我們可以看到在推理與工具調用之間可能會有多次迭代。提示詞可能會繼續增長,直至我們最終收到助理訊息,表示回合結束:

純文字

1
data: {"type":"response.output_text.done","text": "I added a diagram to explain...", ...}
2
data: {"type":"response.completed","response":{...}}

在 Codex CLI 中,我們會向用戶顯示助理訊息,並聚焦輸入框,以向用戶表明這是他們的「回合」以繼續對話。如果用戶回應,上一回合的助理訊息和用戶的新訊息都必須附加到 Responses API 請求的輸入中,以開始新一個回合:

JavaScript

1
[
2
/* ... all items from the last Responses API request ... */
3
{
4
"type": "message",
5
"role": "assistant",
6
"content": [
7
{
8
"type": "output_text",
9
"text": "I added a diagram to explain the client/server architecture."
10
}
11
]
12
},
13
{
14
"type": "message",
15
"role": "user",
16
"content": [
17
{
18
"type": "input_text",
19
"text": "That's not bad, but the diagram is missing the bike shed."
20
}
21
]
22
}
23
]

特此重申,由於我們正在繼續對話,傳送到 Responses API 的輸入長度不斷增加:

標示為「Snapshot 3」的圖表,顯示 AI 智能代理迴圈的最後階段。在收到工具結果後,模型會產生結論性思考,並將最終答案回傳給用戶。箭頭顯示從工具輸出到完成回覆的過渡。

讓我們檢視這個不斷增長的提示詞對效能的意義。

效能考量

你可能會問自己:「等等,智能代理迴圈在整段對話期間傳送到 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 的沙盒模型。

作者

Michael Bolin

鳴謝

特別鳴謝整個團隊開發 Codex CLI。