📢 转载信息
原文链接:https://openai.com/index/unrolling-the-codex-agent-loop
原文作者:Michael Bolin, Member of the Technical Staff
2026年1月23日
Codex CLI(opens in a new window) 是我们跨平台的本地软件代理,旨在安全高效地在您的机器上生成高质量、可靠的软件更改。自我们去年四月首次推出 CLI 以来,我们在构建世界一流的软件代理方面学到了很多东西。为了深入探讨这些见解,这是我们将要探索 Codex 工作原理的各个方面以及吸取的宝贵经验的持续系列文章的第一篇。(如需了解 Codex CLI 构建方式的更细粒度视图,请查看我们的开源仓库:https://github.com/openai/codex(opens in a new window)。如果您想了解更多信息,我们设计决策的许多细节都记录在 GitHub issue 和 pull request 中。)
首先,我们将重点关注代理循环(agent loop),这是 Codex CLI 中的核心逻辑,负责编排用户、模型以及模型为执行有意义的软件工作而调用的工具之间的交互。我们希望这篇文章能让您对我们的代理(或“框架”,harness)在利用大型语言模型(LLM)方面所扮演的角色有一个清晰的认识。
在深入研究之前,我们快速说明一下术语:在 OpenAI,“Codex”涵盖了一套软件代理产品,包括 Codex CLI、Codex Cloud 和 Codex VS Code 扩展。本文重点介绍 Codex 框架(harness),它提供了所有 Codex 体验的基础核心代理循环和执行逻辑,并通过 Codex CLI 呈现出来。为方便起见,我们将交替使用“Codex”和“Codex CLI”这两个术语。
模型推理(Model inference)
每个 AI 代理的核心就是所谓的“代理循环”。代理循环的简化图示如下:
首先,代理从用户那里获取输入,并将其纳入为提示词(prompt)准备给模型的文本指令集中。
下一步是通过向模型发送指令并要求其生成响应来查询模型,这个过程称为推理(inference)。在推理过程中,文本提示首先被翻译成一串输入令牌(opens in a new window)——索引到模型词汇表的整数。然后使用这些令牌对模型进行采样,生成一串新的输出令牌。
输出令牌被翻译回文本,成为模型的响应。由于令牌是增量产生的,这种翻译可以在模型运行时发生,这就是为什么许多基于 LLM 的应用程序会显示流式输出。在实践中,推理通常被封装在一个操作文本的 API 后面,从而抽象化了令牌化的细节。
作为推理步骤的结果,模型要么(1)对用户的原始输入产生最终响应,要么(2)请求代理应执行的工具调用(例如,“运行 ls 并报告输出”)。在情况(2)下,代理执行工具调用,并将其输出附加到原始提示词中。此输出用于生成新的输入,用于重新查询模型;然后代理可以考虑这些新信息并再次尝试。
这个过程会重复,直到模型停止发出工具调用,而是为用户生成一条消息(在 OpenAI 模型中称为助手消息,assistant message)。在许多情况下,此消息直接回答了用户的原始请求,但也可能是向用户提出的后续问题。
由于代理可以执行修改本地环境的工具调用,“输出”不仅仅局限于助手消息。在许多情况下,软件代理的主要输出是它在您的机器上编写或编辑的代码。尽管如此,每一轮对话总是以一条助手消息结束——例如“我已添加您要求的 architecture.md”——这标志着代理循环中的一个终止状态。从代理的角度来看,它的工作已经完成,控制权返回给用户。
图中所示的从用户输入到代理响应的旅程被称为对话的一轮(在 Codex 中称为线程,thread)。尽管这个对话轮次可能包含 模型推理 和 工具调用 之间的多次迭代)。每次您向现有对话发送新消息时,对话历史记录都会作为新一轮提示词的一部分包含在内,其中包含先前轮次的消息和工具调用:
这意味着随着对话的增长,用于模型采样的提示词的长度也会增加。这个长度很重要,因为每个模型都有一个上下文窗口(context window),即单次推理调用所能使用的最大令牌数。请注意,此窗口包括输入和输出令牌。正如您可能想象的那样,代理可能会决定在一个轮次中进行数百次工具调用,从而可能耗尽上下文窗口。因此,上下文窗口管理是代理的众多职责之一。现在,让我们深入了解 Codex 如何运行代理循环。
模型推理
Codex CLI 通过向Responses API(opens in a new window)发送 HTTP 请求来运行模型推理。我们将研究信息如何通过 Codex 流动,Codex 使用 Responses API 来驱动代理循环。
Codex CLI 使用的 Responses API 端点是可配置的(opens in a new window),因此它可以与任何实现 Responses API 的端点(opens in a new window)一起使用:
- 当使用 ChatGPT 登录(opens in a new window)与 Codex CLI 时,它使用
https://chatgpt.com/backend-api/codex/responses作为端点 - 当使用 API 密钥身份验证(opens in a new window)与 OpenAI 托管模型时,它使用
https://api.openai.com/v1/responses作为端点 - 当使用
--oss运行 Codex CLI 以使用gpt-oss和ollama 0.13.4+(opens in a new window)或LM Studio 0.3.39+(opens in a new window)时,它默认为在本地计算机上运行的http://localhost:11434/v1/responses - Codex CLI 可以与 Azure 等云提供商托管的 Responses API 配合使用
让我们探讨一下 Codex 如何为对话中的第一次推理调用构建提示词。
构建初始提示词
作为最终用户,当您查询 Responses API 时,您不会逐字指定用于模型采样的提示词。相反,您在查询中指定各种输入类型,Responses API 服务器决定如何将此信息构造为模型设计用于消费的提示词。您可以将提示词视为“项目列表”;本节将解释您的查询如何转换为该列表。
在初始提示词中,列表中的每个项目都与一个角色(role)相关联。role 指示相关内容应具有多少权重,并且是以下值之一(按优先级递减):system、developer、user、assistant。
Responses API 接受一个带有许多参数的 JSON 负载。我们将重点关注这三个参数:
instructions(opens in a new window):插入到模型上下文中的 system(或 developer)消息tools(opens in a new window):模型在生成响应时可能调用的工具列表input(opens in a new window):文本、图像或文件输入的列表
在 Codex 中,instructions 字段从 ~/.codex/config.toml 中的model_instructions_file(opens in a new window)(如果指定)读取;否则,使用与模型关联的base_instructions(opens in a new window)。模型特定的指令存在于 Codex 仓库中,并捆绑到 CLI 中(例如,gpt-5.2-codex_prompt.md(opens in a new window))。
tools 字段是工具定义列表,符合 Responses API 定义的模式(schema)。对于 Codex 而言,这包括 Codex CLI 提供的工具、应提供给 Codex 的 Responses API 提供的工具,以及用户提供的工具,通常是通过 MCP 服务器:
JavaScript
1[2 // Codex's 默认 shell 工具,用于在本地生成新进程。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 内置计划工具。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 // Responses API 提供的Web 搜索工具。34 {35 "type": "web_search",36 "external_web_access": false37 },38
39 // MCP 服务器用于获取用户 ~/.codex/config.toml 中配置的天气信息。40 // 的天气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 负载的 input 字段是一个项目列表。在添加用户消息之前,Codex 将以下项目插入(opens in a new window)到 input 中:
1. 一条带有 role=developer 的消息,描述了仅适用于 Codex 提供的 shell工具(在 tools 部分定义)的沙箱环境。也就是说,其他工具(例如从 MCP 服务器提供的工具)不受 Codex 沙箱化保护,需要自行执行保护措施。
该消息是根据模板构建的,其中关键内容来自捆绑在 Codex CLI 中的 Markdown 片段,例如 workspace_write.md(opens in a new window) 和 on_request.md(opens in a new window):
纯文本
1<permissions instructions>2 - description of the sandbox explaining file permissions and network access3 - instructions for when to ask the user for permissions to run a shell command4 - list of folders writable by Codex, if any5</permissions instructions>
2.(可选)一条带有 role=developer 的消息,其内容是用户 config.toml 文件中读取的 developer_instructions 值。
3.(可选)一条带有 role=user 的消息,其内容是“用户指令”,这些指令并非来自单个文件,而是从多个来源聚合(opens in a new window)而成。一般来说,更具体的指令会出现在后面:
$CODEX_HOME中AGENTS.override.md和AGENTS.md的内容- 受限额(默认 32 KiB),查找从
cwd的 Git/项目根目录(如果存在)到cwd本身的所有文件夹:添加AGENTS.override.md、AGENTS.md或config.toml中project_doc_fallback_filenames指定的任何文件名内容 - 如果配置了任何技能(opens in a new window):
- 关于技能的简短前言
- 每个技能的技能元数据(opens in a new window)
- 关于如何使用技能的(opens in a new window)一部分
4. 一条带有 role=user 的消息,描述了代理当前操作的本地环境。这指定了当前工作目录和用户的 shell(opens in a new window):
纯文本
1<environment_context>2 <cwd>/Users/mbolin/code/codex5</cwd>3 <shell>zsh</shell>4</environment_context>
一旦 Codex 完成上述所有计算来初始化 input,它就会附加用户消息以开始对话。
前面的示例侧重于每条消息的内容,但请注意,input 的每个元素都是一个 JSON 对象,具有如下的 type、role(opens in a new window)和 content:
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 标头和查询参数)使用 Authorization 标头发出 HTTP POST 请求。
当 OpenAI Responses API 服务器接收到请求时,它会使用 JSON 按如下方式推导出模型的提示词(为确保准确性,自定义的 Responses API 实现可能会做出不同的选择):
正如您所见,提示词中前三个项目的顺序由服务器决定,而不是客户端。话虽如此,在这三项中,只有系统消息的内容也是由服务器控制的,因为 tools 和 instructions 是由客户端确定的。紧随其后的是 JSON 负载中的 input,以完成提示词。
现在我们有了提示词,我们就可以对模型进行采样了。
第一轮对话
这个对 Responses API 的 HTTP 请求启动了 Codex 中对话的第一个“轮次”。服务器以服务器发送事件(Server-Sent Events,SSE(opens in a new window))流进行响应。每个事件的 data 是一个 JSON 负载,其中包含一个以 "response" 开头的 "type",可能如下所示(完整的事件列表可以在我们的API 文档(opens in a new window)中找到):
纯文本
1data: {"type":"response.reasoning_summary_text.delta","delta":"ah ", ...}2data: {"type":"response.reasoning_summary_text.delta","delta":"ha!", ...}3data: {"type":"response.reasoning_summary_text.done", "item_id":...}4data: {"type":"response.output_item.added", "item":{...}}5data: {"type":"response.output_text.delta", "delta":"forty-", ...}6data: {"type":"response.output_text.delta", "delta":"two!", ...}7data: {"type":"response.completed","response":{...}}
Codex 消费事件流(opens in a new window)并将其重新发布为可供客户端使用的内部事件对象。诸如 respon... [内容被截断]
🚀 想要体验更好更全面的AI调用?
欢迎使用青云聚合API,约为官网价格的十分之一,支持300+全球最新模型,以及全球各种生图生视频模型,无需翻墙高速稳定,文档丰富,小白也可以简单操作。
评论区