摘要

1) 一句话总结

Codex App Server 是一个基于双向 JSON-RPC 协议的底层接口,它将核心的 Codex 智能体引擎统一暴露给 Web、IDE、CLI 和桌面端等多种客户端,以支持丰富的流式交互与状态管理。

2) 关键要点

  • 架构演进决策:团队最初尝试将 Codex 作为 MCP 服务器公开,但因难以维护 IDE 所需的复杂语义而放弃,最终选择构建向后兼容的双向 JSON-RPC 协议。
  • 核心组件:App Server 是一个长期运行的进程,包含四个主要组件:stdio 读取器、Codex 消息处理器、线程管理器和核心线程。
  • 引擎职责:除了核心智能体循环,引擎还负责线程生命周期与持久化、配置与身份验证(如“使用 ChatGPT 登录”),以及在沙盒中执行工具和连接扩展。
  • 三大对话原语
    • Item(项目):最小原子单位(如消息、diff),具有明确的生命周期(starteddelta 流式传输、completed)。
    • Turn(轮次):由用户输入发起的一个智能体工作单元,包含一系列 Items。
    • Thread(线程):持久化的会话容器,包含多个 Turns,支持恢复和分支。
  • 本地与 IDE 集成:客户端(如 VS Code、Xcode)将 App Server 作为子进程启动,通过 stdio 通信。向后兼容性允许服务端独立于客户端进行升级。
  • Web 端集成:App Server 在容器化工作节点中运行,通过 HTTP 和 SSE 与浏览器通信。状态保存在服务端,确保标签页关闭后任务仍能继续。
  • TUI 重构计划:原有的同进程 TUI 将被重构为基于 App Server 的架构,以解锁连接远程机器和计算资源的新工作流。
  • 集成方案推荐:官方默认推荐使用 App Server 以获取完整功能;其他方案包括 Codex Exec(适用于 CI/自动化流水线)和 Codex SDK(适用于 TypeScript 编程式控制)。

3) 风险与不足(基于原文明确提及)

  • MCP 与通用协议的局限性:将 Codex 作为 MCP 服务器或使用跨提供商协议时,只能获取公共功能子集,难以映射 Codex 特有的丰富会话语义(如 diff 更新)。
  • App Server 的集成成本:使用 App Server 的主要门槛是集成工作量,开发者需要在其使用的语言中自行构建客户端 JSON-RPC 绑定。
  • Codex SDK 的局限性:由于发布较早,当前的 Codex SDK 支持的语言较少,且功能覆盖面不如 App Server 完整。
  • Web 会话的脆弱性:Web 标签页具有短暂性(易关闭或断网),不能作为长期运行任务的真实数据源,必须依赖服务端来保留状态和进度。

正文

OpenAI 的编程智能体 Codex 存在于许多不同的平台中:Web 应用、CLI、IDE 扩展以及全新的 Codex macOS 应用。在底层,它们都由同一个 Codex 引擎(harness)驱动——这是构成所有 Codex 体验基础的智能体循环与逻辑。连接它们的关键纽带是什么?正是 Codex App Server,一个对客户端友好、双向的 JSON-RPC API。

在这篇文章中,我们将介绍 Codex App Server;分享我们迄今为止学到的最佳实践,帮助您将 Codex 的能力引入您的产品,从而让您的用户大幅提升工作流效率。我们将涵盖 App Server 的架构和协议,它如何与不同的 Codex 平台集成,以及利用 Codex 的技巧——无论您是想将 Codex 变成代码审查员、SRE 智能体,还是编程助手。

App Server 的起源

在深入探讨架构之前,了解 App Server 的背景会很有帮助。最初,App Server 只是在不同产品间复用 Codex 引擎的一种实用方法,后来逐渐演变成了我们的标准协议。

Codex CLI 最初是一个 TUI(终端用户界面),这意味着用户通过终端访问 Codex。当我们构建 VS Code 扩展(一种对 IDE 更友好的 Codex 智能体交互方式)时,我们需要一种方法来使用相同的引擎,以便在不重新实现它的情况下,从 IDE UI 驱动相同的智能体循环。这意味着需要支持超越简单“请求/响应”的丰富交互模式,例如探索工作区、在智能体推理时流式传输进度以及输出差异(diffs)。

