📢 转载信息
原文链接:https://openai.com/index/unrolling-the-codex-agent-loop
原文作者:Michael Bolin, Member of the Technical Staff
Codex CLI(opens in a new window) 是我们跨平台的本地软件智能体,旨在安全高效地在您的机器上生成高质量、可靠的软件变更。自我们 4 月首次发布 CLI 以来,我们在构建世界级的软件智能体方面学到了很多东西。为了阐述这些见解,这是我们计划中一个持续系列文章的第一篇,我们将探讨 Codex 工作原理的各个方面以及来之不易的经验教训。(如需了解 Codex CLI 构建方式的更细粒度视图,请查看我们在 GitHub 上的开源代码库 https://github.com/openai/codex(opens in a new window)。如果您想了解更多信息,我们设计决策的许多细微差别都记录在 GitHub issues 和 pull requests 中。)
为了开个好头,我们将重点关注 智能体循环(agent loop),这是 Codex CLI 的核心逻辑,负责协调用户、模型以及模型为执行有意义的软件工作而调用的工具之间的交互。我们希望这篇文章能让您对我们的智能体(或“框架”)在利用 LLM 方面所扮演的角色有一个很好的认识。
在深入探讨之前,快速说明一下术语:在 OpenAI,“Codex” 涵盖了一套软件智能体产品,包括 Codex CLI、Codex Cloud 和 Codex VS Code 扩展。本文重点介绍 Codex 框架(harness),它提供了所有 Codex 体验的基础核心智能体循环和执行逻辑,并通过 Codex CLI 呈现出来。为方便起见,我们将 “Codex” 和 “Codex CLI” 互换使用。
智能体循环
在每个 AI 智能体的核心,都有一个被称为“智能体循环”的东西。智能体循环的简化图示如下:
首先,智能体会从用户那里获取 输入(input),并将其包含在一组为模型准备的文本指令中,这些指令被称为 提示(prompt)。
下一步是通过将指令发送给模型并要求其生成响应来查询模型,这个过程称为 推理(inference)。在推理过程中,文本提示首先被翻译成一个输入 标记(tokens)序列——即模型词汇表的索引整数。然后使用这些标记来采样模型,产生一个新的输出标记序列。
输出标记被翻译回文本,成为模型的响应。由于标记是增量生成的,因此这种翻译可以在模型运行时发生,这就是为什么许多基于 LLM 的应用程序会显示流式输出。在实践中,推理通常被封装在一个操作文本的 API 后面,从而抽象化了标记化的细节。
作为推理步骤的结果,模型要么(1)对用户的原始输入产生最终响应,要么(2)请求智能体预期执行的工具调用(例如,“运行 ls 并报告输出”)。在情况 (2) 下,智能体会执行工具调用,并将其输出附加到原始提示中。此输出用于生成新的输入,该输入将用于重新查询模型;然后智能体可以考虑此新信息并再次尝试。
这个过程会重复,直到模型停止发出工具调用,转而为用户生成一条消息(在 OpenAI 模型中称为助手消息)。在许多情况下,此消息直接回答了用户的原始请求,但也可能是一个需要向用户提出的后续问题。
由于智能体可以执行修改本地环境的工具调用,因此其“输出”不仅仅限于助手消息。在许多情况下,软件智能体的主要输出是它在您的机器上编写或编辑的代码。尽管如此,每一轮(turn)的结束总会伴随着一条助手消息——例如“我已添加您要求的 architecture.md”——这在智能体循环中标志着一个终止状态。从智能体的角度来看,其工作已完成,控制权返回给用户。
图中从用户输入到智能体响应的整个过程被称为对话中的一个轮次(turn)(在 Codex 中称为线程(thread))。虽然这个对话轮次可能包含模型推理和工具调用之间的多次迭代)。每次您向现有对话发送新消息时,对话历史记录都会作为新轮次提示的一部分包含进去,其中包括先前轮次的消息和工具调用:
这意味着随着对话的增长,用于采样模型的提示的长度也会增加。这个长度很重要,因为每个模型都有一个上下文窗口(context window),即单个推理调用所能使用的最大标记数。请注意,此窗口同时包括输入和输出标记。正如您可以想象的那样,一个智能体在一轮中可能会决定进行数百次工具调用,从而可能耗尽上下文窗口。因此,上下文窗口管理是智能体的众多职责之一。现在,让我们深入了解 Codex 如何运行智能体循环。
模型推理
Codex CLI 通过向 Responses API 发送 HTTP 请求来运行模型推理。我们将研究信息如何在 Codex 中流动,Codex 使用 Responses API 来驱动智能体循环。
Codex CLI 使用的 Responses API 端点是可配置的,因此它可以与任何实现 Responses API 的端点一起使用:
- 当使用 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 表示相关内容应具有的权重,并且是以下值之一(按优先级递减):system、developer、user、assistant。
Responses API 接受带有许多参数的 JSON 载荷。我们将重点关注这三个:
instructions(opens in a new window):插入到模型上下文中的系统(或开发者)消息tools(opens in a new window):模型在生成响应时可能调用的工具列表input(opens in a new window):发送给模型的文本、图像或文件输入的列表
在 Codex 中,如果指定了 instructions 字段,则会从 ~/.codex/config.toml 文件中的 model_instructions_file 读取;否则,将使用与模型关联的 base_instructions。特定于模型的指令位于 Codex 仓库中,并捆绑到 CLI 中(例如,gpt-5.2-codex_prompt.md)。
tools 字段是工具定义列表,它遵循 Responses API 定义的Schema。对于 Codex 而言,这包括 Codex CLI 提供的工具、应提供给 Codex 的 Responses API 提供的工具,以及用户通常通过 MCP 服务器提供的工具:
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 提供的网页搜索工具。34 {35 "type": "web_search",36 "external_web_access": false37 },38
39 // MCP 服务器,用于获取用户 40 // ~/.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 载荷中的 input 字段是一个项目列表。Codex 在添加用户消息之前,插入以下项目(opens in a new window)到 input 中:
1. 一个具有 role=developer 的消息,它描述了仅适用于 Codex 提供的 shell工具 的沙箱,该工具在 tools 部分定义。也就是说,其他工具(例如从 MCP 服务器提供的工具)不受 Codex 的沙箱限制,并负责执行自己的安全护栏。
此消息是根据模板构建的,其中关键内容来自捆绑到 Codex CLI 中的 Markdown 片段,例如 workspace_write.md 和 on_request.md:
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,如下所示:
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 构建完整的 JSON 载荷发送给 Responses API 后,它会根据 ~/.codex/config.toml 中 Responses API 端点的配置方式,使用 Authorization 标头发出 HTTP POST 请求(如果指定,还会添加额外的 HTTP 标头和查询参数)。
当 OpenAI Responses API 服务器收到请求时,它使用 JSON 如下推导出模型的提示(请注意,Responses API 的自定义实现可以选择不同的方式):
正如您所见,提示中前三个项目的顺序由服务器决定,而不是客户端。话虽如此,在这三项中,只有系统消息的内容也由服务器控制,因为 tools 和 instructions 是由客户端决定的。随后是 JSON 载荷中的 input,以完成提示。
现在我们有了提示,我们就可以采样模型了。
第一轮
向 Responses API 发出的此 HTTP 请求启动了 Codex 对话的第一个“轮次”。服务器通过服务器发送事件(Server-Sent Events,SSE)流进行响应。每个事件的 data 是一个 JSON 载荷,其中包含一个以 "response" 开头的 "type",可能如下所示(事件的完整列表可以在我们的API 文档中找到):
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+全球最新模型,以及全球各种生图生视频模型,无需翻墙高速稳定,文档丰富,小白也可以简单操作。
评论区