OpenAI 最近开始频繁使用一个容易引起误解的词:Codex harness。

它不是一个需要单独安装的新产品,也不是 Codex CLI 改了名字。OpenAI 给出的最小定义是:harness 是围绕模型运行的执行系统,负责保存会话状态、组织工具循环、执行命令、施加 sandbox 与 approval 策略,并把过程事件交给上层界面。Codex CLI 则是这套系统最早、最直接的终端产品形态,同时也是它的发行入口和源码宿主。

因此,两者最准确的关系不是“同一个东西”,也不是“前端和后端”这么简单,而是:

Codex CLI 包含并暴露 Codex harness;harness 可以脱离 TUI,被 codex exec、App Server、SDK 和其他 Codex 客户端复用。

这个判断可以同时解释几个看似矛盾的事实:为什么 OpenAI 会说 harness “通过 Codex CLI 暴露”,又说它驱动 Codex App、IDE 与 Web;为什么开源仓库叫 openai/codex,官方文档却称其中一部分为 Codex Core;为什么 TypeScript SDK 是一个库,底层仍要启动 codex 进程。

本文以 2026 年 8 月 24 日的官方文档和 openai/codex 主干源码为准,源码固定在提交 068c49f075cf287a1fe7d1ee36cf005efac922e7main 会继续变化,文中所有“当前实现”都指这个快照。

先拆掉三个同名层

Codex 的命名困难,来自产品名、发行物和内部模块共用了同一组词。

名称 所在层 更准确的含义
Codex 产品族 覆盖终端、桌面、IDE、Web/Cloud 等体验的总称
Codex CLI 终端产品与发行物 codex 命令、TUI、非交互模式及一组开发入口
Codex harness 运行时能力集合 会话、上下文、模型循环、工具、权限、sandbox、扩展与事件流
Codex Core 核心实现 Rust 业务逻辑库,也是单个 thread 的运行时核心
App Server 托管与协议边界 托管 Core thread,把内部事件转成稳定的客户端协议
TUI / codex exec 客户端表面 分别服务交互式终端和脚本、CI 等非交互场景
Codex SDK 编程接口 当前 TypeScript 实现通过子进程驱动 CLI

OpenAI 在 2026 年 1 月的 agent loop 文章中说明,Codex 是一组软件 Agent 产品,而 harness 提供所有体验底下的核心 agent loop 与执行逻辑,并通过 Codex CLI 呈现。到了 8 月的 Codex as a platform,定义更简洁:模型之外那套维持上下文、调用工具、暴露进度、处理失败和请求审批的 surrounding execution system,就是 harness。

这两个定义共同排除了两种常见误读。

第一,harness 不是模型。模型生成下一步动作,harness 控制输入上下文、可用工具、执行权限、工具结果回灌和会话保存。

第二,harness 不是 TUI。TUI 负责终端交互和渲染;关掉 TUI,Core、App Server、codex exec 仍然可以组成完整的 Agent 执行路径。

站内旧文曾把 Harness 概括为“把随机模型锁进可验证的箱体”。这个工程比喻便于理解,但 OpenAI 官方语境更窄,只指模型周围可复用的执行层。

另一个歧义来自 evaluation harness。本文讨论的是运行 Agent 的 harness,不是负责跑基准、计分和汇总结果的评测基础设施。两种含义的区别见《从模型评测到 Harness 评测》。

Codex CLI 为什么既“包含 Harness”又“只是一个客户端”

只看产品,Codex CLI 是用户在终端里安装和运行的本地 Agent。只看运行时,默认 TUI 又是 Codex harness 的一个客户端。这两句话并不冲突,因为“Codex CLI”至少指向三样东西。

作为发行物:CLI 把整套能力送到本机

npm 包 @openai/codex 暴露 codex 命令,平台对应的 Rust 二进制随发行物一起安装。这个二进制不是只有一层字符界面。当前 Subcommand 同时包含 execreviewapp-server、MCP、sandbox 等入口;没有子命令时才进入 TUI。

从这个角度说,CLI 是 harness 的分发容器和总启动器。用户安装 CLI,也把 Core、App Server、工具执行与 sandbox 相关组件装到了本机。

作为产品界面:默认 codex 是 TUI

官方 Developer commands 把无子命令的 codex 定义为交互式 TUI,把 codex exec 定义为非交互运行,把 codex app-server 定义为面向开发和调试的协议入口。

源码中的分发也很直接:exec 分支进入 codex_exec::run_main,默认交互分支进入 codex_tui::run_main。顶层 CLI 主要做参数解析、配置继承和入口分派,真正的 Agent 循环不在这里。