我们最初尝试将 Codex 作为 MCP 服务器公开,但事实证明,以一种对 VS Code 有意义的方式维护 MCP 语义非常困难。相反,我们引入了一种镜像 TUI 循环的 JSON-RPC 协议,这成为了 App Server 的非正式第一个版本。当时,我们并没有期望其他客户端会依赖 App Server,因此它并没有被设计为一个稳定的 API。

随着接下来几个月 Codex 采用率的增长,内部团队和外部合作伙伴希望能够将相同的引擎嵌入到他们自己的产品中,以加速其用户的软件开发工作流。例如,JetBrains 和 Xcode 需要 IDE 级别的智能体体验,而 Codex 桌面应用需要并行编排多个 Codex 智能体。这些需求促使我们设计了一个平台接口,使我们的产品和合作伙伴的集成都能在未来安全地依赖它。它需要易于集成且向后兼容,这意味着我们可以在不破坏现有客户端的情况下演进协议。

接下来,我们将详细介绍我们是如何设计架构和协议的,以便不同的客户端可以使用相同的引擎。

深入 Codex 引擎内部

首先,让我们放大看看 Codex 引擎内部的内容,以及 Codex App Server 是如何将其暴露给客户端的。在我们上一篇关于 Codex 的文章中,我们剖析了协调用户、模型和工具之间交互的核心智能体循环。这是 Codex 引擎的核心逻辑,但完整的智能体体验还包括更多内容:

  • 线程生命周期与持久化:线程(Thread)是用户与智能体之间的 Codex 对话。Codex 会创建、恢复、分支和归档线程,并持久化事件历史,以便客户端可以重新连接并渲染一致的时间线。
  • 配置与身份验证:Codex 负责加载配置、管理默认设置,并运行诸如“使用 ChatGPT 登录”之类的身份验证流程,包括凭据状态。
  • 工具执行与扩展:Codex 在沙盒中执行 Shell/文件工具,并连接 MCP 服务器和技能(skills)等集成,使它们能够在一致的策略模型下参与智能体循环。

我们在这里提到的所有智能体逻辑(包括核心智能体循环)都存在于 Codex CLI 代码库中被称为“Codex core”的部分。Codex core 既是一个包含所有智能体代码的库,也是一个可以启动以运行智能体循环并管理单个 Codex 线程(对话)持久化的运行时。

为了发挥作用,Codex 引擎需要能够被客户端访问。这就是 App Server 发挥作用的地方。

App Server 既是客户端和服务器之间的 JSON-RPC 协议,也是一个托管 Codex core 线程的长期运行进程。App Server 进程包含四个主要组件:标准输入输出读取器(stdio reader)、Codex 消息处理器、线程管理器和核心线程。线程管理器为每个线程启动一个核心会话,然后 Codex 消息处理器直接与每个核心会话通信,以提交客户端请求并接收更新。

一个客户端请求可能会产生许多事件更新,正是这些详细的事件使我们能够在 App Server 之上构建丰富的 UI。此外,stdio reader 和 Codex 消息处理器充当了客户端和 Codex core 线程之间的翻译层。它们将客户端的 JSON-RPC 请求翻译为 Codex core 操作,监听 Codex core 的内部事件流,然后将这些底层事件转换为一小组稳定的、可直接用于 UI 的 JSON-RPC 通知。

客户端和 App Server 之间的 JSON-RPC 协议是完全双向的。一个典型的线程包含一个客户端请求和许多服务器通知。此外,当智能体需要输入(如审批)时,服务器可以发起请求,然后暂停当前轮次,直到客户端响应。

对话原语

