Codex Harness 开源解读:如何读懂 Agent Loop,并以 SDK 与 App Server 构建自己的应用

2026 年 8 月 19 日,OpenAI 将 Codex 更清晰地定义为一个可供开发者构建产品的 开放 Agent Harness。这句话容易被误读成“Codex 模型开源了”,实际开放的是模型周围的运行时与集成面:会话、Agent Loop、工具执行、沙箱、审批、事件流、CLI、SDK 与 App Server。
用一句话概括:
模型决定下一步做什么,Harness 决定它能看到什么、可以怎样做、需要谁批准,以及整个过程如何被恢复和观察。
这篇文章会回答三个问题:
openai/codex到底开放了什么,没开放什么;- 面对庞大的 Rust 工作区,应当怎样学习 Agent Harness 的原理;
- 如何选择
codex exec、Codex SDK 或 App Server,构建自己的应用。
一、先划清边界:开源的是 Harness,不是全部 Codex
根据 Codex Open Source 官方页面,当前主要开放组件包括:
| 组件 | 位置 | 用途 |
|---|---|---|
| Codex CLI | openai/codex | 本地 Agent、终端 UI 与非交互执行入口 |
| Codex SDK | openai/codex/sdk | 在 TypeScript 或 Python 应用中启动、继续和恢复 Codex Thread |
| Codex App Server | codex-rs/app-server | 面向富客户端的双向协议、事件与审批接口 |
| Codex MCP Server | Codex CLI 能力 | 将 Codex 作为更大 Agent 系统中的编码专家 |
| Universal Cloud Environment | openai/codex-universal | Codex Cloud 使用的基础环境 |
主仓库采用 Apache-2.0 许可证。但需要注意:
- 开源层是 Harness 与集成面;
- 模型访问和托管服务是独立层;
- IDE 扩展与 Codex Cloud 本身不在开源组件列表中;
- 源码开放不等于你的产品自动获得模型、账户或服务权限。
因此,“Fork Codex”与“使用 Codex 模型”是两个不同问题。前者涉及运行时代码,后者仍受模型与服务的可用性、认证和条款约束。
二、Codex Harness 比一个 Prompt 多了什么
普通代码生成调用:
需求 + 代码上下文 → 模型 → 一段回答
Codex Harness 的实际闭环更接近:
任务
→ 建立或恢复 Thread
→ 装配指令、仓库上下文与工具
→ 模型推理
→ 发出命令 / 文件修改 / MCP 调用
→ 沙箱与审批策略判定
→ 执行并回传结果
→ 模型观察结果并继续
→ 流式发出 Items 与最终响应
OpenAI 官方将 Harness 的责任归纳为:管理对话状态、流式执行、使用工具、执行沙箱和审批策略、跨 Turn 延续工作。这里的关键不是“让模型不停循环”,而是让循环具备以下控制面:
- 状态:知道过去发生了什么;
- 工具:不只说怎么改,而是能检查、修改和验证;
- 边界:文件、网络与命令不是默认无限开放;
- 同意:高影响动作可以停下来请求用户;
- 事件:UI 与自动化系统能看到进行中的工作;
- 恢复:一次进程或客户端中断不必丢掉任务语境。
三、先看全景:应用、Harness、执行环境
上面的架构图可以拆成三层。
1. Your App:产品应该保留什么
你的应用仍然负责:
- 业务界面:工单、仓库、告警、客户记录或任务队列;
- 用户身份、租户和业务权限;
- 把当前界面的实体与约束转换成 Agent 上下文;
- 决定哪些动作必须人工批准;
- 将最终结果写回业务系统。
最好的 Codex 应用通常不是“另一个通用聊天框”,而是把 Agent 放到用户原本就理解的工作流旁边。
2. Codex Harness:可复用的控制循环
Harness 负责 Thread、Turn、Items、工具调用、流式事件、取消、恢复、沙箱与审批。它是产品与模型、执行环境之间的中间层。
3. Execution:副作用真正发生的地方
执行层包含:
- 工作区文件与 Git;
- Shell 命令和后台进程;
- Apply Patch 等文件变更能力;
- 本地或远程沙箱;
- 通过 MCP 暴露的业务数据与动作。
安全问题几乎都发生在这一层,因此应用不能只关注“回答内容是否正确”,还要验证“副作用是否在授权范围内”。
四、如何读 openai/codex:不要从第一行读到最后一行
openai/codex 是一个不断演进的大型工作区。仓库根目录主要有:
codex-rs/ Rust 主实现:核心、CLI、TUI、App Server、协议、沙箱、工具
sdk/ TypeScript、Python SDK 与 Python runtime
codex-cli/ CLI 相关包与兼容层
docs/ 开发和功能文档
tools/ 仓库维护工具
codex-rs/ 下 crate 很多,不应该按字母顺序学习。更有效的办法是围绕一条用户请求,分五层阅读。
第 1 层:入口与表现层
先回答“用户从哪里进入”:
cli:命令分发;tui:终端交互界面;exec:非交互任务;app-server:富客户端协议入口;mcp-server:把 Codex 暴露为 MCP 服务。
这层不要深挖渲染细节,只追踪入口怎样把请求转换成核心可理解的配置与输入。
第 2 层:协议与状态词汇
接着理解几个稳定概念:
| 概念 | 含义 |
|---|---|
| Thread | 一段可持续、可恢复的 Agent 对话 |
| Turn | 用户的一次请求以及随后发生的完整 Agent 工作 |
| Item | Turn 中的输入或输出单元,如消息、命令、文件变更、工具调用 |
| Event | Item 与 Turn 的增量状态通知 |
| Approval | 在副作用执行前暂停并等待宿主决定 |
对应阅读 protocol、app-server-protocol、rollout、thread-store 等模块。先理解数据如何流动,再看实现细节。
第 3 层:Agent Loop 与核心状态机
再进入 core、core-api。这一层重点找四条链:
输入如何变成模型请求
模型输出如何被解析为 Item / Tool Call
工具结果如何回到下一轮模型上下文
何种条件让 Turn 完成、失败、中断或继续
不要把 Agent Loop 理解成一个简单 while。真正困难的是:并发事件、取消、超时、审批暂停、上下文压缩、历史持久化和工具部分失败同时存在时,状态仍要一致。
第 4 层:工具与执行边界
然后读:
tools:工具定义与分发;shell-command/exec-server:命令执行;apply-patch:结构化文件修改;file-system/file-search:工作区能力;codex-mcp/rmcp-client:MCP 集成。
阅读时为每个工具记录四项:输入 Schema、模型可见结果、真实副作用、审批点。这样才能看懂 Harness 如何把“模型意图”转换成“受控执行”。
第 5 层:沙箱、权限与宿主集成
最后看 sandboxing、linux-sandbox、bwrap、execpolicy、shell-escalation、process-hardening。这些代码决定模型生成的命令到底在哪里运行,以及怎样越过边界。
很多 Agent Demo 只展示 Loop,却省略这一层;而生产系统真正的工程含量恰恰在这里。
五、用“一条任务”学习,比阅读目录更有效
建议选择一个小仓库,执行只读任务:
阅读项目结构,找到测试入口,并解释一个失败测试的可能原因。不要修改文件。
然后做四次追踪。
追踪 1:输入
从 CLI、SDK 或 App Server 的 turn/start 开始,记录:工作目录、模型、沙箱、审批策略和用户输入怎样进入核心。
追踪 2:事件
观察 turn/started、item/started、消息 delta、命令或工具 Item、item/completed 与 turn/completed。把事件时间线画出来。
追踪 3:副作用
找到工具执行前的策略判定、实际进程启动、stdout/stderr 回传与退出状态。确认拒绝、取消、超时分别走哪条路径。
追踪 4:恢复
结束客户端,重新 Resume Thread,再提交一个后续 Turn。检查历史从哪里加载,哪些运行时状态不会恢复。
这四次追踪完成以后,再读 crate 间抽象就不会悬空。
六、三种集成层:按产品复杂度选择
| 需求 | 推荐方式 | 原因 |
|---|---|---|
| 脚本、CI、一次性后台任务 | codex exec | 边界清晰,进程级集成简单 |
| 服务端应用启动/继续 Codex 任务 | Codex SDK | 省去底层协议处理,支持 Thread 生命周期 |
| 自定义 IDE、桌面端、运维控制台 | App Server | 直接控制事件、审批、历史与交互生命周期 |
| Codex 是多 Agent 系统里的编码专家 | Codex MCP Server | 由更上层 Orchestrator 统一协调 |
选择原则是:能用高层 SDK 完成,就不要先绑定底层协议;只有产品体验需要完整事件和审批控制时,才使用 App Server。
七、基于 Codex SDK 构建自己的应用
官方 TypeScript SDK 适合服务端 Node.js 18+,可以启动、继续和恢复本地 Codex Thread。
1. 安装
npm install @openai/codex-sdk
2. 最小调用
import { Codex } from "@openai/codex-sdk";
const codex = new Codex();
const thread = codex.startThread();
const diagnosis = await thread.run(
"分析当前仓库的 CI 失败,只给出带文件证据的修复计划,不要修改文件。",
);
console.log(diagnosis.finalResponse);
const implementation = await thread.run(
"按照计划实现最小修复,并运行相关测试。",
);
console.log(implementation.finalResponse);
run() 再次调用同一 Thread 时会延续上下文。持久任务也可以由应用保存 Thread ID,稍后通过 resumeThread(threadId) 恢复。
3. 不要把 SDK 直接放进浏览器
SDK 控制的是本地 Codex Agent,应在服务端或受控 Worker 中运行:
Browser / Mobile
→ Your API(认证、业务权限、限流)
→ Job Queue
→ Isolated Worker + Codex SDK
→ Workspace / MCP / Artifact Store
否则浏览器无法安全持有运行时权限,也无法隔离不同用户的文件与进程。
4. 一个“代码修复队列”应怎样建模
type CodexJob = {
id: string;
threadId?: string;
repo: string;
revision: string;
workspaceId: string;
requestedBy: string;
phase: "queued" | "diagnosing" | "awaiting_approval" | "applying" | "done";
risk: "read" | "workspace-write" | "external-write";
};
应用先以只读阶段生成计划和证据,用户批准后再启动写入阶段。Thread 可以延续,但执行权限必须由宿主重新判定,不能因为模型“记得用户同意过”就跳过当前审批。
八、App Server:构建自定义界面的底层基座