作为运行时客户端:TUI 通过 App Server 语义驱动 Core

当前源码里有一个很关键的 crate:codex-app-server-client。它的 README 明确列出两个使用者:codex-execcodex-tui

这个 client 不一定启动外部子进程。默认本地路径可以在同一进程内启动 App Server runtime,用 typed channel 传递请求和事件,但仍然保留 App Server 的请求、响应和通知语义。TUI 的 start_embedded_app_server 会调用 InProcessAppServerClient::startcodex exec 也在自己的主流程中启动同一个 in-process client

所以,“TUI 是 App Server 客户端”描述的是逻辑边界,不必然表示操作系统里总有两个进程,也不表示本地热路径一定把消息序列化成 JSON。当前 in-process 方案恰恰是在保留协议语义的同时去掉进程边界和外部序列化成本。

这也是理解 CLI 与 harness 关系最重要的一步:CLI 不是一层薄壳,但 TUI 也不再拥有一套独立的 Agent 内核。

当前源码里的四层结构

把仓库按职责压缩后,可以得到四层。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
产品入口
@openai/codex -> codex launcher
├── codex -> TUI
├── codex exec -> non-interactive client
└── codex app-server -> external protocol surface

客户端与协议
TUI / exec / IDE / App / custom client
-> app-server client
-> thread / turn / item / approval / event stream

Harness 核心
ThreadManager -> Codex Core session -> run_turn
-> context / compaction / model request
-> tool routing / approval / sandbox / extensions

外部系统
Responses API / local shell / filesystem / MCP servers / skills

Core:不是一个循环函数,而是单个 thread 的运行时

codex-core 的 README 把它称为 Codex 的业务逻辑,供不同 Rust UI 使用。官方 App Server 文章给出的定义更完整:Core 既是 Agent 代码所在的库,也是能够运行 agent loop、管理一个 thread 持久化状态的运行时。

这里的 thread 不是简单的消息数组。它要支持创建、恢复、分叉、归档和事件持久化,还要绑定配置、认证、模型、工具、MCP、skills、sandbox 和审批策略。Core 承担的是这些能力的实现,而不是屏幕上那一行行输出。

真正的循环在 run_turn 一带展开。它先处理待注入的上下文和压缩条件,组装历史输入,再调用模型客户端。模型若返回工具调用,Core 交给工具层执行,把结果记录回会话,随后继续下一次采样;模型只返回最终消息且没有待处理输入时,这个 turn 才结束。

这正是 harness 与普通 API wrapper 的分界。wrapper 可以把一段 prompt 发给模型;harness 必须维护一个可能跨越多次模型请求、工具调用和人工审批的状态机。

App Server:把 Core 变成可复用的客户端边界

Core 内部事件非常细,Rust 类型也会随实现重构。桌面应用、IDE 插件、Web worker 和第三方客户端不适合直接依赖这些内部类型。App Server 在两者之间建立了稳定边界。

官方定义里,App Server 既指协议,也指托管多个 Core thread 的长生命周期 runtime。它的 ThreadManager 为每个 thread 启动 Core session,message processor 把客户端请求翻译成 Core 操作,再把内部事件压缩成适合 UI 消费的通知。当前源码中的 ThreadManager 负责创建并在内存中维护 threads,App Server 的 message processor 则构造并持有它。

外部协议以 threadturnitem 为三个核心对象:

  • thread 是一段可恢复、可分叉的对话;
  • turn 从一次用户输入开始,到 Agent 消息或中断结束;
  • item 是用户消息、推理、命令、文件修改、审批等可流式更新的原子记录。

这些对象及生命周期定义在 App Server README 中。外部传输默认是 JSONL over stdio,也可以使用其他已支持的 transport;本地 TUI/exec 的 in-process 路径则使用 typed channel。

App Server 不是 harness 的全部。它不负责替代 Core 的工具循环,也不是模型服务。它解决的是“怎样让不同客户端稳定地驱动同一套 harness”。

TUI 与 exec:两个表面,一套会话语义

TUI 需要处理输入编辑、流式渲染、diff 展示、审批对话框、恢复会话等交互问题。exec 更关心标准输入输出、JSONL 事件、退出码和 CI 友好性。两者的产品行为不同,但都不应该各写一份 thread 生命周期、工具执行和权限逻辑。

共享 App Server client 后,区别被限制在客户端层:同一个 turn/start 语义可以被 TUI 渲染成不断更新的终端界面,也可以被 exec 输出成机器可读事件。业务运行时只维护一份。