接下来,我们将分解对话原语,即 App Server 协议的构建块。为智能体循环设计 API 是棘手的,因为用户与智能体的交互不是简单的请求/响应。一个用户请求可以展开为一系列结构化的操作,客户端需要忠实地表示这些操作:用户的输入、智能体的增量进度、沿途产生的工件(例如 diffs)。为了使这种交互流易于集成并在不同 UI 中保持弹性,我们确定了三个具有明确边界和生命周期的核心原语:

  • Item(项目):Item 是 Codex 中输入/输出的最小原子单位。Item 具有类型(例如,用户消息、智能体消息、工具执行、审批请求、diff),并且每个 Item 都有明确的生命周期:
    • item/started:项目开始时。
    • 可选的 item/*/delta 事件:随着内容流式传输而产生(用于流式项目类型)。
    • item/completed:项目以其最终有效载荷结束时。 这种生命周期允许客户端在 started 时立即开始渲染,在 delta 时流式传输增量更新,并在 completed 时完成。
  • Turn(轮次):Turn 是由用户输入发起的一个智能体工作单元。它在客户端提交输入(例如,“运行测试并总结失败原因”)时开始,并在智能体完成该输入的输出生成时结束。一个 Turn 包含一系列 Items,代表中间步骤和沿途产生的输出。
  • Thread(线程):Thread 是用户与智能体之间正在进行的 Codex 会话的持久化容器。它包含多个 Turns。线程可以被创建、恢复、分支和归档。线程历史会被持久化,以便客户端可以重新连接并渲染一致的时间线。

在对话开始时,客户端和服务器需要建立 initialize 握手。客户端必须在任何其他方法之前发送一个 initialize 请求,服务器则以响应进行确认。这使得服务器有机会宣告其功能,并让双方在真正开始工作之前就协议版本、功能标志和默认设置达成一致。以下是 VS Code 扩展的示例请求:

{
  "method": "initialize",
  "id": 0,
  "params": {
    "clientInfo": {
      "name": "codex_vscode",
      "title": "Codex VS Code Extension",
      "version": "0.1.0"
    }
  }
}

这是服务器返回的内容:

{
  "id": 0,
  "result": {
    "userAgent": "codex_vscode/0.94.0-alpha.7 (Mac OS 26.2.0; arm64) vscode/2.4.22 (codex_vscode; 0.1.0)"
  }
}

当客户端发出新请求时,它将首先创建一个 Thread,然后创建一个 Turn。服务器将发回进度通知(thread/startedturn/started)。它还会将注册为 Items 的输入(如用户消息)发回。

工具调用也会作为 Items 发回给客户端。此外,服务器在运行操作之前可能会通过发送服务器请求来要求客户端审批。审批将暂停该 Turn,直到客户端回复“允许”或“拒绝”。

最后,服务器发送一条智能体消息,然后以 turn/completed 结束该 Turn。智能体消息的 delta 事件会将消息片段流式传输回来,直到消息以 item/completed 最终完成。

如果您想查看完整轮次的 JSON,可以从 Codex CLI 仓库运行测试客户端:

codex debug app-server send-message-v2 "run tests and summarize failures"

与客户端集成

现在,让我们看看不同的客户端平台如何通过 App Server 嵌入 Codex。我们将涵盖三种模式:本地应用和 IDE、Codex Web 运行时以及 TUI。

在所有这三种模式中,传输方式都是基于 stdio 的 JSON-RPC (JSONL)。JSON-RPC 使得用您选择的语言构建客户端绑定变得非常简单。Codex 平台和合作伙伴集成已经用 Go、Python、TypeScript、Swift 和 Kotlin 等语言实现了 App Server 客户端。对于 TypeScript,您可以通过运行以下命令直接从 Rust 协议生成定义:

codex app-server generate-ts

对于其他语言,您可以生成一个 JSON Schema 包,并将其输入到您首选的代码生成器中:

codex app-server generate-json-schema

本地应用与 IDE

本地客户端通常会捆绑或获取特定平台的 App Server 二进制文件,将其作为长期运行的子进程启动,并保持一个双向的 stdio 通道打开以进行 JSON-RPC 通信。例如,在我们的 VS Code 扩展和桌面应用中,交付的工件包含特定平台的 Codex 二进制文件,并固定在经过测试的版本上,以便客户端始终运行我们验证过的确切代码。

并非每个集成都能频繁发布客户端更新。像 Xcode 这样的合作伙伴通过保持客户端稳定并在需要时允许其指向较新的 App Server 二进制文件来解耦发布周期。这样,他们就可以采用服务器端的改进(例如,Codex core 中更好的自动压缩或新支持的配置键)并推出错误修复,而无需等待客户端发布。App Server 的 JSON-RPC 接口被设计为向后兼容,因此旧客户端可以安全地与新服务器通信。

Codex Web

Codex Web 使用 Codex 引擎,但将其运行在容器环境中。工作节点(worker)配置一个带有已检出工作区的容器,在其中启动 App Server 二进制文件,并维护一个长期运行的基于 stdio 的 JSON-RPC 通道。Web 应用(在用户的浏览器标签页中运行)通过 HTTP 和 SSE 与 Codex 后端通信,流式传输由工作节点生成的任务事件。这保持了浏览器端 UI 的轻量级,同时仍然为我们提供了一致的桌面和 Web 运行时。

由于 Web 会话是短暂的(标签页关闭、网络断开),Web 应用不能作为长期运行任务的真实数据源。将状态和进度保留在服务器上意味着即使标签页消失,工作也会继续。流式协议和保存的线程会话使得新会话可以轻松重新连接、从中断处继续并赶上进度,而无需在客户端重建状态。

TUI / Codex CLI

从历史上看,TUI 是一个“原生”客户端,它与智能体循环在同一个进程中运行,并直接与 Rust 核心类型通信,而不是使用 app-server 协议。这使得早期迭代速度很快,但也使 TUI 成为一个特殊情况的平台。

现在 App Server 已经存在,我们计划重构 TUI 以使用它,使其表现得像任何其他客户端一样:启动一个 App Server 子进程,通过 stdio 使用 JSON-RPC 通信,并渲染相同的流式事件和审批。这解锁了新的工作流,TUI 可以连接到在远程机器上运行的 Codex 服务器,使智能体靠近计算资源,即使笔记本电脑休眠或断开连接也能继续工作,同时仍然在本地提供实时更新和控制。

选择合适的协议

Codex App Server 将是我们未来维护的一流集成方法,但也存在其他功能更有限的方法。默认情况下,我们建议客户端使用 Codex App Server 与 Codex 集成,但了解不同的集成方法及其优缺点也是值得的。以下是驱动 Codex 的最常见方法以及它们各自的适用场景。

JSON-RPC 协议

  • 将 Codex 作为 MCP 服务器 运行 codex mcp-server 并从任何支持 stdio 服务器的 MCP 客户端(例如 OpenAI Agents SDK)连接。如果您已经有一个基于 MCP 的工作流并希望将 Codex 作为可调用工具调用,这是一个很好的选择。缺点是您只能获得 MCP 暴露的功能,因此依赖于更丰富会话语义(例如 diff 更新)的 Codex 特定交互可能无法通过 MCP 端点干净地映射。
  • 跨提供商智能体引擎协议 一些生态系统提供了一个可移植的接口,可以针对多个模型提供商和运行时。如果您想要一个协调多个智能体的抽象,这可能是一个很好的选择。权衡之处在于,这些协议通常收敛于功能的公共子集,这可能使更丰富的交互难以表示,特别是当特定于提供商的工具和会话语义很重要时。这个领域发展迅速,随着我们弄清楚表示现实世界智能体工作流的最佳原语,我们预计会出现更通用的标准。
  • Codex App Server 当您希望将完整的 Codex 引擎作为稳定、对 UI 友好的事件流暴露时,请选择 App Server。您不仅可以获得智能体循环的完整功能,还可以获得其他支持功能,如“使用 ChatGPT 登录”、模型发现和配置管理。主要成本是集成工作,因为您需要在您使用的语言中构建客户端 JSON-RPC 绑定。然而在实践中,如果您向 Codex 提供 JSON schema 和文档,它能够完成大部分繁重的工作。与我们合作的许多团队都能够使用 Codex 快速完成有效的集成。

其他嵌入 Codex 的方法

  • Codex Exec 一种轻量级、可编写脚本的 CLI 模式,用于一次性任务和 CI 运行。它非常适合自动化和流水线,在这些场景中,您希望单个命令以非交互方式运行至完成,为日志流式传输结构化输出,并以明确的成功或失败信号退出。
  • Codex SDK 一个 TypeScript 库,用于从您自己的应用程序中以编程方式控制本地 Codex 智能体。当您想要一个用于服务器端工具和工作流的原生库接口,而又不想构建单独的 JSON-RPC 客户端时,它是最佳选择。由于它比 App Server 发布得早,目前支持的语言较少,覆盖面也较小。如果开发者有兴趣,我们可能会添加封装 App Server 协议的额外 SDK,以便团队可以在不编写 JSON-RPC 绑定的情况下覆盖更多的引擎功能。

展望未来

在这篇文章中,我们分享了我们如何设计与智能体交互的新标准,以及如何将 Codex 引擎转变为一个稳定、对客户端友好的协议。我们介绍了 App Server 如何暴露 Codex core,让客户端驱动完整的智能体循环,并为包括 TUI、本地 IDE 集成和 Web 运行时在内的广泛平台提供动力。

如果这激发了您将 Codex 集成到自己工作流中的想法,那么 App Server 值得一试。所有源代码都位于 Codex CLI 开源仓库中。欢迎分享您的反馈和功能请求。我们很高兴能听到您的声音,并继续让智能体变得对每个人都更易于使用。

关联主题