Codex App Server 是 Codex 用来支持富客户端的接口。它适合需要下面能力的产品:
- 展示实时推理与工具活动;
- 管理多段对话历史;
- 让用户批准或拒绝命令与文件变更;
- 在任务运行中追加指令、打断或取消;
- 将 Agent 嵌入已有业务面板,而不是只返回最终字符串。
1. 协议与传输
App Server 使用双向 JSON-RPC 风格消息,线上的 JSON 省略 "jsonrpc":"2.0" 字段。默认 stdio 使用一行一个 JSON 消息;也支持 Unix Socket。WebSocket 目前被官方标为实验性且不支持生产工作负载,应谨慎对待远程暴露。
2. 标准生命周期
连接进程
→ initialize
→ initialized
→ thread/start 或 thread/resume
→ turn/start
→ 持续读取 item/* 与 turn/* 通知
→ 处理 approval request
→ turn/completed 或 turn/interrupt
App Server 的三个核心原语是:
- Thread:包含多个 Turn 的对话;
- Turn:一次用户请求及 Agent 随后的工作;
- Item:消息、命令、文件变更、工具调用等最小可展示单元。
3. 最小协议客户端
下面的 TypeScript 片段展示进程与握手,字段与官方示例一致:
import { spawn } from "node:child_process";
import readline from "node:readline";
const proc = spawn("codex", ["app-server"], {
stdio: ["pipe", "pipe", "inherit"],
});
const lines = readline.createInterface({ input: proc.stdout });
const send = (message: unknown) => {
proc.stdin.write(`${JSON.stringify(message)}\n`);
};
let threadId: string | null = null;
lines.on("line", (line) => {
const message = JSON.parse(line);
console.log("event:", message);
if (message.id === 1 && message.result?.thread?.id && !threadId) {
threadId = message.result.thread.id;
send({
method: "turn/start",
id: 2,
params: {
threadId,
input: [{ type: "text", text: "只读分析这个仓库。" }],
},
});
}
});
send({
method: "initialize",
id: 0,
params: {
clientInfo: {
name: "repo_inspector",
title: "Repo Inspector",
version: "0.1.0",
},
},
});
send({ method: "initialized", params: {} });
send({ method: "thread/start", id: 1, params: {} });
真正的产品还必须补上:请求 ID 映射、进程退出、协议错误、消息背压、超时、取消、审批响应、版本能力协商与持久状态。
4. 事件 UI 不应该只显示“思考中”
建议把 Item 映射成明确的产品状态:
| Item / Event | UI 表达 |
|---|---|
| Agent message delta | 流式说明 |
| Command execution | 命令、工作目录、状态与输出摘要 |
| File change | 可审阅 diff |
| MCP tool | 工具名、参数摘要与数据来源 |
| Approval request | 影响范围、允许一次/拒绝操作 |
| Turn completed | 最终结果、验证证据、剩余风险 |
透明不等于把所有原始日志倾倒给用户。UI 应优先展示“发生了什么、为什么需要注意、用户现在能做什么”。
九、实战架构:构建一个带审批的发布故障助手
假设目标是:在部署失败后自动检查仓库、CI 日志和服务状态,提出修复;任何写代码、重跑流水线或创建工单的动作都要审批。
1. 产品拥有业务语境
Incident UI
├─ 服务、版本、负责人、告警与最近部署
├─ “开始诊断”业务按钮
├─ Agent Event Timeline
└─ Approval Drawer
用户不必从空白 Prompt 开始。点击“开始诊断”时,应用自动传入当前 incident ID、仓库 revision、日志引用与组织规则。
2. MCP 提供业务数据与动作
将能力拆成读写不同的工具:
只读:get_incident、get_deploy、query_logs、read_runbook
写入:rerun_pipeline、create_ticket、rollback_release
读工具也要最小授权;写工具必须由应用确认当前用户、当前对象与当前动作,而不是只相信模型参数。
3. Codex 负责 Agent Loop 和工作区执行
App Server 维持 Thread,Codex 在隔离工作区读取代码、运行检查、调用 MCP,并把进度作为 Items 流给 UI。
4. 宿主负责审批与事实写回
模型建议 rollback_release(release-42)
→ Harness 发出审批请求
→ UI 展示服务、版本、影响与理由
→ 用户批准一次
→ 宿主重新校验权限与资源版本
→ MCP Server 执行动作
→ 业务视图从系统记录重新拉取
审批不能只是一个“允许”按钮。它必须与准确的工具、参数、资源版本和授权主体绑定,避免审批后状态已经改变。
十、SDK 还是 App Server:一个更实际的决策树
只需要一次任务和最终结果?
└─ 是 → codex exec
应用代码需要启动、继续或恢复任务?
└─ 是 → Codex SDK
需要实时 Item UI、审批、打断、历史列表和自定义客户端?
└─ 是 → App Server
Codex 只是多 Agent 系统中的一个专家?
└─ 是 → Codex MCP Server + 上层 Orchestrator
不要为了“更底层、更灵活”过早选择 App Server。协议意味着更多生命周期、兼容性与错误恢复责任。
十一、Fork 源码前先问:我真的需要修改 Harness 吗?
大多数业务需求可以通过以下方式完成:
AGENTS.md与配置定义仓库规则;- Skills 封装可复用工作流;
- MCP 接入业务数据与动作;
- SDK 控制 Thread;
- App Server 自定义界面与审批;
- 沙箱和执行策略控制边界。
只有当需求落在这些扩展面之外,例如必须改变核心 Turn 状态机、工具调度语义或新平台沙箱实现时,才值得维护源码分支。
Fork 的隐性成本包括:
- 上游高频变更与冲突;
- 协议、SDK 和 App Server 版本绑定;
- 安全修复同步;
- Session 与配置格式迁移;
- 不同平台的执行与沙箱测试。
“能改源码”不等于“应该改源码”。开源更重要的价值常常是可检查、可验证和可贡献,而不是每个团队都维护一套私有 Harness。
十二、生产应用必须补齐的六层护栏
1. 输入边界
仓库文件、Issue、网页、日志与 MCP 返回都可能包含提示注入。外部文本是数据,不是更高优先级指令。
2. 工具边界
工具 Schema 要窄,读写工具分开;危险动作接受结构化参数,避免把任意 Shell 当作业务 API。
3. 执行边界
每个任务使用独立工作区或容器,限制文件、网络、CPU、内存、进程数与运行时间。
4. 审批边界
审批绑定具体动作、参数、主体和资源版本;批准一次不等于给 Thread 永久提权。
5. 状态边界
恢复 Thread 时验证仓库 revision、身份、租户和工作区租约。历史上下文不能替代当前事实。
6. 评估边界
不仅评文本答案,还要评:
- 是否改了允许的文件;
- 是否运行了正确验证;
- 是否遗漏审批;
- 是否在预算内停止;
- 是否能从工具失败恢复;
- 最终结论是否有可核验证据。
十三、五天学习路线
Day 1:把 Codex 当用户使用
运行 CLI,分别完成只读分析与小修复,观察命令、diff、审批和恢复。
Day 2:理解协议词汇
阅读 App Server 的 Thread、Turn、Item、Event 与 Approval。自己记录一次事件时间线。
Day 3:沿入口追核心
从 exec 或 app-server 追到 core,只追一条任务,不发散到所有 crate。
Day 4:研究执行与安全
选择 Shell 或 Apply Patch,追踪从模型工具调用到沙箱执行、审批和结果回传的完整链。
Day 5:用 SDK 做一个垂直原型
不要复制 Codex UI。选择一个小场景,例如失败测试诊断、依赖升级评估或 PR 证据检查,把业务上下文、Agent Loop 和审批分层实现。
完成这个原型后,再判断是否需要 App Server 或源码级扩展。
十四、总结:学习 Harness,重点不是复刻一个聊天框
Codex Harness 展示了一种更成熟的 Agent 工程分层:
业务应用拥有语境、界面、权限与结果
Codex Harness 拥有会话、循环、事件与受控工具执行
模型提供推理能力
沙箱和 MCP 将推理连接到真实环境
对于开发者,最值得学习的不是某一个 Prompt,而是:
- 如何把持续任务建模为 Thread / Turn / Item;
- 如何让工具结果安全地回到下一轮推理;
- 如何在执行前插入沙箱和人工同意;
- 如何通过事件流让过程可见、可打断、可恢复;
- 如何让产品保留业务控制权,而 Harness 专注通用 Agent Loop。
构建自己的应用时,从 SDK 开始,把任务做小、权限做窄、状态做实;只有 UI 与生命周期确实需要深度控制时,再进入 App Server。这样才能真正利用开源 Harness,而不是把复杂度搬进自己的代码库。