SDK:目前是 CLI 的程序化外壳

TypeScript SDK 经常被误解成另一套 Codex runtime。官方仓库的 sdk/typescript/README.md 写得很明确:SDK 包装 @openai/codex 中的 CLI,启动子进程,并通过 stdin/stdout 交换 JSONL 事件。

源码里的执行器会调用 codex exec --experimental-json。因此,SDK 提供的是 TypeScript 对象、异步迭代和结构化结果等开发体验,harness 仍运行在被启动的 Codex CLI 进程中。

这与 App Server 的定位不同。SDK 适合从 TypeScript 程序发起本地 Agent 任务;App Server 适合需要完整 thread/turn/item 协议、双向审批和自定义 UI 的客户端。它们都复用 harness,但接入面和能力范围不一样。

一次输入究竟走过了什么

以默认交互式 codex 为例,一次用户输入会经过下面的链路。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
用户提交文本
-> TUI 将输入封装成 UserTurn
-> AppServerSession 构造 turn/start
-> App Server 定位或创建 thread
-> ThreadManager 把输入交给 Core session
-> Core 启动 RegularTask 和 run_turn
-> 组装 instructions、历史、工具定义与当前输入
-> 调用 Responses API
-> 收到工具调用
-> 检查 approval / sandbox / policy
-> 执行 shell、文件或扩展工具
-> 把工具结果追加到会话,再次调用模型
-> 生成最终消息或等待更多输入
-> App Server 发出 item / turn 事件
-> TUI 流式渲染

TUI 把用户提交转成 UserTurn,再由 AppServerSession::turn_start 发出 turn 请求。Core 收到输入后创建普通任务,进入 run_turn。每次采样前,它从 thread 历史构造模型输入;每次工具执行后,又把结果送回同一条会话链。

这里没有一个叫“harness main”的单一函数。harness 是跨 Core session、工具路由、sandbox、审批、持久化和协议事件协作出来的运行时行为。把它缩成“while 循环调用模型”会漏掉真正影响可靠性和产品体验的大部分机制。

2026 年 2 月的文章为什么和当前源码不完全一样

OpenAI 在 2026 年 2 月发布 Unlocking the Codex harness 时,明确写道:TUI 历史上在同一进程里直接依赖 Rust Core 类型,团队计划把它改造成普通 App Server 客户端。

如果只读这篇文章,会得出“TUI 还没有走 App Server”的结论。当前主干则已经出现共享的 codex-app-server-client,TUI 和 exec 都通过它启动或连接 App Server runtime。这说明文章描述的是当时架构和迁移计划,源码快照反映的是后续进展。

这次变化不是单纯重构调用方式。它消除了 TUI 的特殊通路,让交互式终端、非交互执行和外部客户端逐步收敛到同一套会话协议。TUI 仍可在进程内运行 App Server,因而保留本地启动效率;同时也可以把客户端和运行时分开,连接远端 App Server。

这类项目不适合用滚动的 main 链接证明长期结论。本文引用的源码都固定到 068c49f...,官方博客则保留发布日期。两者放在一起,才能区分设计意图、历史状态和当前实现。

“同一个 Harness”不等于“所有产品完全相同”

OpenAI 说 App、CLI、IDE 和 Web 由同一个 Codex harness 驱动,强调的是核心 agent loop 与执行逻辑复用,不是每个产品运行在完全相同的进程、机器和权限环境里。

本地 App 或 IDE 通常捆绑并启动经过版本固定的 Codex/App Server 二进制。Web 侧可以在带工作区的容器中运行 harness,由后端维持长任务和状态,再把事件流送到浏览器。TUI 可以嵌入 App Server,也可以连接独立或远端实例。

这些表面共享 Core 的会话、工具和事件语义,但外层应用仍然决定:

  • 工作区在哪里,进程运行在哪台机器;
  • 客户端怎样展示 diff、进度与审批;
  • 哪些工具、MCP server、skills 和产品上下文可用;
  • sandbox、网络与 approval 策略怎样配置;
  • 长任务和产品记录由谁保存、恢复和展示。

“同一个 harness”更接近共享浏览器内核,而不是所有浏览器产品完全一样。复用的是高成本、容易分叉的执行核心;产品仍可以拥有不同的交互、环境和治理边界。

开源的边界在哪里

“OpenAI 开源了 Codex harness”也容易被扩大成“Codex 整套产品都开源”。官方 Open Source 清单 给出的边界很清楚:Codex CLI、SDK 和 App Server 在 openai/codex 等仓库中公开;IDE extension 与 Codex cloud 没有开源。Codex cloud 使用的基础环境另有开源仓库,但这不等于云产品自身开源。

模型服务也不在这个 Apache-2.0 仓库里。开源的是模型周围的 Agent runtime 和若干接入面,不是远端推理服务、模型权重、云端控制面或所有第一方 UI。

这条边界反而说明了 harness 的工程价值。即使模型推理由远端服务完成,开发者仍能检查并修改本地执行层:上下文如何组织、工具怎样注册、命令如何隔离、事件怎样持久化、客户端如何接入。模型前后的关键控制逻辑不再是不可见黑箱。

本地、sandbox 与安全不能画等号

Codex CLI 在本地仓库中运行工具,但模型推理通常仍访问配置的 Responses API 端点。发送给模型的 instructions、会话上下文和工具结果可能包含仓库信息。不能因为 CLI 开源或命令在本地执行,就推导出“代码从不离开本机”。

sandbox 和 approval 也不是一句“安全运行”就能概括。sandbox 规定文件系统、网络和进程可以触及的技术边界;approval policy 决定跨越某些边界时是否需要用户或审查器授权。OpenAI 在 Running Codex safely 中把两者明确区分,并强调网络策略、身份凭据、集中配置和审计日志仍是独立控制面。

因此,harness 提供的是可执行、可配置的治理机制,不是无条件安全保证。MCP 工具和外部系统也必须执行自己的权限校验,不能把所有安全责任交给客户端弹窗。

为什么 OpenAI 要把 App Server 放在 CLI 仓库里

如果 harness 只服务 TUI,最省事的做法是让界面直接调用 Core,早期 Codex 也确实如此。问题在于,每出现一个新表面,都要重新解决流式事件、审批、diff、会话恢复、配置、认证和工具生命周期。

App Server 把这些能力整理成协议后,新增客户端不必重写 Agent。桌面应用可以专注多任务管理,IDE 可以专注编辑器上下文和内联 diff,Web 可以专注容器与长任务,终端可以专注低延迟交互。Core 的改进,例如上下文压缩或工具策略修复,也能通过共享 runtime 进入多个表面。

这解释了仓库组织看起来“比一个 CLI 大得多”的原因。openai/codex 已经不是单一 TUI 项目,而是 CLI 发行物、核心 harness、协议服务和部分 SDK 的共同开发仓库。仓库仍沿用 Codex CLI 的历史入口,但架构目标已经是可嵌入的平台。

什么时候直接用 CLI,什么时候越过 TUI

CLI 仍然是使用 Codex harness 成本最低的入口。

日常交互式开发直接运行 codex。终端界面已经处理输入、流式渲染、diff、审批和会话恢复,没有必要为这些能力重新写客户端。

脚本、CI 和一次性自动化使用 codex exec。它保留同一套 Core 能力,同时提供标准输出、结构化事件和明确退出状态。

TypeScript 服务希望以对象和异步迭代器控制任务,可以使用 Codex SDK,但需要接受它当前通过子进程包装 CLI 的实现边界。

需要自定义 UI、完整的双向事件流、thread/turn/item 生命周期或跨语言接入时,才值得直接集成 App Server。此时承担的工作是客户端协议、版本固定、认证和运行时部署,不是重新实现 harness。

如果目标是跨多个 Agent、项目管理系统或持续任务做更外层编排,Codex harness 仍可作为被调度的执行单元。OpenAI 的 Symphony 就处在这层:它在 harness 外面决定何时创建任务、怎样并发和怎样汇报,而不是替换 Core 内部的模型工具循环。

最后的边界

把 Codex CLI 叫作 harness,不算完全错误,因为用户安装的 CLI 发行物确实带着 harness,OpenAI 的早期文章也为了叙述方便混用过两个词。但这个叫法不适合解释源码和集成方式。

更稳妥的表达是:

1
2
3
4
5
Codex CLI = 终端产品 + 发行入口 + 一组客户端模式
Codex harness = CLI 仓库中可被多个表面复用的 Agent 执行系统
Codex Core = harness 的核心业务逻辑与 thread runtime
App Server = 托管并协议化暴露 Core 的边界
TUI / exec / SDK / App / IDE / Web = 以不同方式驱动 harness 的表面

CLI 让 harness 可以直接使用,App Server 让它可以被别的产品使用。理解这条关系后,“OpenAI 开源 Codex harness”才不会被误读成又一个命令行工具,也不会被扩大成 Codex 所有产品、模型和云服务都已经开源。

参考资料