原创声明: 本文为原创技术文章,围绕 OpenAI 正式开放 Codex Harness 后的架构、核心能力、集成方式与工程实践进行系统梳理。转载请注明出处。
1. 引言:Codex真正开放的,远不只是一个CLI
如果你最近在做 AI Agent,大概率碰到过这样的场景:
模型已经足够强,Prompt 也调了几十版,但真正落地到工程环境以后,问题突然变多了。
比如:
- 模型知道怎么修 Bug,却不知道应该先读哪些文件;
- 工具调用执行到一半失败,整个任务状态丢了;
- Agent 连续运行十几分钟,前面的上下文越来越乱;
- 自动改代码很爽,但谁来决定它能不能执行
rm、访问网络、改生产配置? - 想把 Agent 塞进自己的 IDE、运维平台、工单系统,又发现自己得重新实现会话、流式输出、审批、状态机;
- CI 中运行 Agent 时,最终只想拿 JSON,结果得到一大段适合人看的聊天文本。
很多团队做到这里才发现:
真正难的往往已经不是"大模型会不会写代码",而是如何让模型在真实软件系统里持续、可靠、可控地完成任务。
这也是 Harness 的价值所在。
2026 年 8 月 19 日,OpenAI 在开发者博客发布 《Codex as a platform: build on the open agent harness》 ,明确指出:驱动 Codex App、CLI、IDE 等体验的底层 Codex Harness 已作为开放能力提供给开发者。官方同时给出了三条主要集成路径:codex exec、Codex SDK 和 Codex App Server。
这里有一个非常重要的概念必须先澄清:
Codex Harness 开源,不等于 OpenAI 的模型权重开源。
开放的是 Agent 的执行与集成层,也就是模型外面那套负责"让模型真正干活"的基础设施。官方也明确说明,开放的是 Harness 与集成接口,模型访问及托管服务属于另一层。
Codex 正在从一个开发工具,变成一个可以嵌入其他软件的 Agent Runtime。
2. Codex Harness正式开源,到底开了什么?
2.1 先理解Harness:它不是模型,而是模型的"操作系统"
假设我们直接调用一个大模型:
text
用户问题
↓
Prompt
↓
LLM
↓
文本答案
对于问答系统,这已经够了。
但一个真正的 Coding Agent 要完成"分析项目 → 找文件 → 修改代码 → 跑测试 → 发现失败 → 再修改 → 请求审批 → 输出结果",显然不能只调用一次模型。
真正的流程更接近:
#mermaid-svg-8mNQbVENdvt3wd5p{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-8mNQbVENdvt3wd5p .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8mNQbVENdvt3wd5p .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8mNQbVENdvt3wd5p .error-icon{fill:#552222;}#mermaid-svg-8mNQbVENdvt3wd5p .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8mNQbVENdvt3wd5p .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8mNQbVENdvt3wd5p .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8mNQbVENdvt3wd5p .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8mNQbVENdvt3wd5p .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8mNQbVENdvt3wd5p .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8mNQbVENdvt3wd5p .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8mNQbVENdvt3wd5p .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8mNQbVENdvt3wd5p .marker.cross{stroke:#333333;}#mermaid-svg-8mNQbVENdvt3wd5p svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8mNQbVENdvt3wd5p p{margin:0;}#mermaid-svg-8mNQbVENdvt3wd5p .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-8mNQbVENdvt3wd5p .cluster-label text{fill:#333;}#mermaid-svg-8mNQbVENdvt3wd5p .cluster-label span{color:#333;}#mermaid-svg-8mNQbVENdvt3wd5p .cluster-label span p{background-color:transparent;}#mermaid-svg-8mNQbVENdvt3wd5p .label text,#mermaid-svg-8mNQbVENdvt3wd5p span{fill:#333;color:#333;}#mermaid-svg-8mNQbVENdvt3wd5p .node rect,#mermaid-svg-8mNQbVENdvt3wd5p .node circle,#mermaid-svg-8mNQbVENdvt3wd5p .node ellipse,#mermaid-svg-8mNQbVENdvt3wd5p .node polygon,#mermaid-svg-8mNQbVENdvt3wd5p .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-8mNQbVENdvt3wd5p .rough-node .label text,#mermaid-svg-8mNQbVENdvt3wd5p .node .label text,#mermaid-svg-8mNQbVENdvt3wd5p .image-shape .label,#mermaid-svg-8mNQbVENdvt3wd5p .icon-shape .label{text-anchor:middle;}#mermaid-svg-8mNQbVENdvt3wd5p .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-8mNQbVENdvt3wd5p .rough-node .label,#mermaid-svg-8mNQbVENdvt3wd5p .node .label,#mermaid-svg-8mNQbVENdvt3wd5p .image-shape .label,#mermaid-svg-8mNQbVENdvt3wd5p .icon-shape .label{text-align:center;}#mermaid-svg-8mNQbVENdvt3wd5p .node.clickable{cursor:pointer;}#mermaid-svg-8mNQbVENdvt3wd5p .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-8mNQbVENdvt3wd5p .arrowheadPath{fill:#333333;}#mermaid-svg-8mNQbVENdvt3wd5p .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-8mNQbVENdvt3wd5p .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-8mNQbVENdvt3wd5p .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8mNQbVENdvt3wd5p .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-8mNQbVENdvt3wd5p .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8mNQbVENdvt3wd5p .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-8mNQbVENdvt3wd5p .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-8mNQbVENdvt3wd5p .cluster text{fill:#333;}#mermaid-svg-8mNQbVENdvt3wd5p .cluster span{color:#333;}#mermaid-svg-8mNQbVENdvt3wd5p div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-8mNQbVENdvt3wd5p .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-8mNQbVENdvt3wd5p rect.text{fill:none;stroke-width:0;}#mermaid-svg-8mNQbVENdvt3wd5p .icon-shape,#mermaid-svg-8mNQbVENdvt3wd5p .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8mNQbVENdvt3wd5p .icon-shape p,#mermaid-svg-8mNQbVENdvt3wd5p .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-8mNQbVENdvt3wd5p .icon-shape .label rect,#mermaid-svg-8mNQbVENdvt3wd5p .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8mNQbVENdvt3wd5p .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-8mNQbVENdvt3wd5p .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-8mNQbVENdvt3wd5p :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否
是
是
否
用户提交任务
Harness创建或恢复Thread
收集项目上下文
模型分析与规划
是否需要工具
生成最终结果
检查权限与沙箱策略
是否需要人工审批
请求用户批准
执行工具
写入工具执行结果
更新上下文与状态
保存Thread与事件记录
图中的模型只负责其中一部分。
真正维持循环的是 Harness。
它需要负责:
- 当前任务处于什么阶段;
- 模型已经看过哪些内容;
- 可以调用哪些工具;
- 哪些工具已经执行;
- 执行结果如何重新喂给模型;
- 哪些操作允许自动执行;
- 哪些操作必须请求人类批准;
- 长对话如何压缩上下文;
- 中断后如何继续;
- 前端如何实时知道 Agent 正在做什么。
因此我更愿意把 Harness 理解为:
LLM Agent 的运行时 + 调度器 + 状态管理器 + 权限边界。
模型像 CPU,而 Harness 更接近操作系统。
2.2 为什么这次开放比"CLI开源"意义更大
Codex CLI 本身早就以开源形式存在,因此如果只看到"Codex 开源"四个字,很容易觉得只是旧闻。
真正的新变化,是 OpenAI 开始把 Codex 明确定位成一个可复用 Agent 平台。
官方当前开放组件包括:
| 组件 | 主要用途 | 是否开源 |
|---|---|---|
| Codex CLI | 终端交互式 Coding Agent | 是 |
codex exec |
CI、脚本、自动任务 | 是 |
| Codex SDK | 在应用代码中启动/恢复 Agent | 是 |
| Codex App Server | 完整 Agent 生命周期与事件协议 | 是 |
| Codex Security CLI/SDK | 安全扫描与分析 | 是 |
| Skills / Plugins | 扩展能力 | 是 |
| IDE Extension | IDE 官方客户端 | 否 |
| Codex Cloud | 托管云端能力 | 否 |
OpenAI 官方 Open Source 页面目前明确列出了 CLI、SDK、App Server 等开源组件,同时指出 IDE Extension 与 Codex Cloud 并不属于开源范围。
所以不要把这件事理解成:
"OpenAI 把 Codex 整个产品全部开源了。"
更准确的说法是:
OpenAI 把 Codex 的核心 Agent Harness 与主要集成接口开放出来,让开发者能够在自己的产品里复用这套 Agent Runtime。
2.3 开源协议是什么?
Codex 主仓库采用 Apache-2.0 License。官方仓库的 License 文档对此有明确说明。
Apache-2.0 对企业使用相对友好,你可以:
- 阅读源码;
- 修改实现;
- 基于其进行二次开发;
- 集成进内部系统;
- 在满足许可证要求的前提下进行分发。
但需要注意:
"代码开源"不代表模型调用免费。
Harness 可以自己运行,但如果底层仍然调用 OpenAI 模型,那么模型 API、订阅或托管能力依旧遵循相应的访问和计费规则。
2.4 为什么Harness会直接影响模型效果?
很多开发者容易陷入一个误区:
模型效果不好,就一定要换更大的模型。
实际上 Agent 场景中,Harness 的设计本身也会显著影响最终结果。
OpenAI 在官方文章中给出一个很典型的数据:在 ARC-AGI-3 相关实验中,通过 retained reasoning 与 context compaction 等 Harness 层优化,GPT-5.6 Sol 的成绩从 13.3% 提升到 38.3%,同时输出 Token 数量降低到原来的约六分之一。
这个数据最值得开发者思考的地方并不是具体分数,而是:
同一个模型,运行环境不同,最终能力可能完全不同。
未来 Agent 工程竞争的重点,很可能不只是:
text
谁的模型更强?
而会变成:
text
模型能力
×
上下文质量
×
工具质量
×
反馈速度
×
权限设计
×
Harness效率
3. 核心架构拆解:Thread、Turn、Item是关键
要真正用好 Codex Harness,必须理解三个概念:
- Thread
- Turn
- Item
3.1 Thread:一次长期任务的上下文容器
Thread 可以理解为一个长期存在的 Agent 会话。
例如:
text
Thread #A
├── 用户:分析支付模块偶发超时
├── Agent:检查代码和日志
├── 用户:继续看数据库连接池
├── Agent:发现连接泄漏
└── 用户:修复并补测试
这些操作不应该被当成三个毫无关系的请求。
它们属于同一个工程任务。
所以 Harness 会维护 Thread,让后续 Turn 能够建立在之前的历史、工具执行结果以及任务状态之上。
这也是传统"一问一答 API"与 Agent Runtime 最大的区别之一。
3.2 Turn:一次Agent执行周期
Thread 中每一次用户发起的新指令,对应一个 Turn。
例如:
text
Thread:修复订单服务问题
Turn 1:分析异常原因
Turn 2:根据分析修改代码
Turn 3:运行测试并修复失败
Turn 4:生成最终变更说明
一个 Turn 本身可能持续几十秒甚至几分钟。
期间模型可能连续调用多个工具,而不是一次模型请求就结束。
3.3 Item:真正发生的事件
Turn 内部又包含大量 Item。
例如:
text
用户消息
模型推理
Shell命令
文件修改
MCP调用
Agent消息
计划更新
搜索操作
因此完整层级可以理解为:
#mermaid-svg-vCfI3I7a6X0Whuo4{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-vCfI3I7a6X0Whuo4 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-vCfI3I7a6X0Whuo4 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-vCfI3I7a6X0Whuo4 .error-icon{fill:#552222;}#mermaid-svg-vCfI3I7a6X0Whuo4 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-vCfI3I7a6X0Whuo4 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-vCfI3I7a6X0Whuo4 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-vCfI3I7a6X0Whuo4 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-vCfI3I7a6X0Whuo4 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-vCfI3I7a6X0Whuo4 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-vCfI3I7a6X0Whuo4 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-vCfI3I7a6X0Whuo4 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-vCfI3I7a6X0Whuo4 .marker.cross{stroke:#333333;}#mermaid-svg-vCfI3I7a6X0Whuo4 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-vCfI3I7a6X0Whuo4 p{margin:0;}#mermaid-svg-vCfI3I7a6X0Whuo4 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-vCfI3I7a6X0Whuo4 .cluster-label text{fill:#333;}#mermaid-svg-vCfI3I7a6X0Whuo4 .cluster-label span{color:#333;}#mermaid-svg-vCfI3I7a6X0Whuo4 .cluster-label span p{background-color:transparent;}#mermaid-svg-vCfI3I7a6X0Whuo4 .label text,#mermaid-svg-vCfI3I7a6X0Whuo4 span{fill:#333;color:#333;}#mermaid-svg-vCfI3I7a6X0Whuo4 .node rect,#mermaid-svg-vCfI3I7a6X0Whuo4 .node circle,#mermaid-svg-vCfI3I7a6X0Whuo4 .node ellipse,#mermaid-svg-vCfI3I7a6X0Whuo4 .node polygon,#mermaid-svg-vCfI3I7a6X0Whuo4 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-vCfI3I7a6X0Whuo4 .rough-node .label text,#mermaid-svg-vCfI3I7a6X0Whuo4 .node .label text,#mermaid-svg-vCfI3I7a6X0Whuo4 .image-shape .label,#mermaid-svg-vCfI3I7a6X0Whuo4 .icon-shape .label{text-anchor:middle;}#mermaid-svg-vCfI3I7a6X0Whuo4 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-vCfI3I7a6X0Whuo4 .rough-node .label,#mermaid-svg-vCfI3I7a6X0Whuo4 .node .label,#mermaid-svg-vCfI3I7a6X0Whuo4 .image-shape .label,#mermaid-svg-vCfI3I7a6X0Whuo4 .icon-shape .label{text-align:center;}#mermaid-svg-vCfI3I7a6X0Whuo4 .node.clickable{cursor:pointer;}#mermaid-svg-vCfI3I7a6X0Whuo4 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-vCfI3I7a6X0Whuo4 .arrowheadPath{fill:#333333;}#mermaid-svg-vCfI3I7a6X0Whuo4 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-vCfI3I7a6X0Whuo4 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-vCfI3I7a6X0Whuo4 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vCfI3I7a6X0Whuo4 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-vCfI3I7a6X0Whuo4 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vCfI3I7a6X0Whuo4 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-vCfI3I7a6X0Whuo4 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-vCfI3I7a6X0Whuo4 .cluster text{fill:#333;}#mermaid-svg-vCfI3I7a6X0Whuo4 .cluster span{color:#333;}#mermaid-svg-vCfI3I7a6X0Whuo4 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-vCfI3I7a6X0Whuo4 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-vCfI3I7a6X0Whuo4 rect.text{fill:none;stroke-width:0;}#mermaid-svg-vCfI3I7a6X0Whuo4 .icon-shape,#mermaid-svg-vCfI3I7a6X0Whuo4 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vCfI3I7a6X0Whuo4 .icon-shape p,#mermaid-svg-vCfI3I7a6X0Whuo4 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-vCfI3I7a6X0Whuo4 .icon-shape .label rect,#mermaid-svg-vCfI3I7a6X0Whuo4 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vCfI3I7a6X0Whuo4 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-vCfI3I7a6X0Whuo4 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-vCfI3I7a6X0Whuo4 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Thread 长期任务
Turn 1
Turn 2
Turn 3
User Message
Reasoning
Shell Command
File Change
Agent Message
User Message
MCP Tool Call
Agent Message
这个设计为什么重要?
因为真实产品往往不是只关心"最后一句回答"。
前端可能希望实时显示:
text
正在读取 package.json...
正在检查 src/payment...
正在执行 npm test...
发现 2 个失败测试...
正在修改 connection.ts...
等待用户批准执行数据库迁移...
这就要求 Harness 把执行过程变成结构化事件流。
3.4 App Server承担什么角色?
Codex App Server 就是外部应用与 Harness 之间的重要接口。
它通过双向协议让客户端完成:
text
创建Thread
↓
启动Turn
↓
接收事件
↓
处理工具调用
↓
处理审批
↓
继续执行
↓
完成Turn
官方 App Server 当前以 JSON-RPC 风格协议暴露这些能力,并要求连接建立后先执行 initialize,随后才能创建 Thread 或开始 Turn。
典型时序如下:
Tools Model Harness Codex App Server 业务前端 Tools Model Harness Codex App Server 业务前端 #mermaid-svg-tI4uJAq2JGuBxpLh{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-tI4uJAq2JGuBxpLh .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-tI4uJAq2JGuBxpLh .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-tI4uJAq2JGuBxpLh .error-icon{fill:#552222;}#mermaid-svg-tI4uJAq2JGuBxpLh .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-tI4uJAq2JGuBxpLh .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-tI4uJAq2JGuBxpLh .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tI4uJAq2JGuBxpLh .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tI4uJAq2JGuBxpLh .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-tI4uJAq2JGuBxpLh .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tI4uJAq2JGuBxpLh .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tI4uJAq2JGuBxpLh .marker{fill:#333333;stroke:#333333;}#mermaid-svg-tI4uJAq2JGuBxpLh .marker.cross{stroke:#333333;}#mermaid-svg-tI4uJAq2JGuBxpLh svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tI4uJAq2JGuBxpLh p{margin:0;}#mermaid-svg-tI4uJAq2JGuBxpLh .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-tI4uJAq2JGuBxpLh text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-tI4uJAq2JGuBxpLh .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-tI4uJAq2JGuBxpLh .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-tI4uJAq2JGuBxpLh .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-tI4uJAq2JGuBxpLh .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-tI4uJAq2JGuBxpLh #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-tI4uJAq2JGuBxpLh .sequenceNumber{fill:white;}#mermaid-svg-tI4uJAq2JGuBxpLh #sequencenumber{fill:#333;}#mermaid-svg-tI4uJAq2JGuBxpLh #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-tI4uJAq2JGuBxpLh .messageText{fill:#333;stroke:none;}#mermaid-svg-tI4uJAq2JGuBxpLh .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-tI4uJAq2JGuBxpLh .labelText,#mermaid-svg-tI4uJAq2JGuBxpLh .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-tI4uJAq2JGuBxpLh .loopText,#mermaid-svg-tI4uJAq2JGuBxpLh .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-tI4uJAq2JGuBxpLh .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-tI4uJAq2JGuBxpLh .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-tI4uJAq2JGuBxpLh .noteText,#mermaid-svg-tI4uJAq2JGuBxpLh .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-tI4uJAq2JGuBxpLh .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-tI4uJAq2JGuBxpLh .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-tI4uJAq2JGuBxpLh .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-tI4uJAq2JGuBxpLh .actorPopupMenu{position:absolute;}#mermaid-svg-tI4uJAq2JGuBxpLh .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-tI4uJAq2JGuBxpLh .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-tI4uJAq2JGuBxpLh .actor-man circle,#mermaid-svg-tI4uJAq2JGuBxpLh line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-tI4uJAq2JGuBxpLh :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} initializeinitializedthread/start创建Threadturn/start提交上下文与任务请求工具执行工具返回结果item事件/执行进度继续推理最终结果turn/completed
这意味着开发者不必重新写一套复杂的 Agent Loop。
自己的应用只需要重点管理:
- 用户界面;
- 业务数据;
- 专用工具;
- 权限规则;
- 审批流程;
- 结果落库。
而 Harness 负责维护 Agent 的底层执行循环。
4. 三种集成方案怎么选?别一上来就用App Server
Codex Harness 开放以后,一个很容易犯的错误就是:
"既然 App Server 最底层、能力最完整,那我直接用 App Server。"
技术上当然可以。
但对于绝大多数项目,这并不是最优选择。
OpenAI 官方实际上给出了非常清晰的三层集成方式。
4.1 方案对比
| 方案 | 上手难度 | 控制能力 | 适合场景 | 推荐指数 |
|---|---|---|---|---|
codex exec |
低 | 中 | CI、Shell、批处理 | ★★★★★ |
| Codex SDK | 中 | 高 | Node 服务、业务系统 | ★★★★★ |
| App Server | 高 | 最高 | IDE、Agent产品、复杂UI | ★★★★☆ |
| 自己实现Agent Loop | 极高 | 极高 | 特殊研究需求 | ★★☆☆☆ |
我的建议非常简单:
text
能用 exec → 不急着上 SDK
能用 SDK → 不急着直接操作 App Server
只有产品需要完整 Agent 生命周期 → 再用 App Server
4.2 什么情况使用codex exec?
如果你的需求是:
- 每晚分析一次代码仓库;
- CI 失败后自动总结原因;
- 自动生成 Release Notes;
- PR 合并前进行风险检查;
- 把日志传给 Codex 分析;
- 定时生成技术债报告;
那么 codex exec 基本就是最合适的方案。
它最大的特点是:
Agent 能力被包装成了一个普通 Unix 命令。
这意味着可以直接参与管道:
text
其他命令 stdout
↓
codex exec
↓
JSON/文本
↓
jq / 文件 / CI / 数据库
4.3 什么情况使用Codex SDK?
如果你正在写一个后端服务:
text
用户提交问题
↓
Node.js API
↓
启动Codex任务
↓
保存Thread ID
↓
返回结果
这时候 SDK 更舒服。
官方 TypeScript SDK 目前要求 Node.js 18+,安装方式为:
bash
npm install @openai/codex-sdk
它可以直接启动 Thread,也可以通过 Thread ID 恢复过去的任务。
4.4 什么情况直接接App Server?
如果你要做的是:
- 自研 IDE;
- DevOps Agent 控制台;
- AI SRE 平台;
- 安全调查工作台;
- 工单 Agent;
- 类似 Codex 的桌面应用;
- 需要实时展示 Agent 每一步执行状态;
那么 App Server 更合适。
因为这时候你关心的已经不只是:
text
Agent最后回答什么?
而是:
text
Agent现在在做什么?
调用了什么?
准备修改什么?
是否需要审批?
任务能不能中断?
之前的Thread能不能继续?
这正是 App Server 的能力边界。
5. 实战:从CLI到SDK跑通Codex Harness
下面直接进入实操。
说明: Codex 更新速度很快,命令参数和实验性协议字段未来可能变化。实际生产环境请锁定版本,并以对应版本官方文档为准。
5.1 前置环境
建议准备:
| 项目 | 建议 |
|---|---|
| Git | 最新稳定版 |
| Node.js | SDK 使用时要求 18+ |
| npm | 安装 CLI/SDK |
| 操作系统 | macOS / Linux / Windows 开发环境 |
| 项目 | 建议使用 Git 仓库 |
| Codex认证 | ChatGPT 登录或对应 API 凭证 |
安装 Codex CLI:
bash
npm install -g @openai/codex
也可以根据官方仓库提供的方式使用 Homebrew 或对应平台二进制包安装。
安装完成后检查:
bash
codex --version
然后执行:
bash
codex
完成账号认证。
5.2 步骤1:先用codex exec跑一个最小任务
进入一个 Git 项目:
bash
cd your-project
执行:
bash
codex exec "分析当前项目结构,并列出最值得优先检查的5个潜在风险"
正常情况下,你会看到 Agent:
- 阅读目录;
- 检查关键配置;
- 分析源码;
- 输出最终结果。
官方文档说明,codex exec 运行时会把执行进度输出到 stderr,最终 Agent 消息输出到 stdout,因此非常适合 Shell 重定向与管道处理。
例如生成代码审查报告:
bash
codex exec "审查当前仓库,生成Markdown格式的技术债报告" \
> technical-debt.md
这个能力看似普通,却非常重要。
因为这意味着:
Coding Agent 第一次可以像 grep、awk、curl 一样成为流水线中的一个标准步骤。
5.3 步骤2:让CI读取JSON事件
纯文本适合人看。
机器处理更适合 JSON。
执行:
bash
codex exec --json \
"分析当前仓库结构,并指出最可能导致CI失败的模块" \
| jq
此时输出是 JSON Lines。
典型事件可能包括:
json
{"type":"thread.started","thread_id":"xxx"}
{"type":"turn.started"}
{"type":"item.started","item":{"type":"command_execution"}}
{"type":"item.completed","item":{"type":"agent_message"}}
{"type":"turn.completed"}
官方文档明确列出了 thread.started、turn.started、turn.completed、item.*、error 等事件类型。
有了这个能力,你就可以在 CI 中做:
text
Codex
↓
JSONL
↓
解析事件
↓
风险评分
↓
达到阈值?
┌──────┴──────┐
是 否
↓ ↓
阻止合并 继续CI
5.4 步骤3:给自动化任务设置最小权限
这一步非常重要。
不要因为 Agent 能写代码,就直接给最大权限。
codex exec 默认采用较保守的沙箱策略。需要允许修改工作区时,可以显式指定:
bash
codex exec \
--sandbox workspace-write \
"修复测试失败,并仅修改当前项目中的必要文件"
如果真的需要更广泛权限,也存在更开放的沙箱模式,但官方明确建议只在受控环境,例如隔离的 CI Runner 或容器中使用高权限配置。
生产环境应该遵循:
text
默认拒绝
↓
最小权限
↓
必要时提升
↓
高风险操作人工审批
而不是:
text
先给全部权限
↓
希望Agent不要出错
后者不是自动化,是赌博。
5.5 步骤4:使用Codex SDK接入自己的Node服务
创建项目:
bash
mkdir codex-sdk-demo
cd codex-sdk-demo
npm init -y
npm install @openai/codex-sdk
创建 index.mjs:
javascript
import { Codex } from "@openai/codex-sdk";
const codex = new Codex();
const thread = codex.startThread();
const result = await thread.run(
"分析当前项目,并给出三个最值得优先改进的工程问题"
);
console.log(result.finalResponse);
运行:
bash
node index.mjs
这就是最小可运行 SDK 示例。
官方 SDK 的核心模型也非常直观:
text
Codex
↓
startThread()
↓
thread.run()
↓
finalResponse
官方示例同样使用 new Codex()、startThread() 与 thread.run() 启动任务。
如果接下来继续:
javascript
const result2 = await thread.run(
"针对第一个问题给出修改方案"
);
console.log(result2.finalResponse);
它仍然属于同一个 Thread。
这对业务系统非常重要,因为你不再需要每次都把完整聊天历史手工拼进 Prompt。
5.6 步骤5:恢复历史Thread
假设服务重启了。
传统 Agent 最麻烦的问题之一就是:
"之前任务执行到哪里了?"
Codex SDK 支持根据 Thread ID 恢复历史任务。官方提供了 resumeThread(threadId) 接口。
业务数据库可以只保存:
text
task_id
user_id
thread_id
status
created_at
下次用户继续任务时:
text
业务task_id
↓
数据库查询thread_id
↓
resumeThread(thread_id)
↓
继续执行
这个设计对长任务、异步任务和 Agent 工作台非常关键。
5.7 步骤6:理解App Server最小协议
如果 SDK 已经无法满足产品控制需求,就可以进一步接 App Server。
首先启动:
bash
codex app-server --listen stdio://
客户端建立连接后,第一件事必须是初始化:
json
{
"method": "initialize",
"id": 1,
"params": {
"clientInfo": {
"name": "my_codex_client",
"title": "My Codex Client",
"version": "0.1.0"
}
}
}
之后创建 Thread:
json
{
"method": "thread/start",
"id": 2,
"params": {
"cwd": "/path/to/project",
"sandbox": "workspaceWrite"
}
}
然后启动 Turn:
json
{
"method": "turn/start",
"id": 3,
"params": {
"threadId": "thr_xxx",
"input": [
{
"type": "text",
"text": "分析当前项目并运行测试"
}
]
}
}
随后应用持续监听:
text
item/started
item/completed
item/agentMessage/delta
...
直到任务结束。
官方协议的核心流程就是:
text
initialize
↓
thread/start
↓
turn/start
↓
持续消费事件
并且还可以进行 Thread 恢复、分支、Turn steering、审批处理等更高级操作。
到这里,你已经不再是"调用 Codex"。
而是在:
把 Codex 的 Agent Runtime 嵌入自己的产品。
6. Harness真正改变的是Agent工程方法
6.1 不要让Agent拥有整个世界,只给它完成任务所需的世界
优秀 Agent 系统不是工具越多越好。
假如一次故障分析任务可以使用:
text
Shell
Git
数据库
生产K8s
云平台
工单系统
邮件
Slack
支付后台
模型面对的决策空间反而会迅速膨胀。
更合理的是根据角色提供工具:
#mermaid-svg-PPFMvQhFtBnBaRY1{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-PPFMvQhFtBnBaRY1 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-PPFMvQhFtBnBaRY1 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-PPFMvQhFtBnBaRY1 .error-icon{fill:#552222;}#mermaid-svg-PPFMvQhFtBnBaRY1 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-PPFMvQhFtBnBaRY1 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-PPFMvQhFtBnBaRY1 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-PPFMvQhFtBnBaRY1 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-PPFMvQhFtBnBaRY1 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-PPFMvQhFtBnBaRY1 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-PPFMvQhFtBnBaRY1 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-PPFMvQhFtBnBaRY1 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-PPFMvQhFtBnBaRY1 .marker.cross{stroke:#333333;}#mermaid-svg-PPFMvQhFtBnBaRY1 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-PPFMvQhFtBnBaRY1 p{margin:0;}#mermaid-svg-PPFMvQhFtBnBaRY1 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-PPFMvQhFtBnBaRY1 .cluster-label text{fill:#333;}#mermaid-svg-PPFMvQhFtBnBaRY1 .cluster-label span{color:#333;}#mermaid-svg-PPFMvQhFtBnBaRY1 .cluster-label span p{background-color:transparent;}#mermaid-svg-PPFMvQhFtBnBaRY1 .label text,#mermaid-svg-PPFMvQhFtBnBaRY1 span{fill:#333;color:#333;}#mermaid-svg-PPFMvQhFtBnBaRY1 .node rect,#mermaid-svg-PPFMvQhFtBnBaRY1 .node circle,#mermaid-svg-PPFMvQhFtBnBaRY1 .node ellipse,#mermaid-svg-PPFMvQhFtBnBaRY1 .node polygon,#mermaid-svg-PPFMvQhFtBnBaRY1 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-PPFMvQhFtBnBaRY1 .rough-node .label text,#mermaid-svg-PPFMvQhFtBnBaRY1 .node .label text,#mermaid-svg-PPFMvQhFtBnBaRY1 .image-shape .label,#mermaid-svg-PPFMvQhFtBnBaRY1 .icon-shape .label{text-anchor:middle;}#mermaid-svg-PPFMvQhFtBnBaRY1 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-PPFMvQhFtBnBaRY1 .rough-node .label,#mermaid-svg-PPFMvQhFtBnBaRY1 .node .label,#mermaid-svg-PPFMvQhFtBnBaRY1 .image-shape .label,#mermaid-svg-PPFMvQhFtBnBaRY1 .icon-shape .label{text-align:center;}#mermaid-svg-PPFMvQhFtBnBaRY1 .node.clickable{cursor:pointer;}#mermaid-svg-PPFMvQhFtBnBaRY1 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-PPFMvQhFtBnBaRY1 .arrowheadPath{fill:#333333;}#mermaid-svg-PPFMvQhFtBnBaRY1 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-PPFMvQhFtBnBaRY1 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-PPFMvQhFtBnBaRY1 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PPFMvQhFtBnBaRY1 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-PPFMvQhFtBnBaRY1 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PPFMvQhFtBnBaRY1 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-PPFMvQhFtBnBaRY1 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-PPFMvQhFtBnBaRY1 .cluster text{fill:#333;}#mermaid-svg-PPFMvQhFtBnBaRY1 .cluster span{color:#333;}#mermaid-svg-PPFMvQhFtBnBaRY1 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-PPFMvQhFtBnBaRY1 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-PPFMvQhFtBnBaRY1 rect.text{fill:none;stroke-width:0;}#mermaid-svg-PPFMvQhFtBnBaRY1 .icon-shape,#mermaid-svg-PPFMvQhFtBnBaRY1 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PPFMvQhFtBnBaRY1 .icon-shape p,#mermaid-svg-PPFMvQhFtBnBaRY1 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-PPFMvQhFtBnBaRY1 .icon-shape .label rect,#mermaid-svg-PPFMvQhFtBnBaRY1 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PPFMvQhFtBnBaRY1 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-PPFMvQhFtBnBaRY1 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-PPFMvQhFtBnBaRY1 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 代码开发
故障分析
客服支持
安全分析
用户任务
任务类型
Git + Shell + Test
Logs + Metrics + ReadOnly K8s
订单查询 + 知识库
代码扫描 + 漏洞数据库
Codex Harness
工具越精准:
- Prompt 越简单;
- Token 越少;
- 错误调用概率越低;
- 安全边界越容易定义。
6.2 Human-in-the-loop应该放在"副作用边界"
不是所有步骤都需要审批。
如果每次 ls 都弹一次:
text
是否允许?
用户很快就会无脑点击"允许"。
真正应该审批的是有明显副作用的动作。
比如:
text
读取日志 → 自动
分析代码 → 自动
运行单元测试 → 自动
修改工作区代码 → 可自动/按策略
创建PR → 可审批
修改生产配置 → 必须审批
删除资源 → 必须审批
支付/退款/发消息 → 必须审批
审批点太多会产生"权限疲劳"。
审批点太少又会产生安全风险。
正确原则是:
把人类注意力放在不可逆、外部可见或高风险操作上。
6.3 AGENTS.md会越来越重要
当 Agent 真正进入大型项目后,最值得长期维护的不再只是 README。
还应该包括 Agent 能理解的工程规则:
text
目录职责
架构边界
测试命令
代码生成规则
禁止修改区域
数据库迁移要求
提交规范
安全要求
也就是说,以前我们主要在做:
text
Developer Experience
以后还需要同时考虑:
text
Agent Experience
一个"人类看着很清晰"的项目,并不一定对 Agent 友好。
真正适合 Agent 的仓库通常具备:
- 明确目录;
- 自动化测试;
- 快速反馈;
- 可机器验证的规则;
- 清晰脚本;
- 少量但高质量的说明;
- 稳定工具链。
6.4 把Agent任务设计成可验证闭环
最差的任务描述:
text
优化这个项目。
更好的描述:
text
降低API响应时间。
更适合 Agent 的任务:
text
分析 /api/orders P95 延迟偏高的问题。
目标:
1. 找出主要瓶颈;
2. 不修改对外API;
3. 修改后运行单元测试;
4. 执行性能测试;
5. P95至少下降20%;
6. 输出修改文件和验证结果。
Agent 最喜欢的不是"描述得特别长",而是:
目标清晰,并且能够验证。
未来 Harness 工程中非常关键的一条原则就是:
text
任务
↓
执行
↓
验证
↓
失败
↓
重新执行
↓
通过
而不是:
text
任务
↓
模型说完成了
↓
相信它
6.5 从Prompt Engineering升级到Harness Engineering
过去两年,很多团队优化 AI 的方式是:
text
改Prompt
↓
再改Prompt
↓
继续改Prompt
但 Agent 进入真实工程后,需要优化的变量明显更多。
包括:
| 方向 | 需要解决的问题 |
|---|---|
| Context | Agent应该看到什么? |
| Tools | Agent可以使用什么? |
| Sandbox | Agent可以操作到哪里? |
| Approval | 哪些动作必须批准? |
| State | 长任务怎么恢复? |
| Feedback | 如何知道执行结果? |
| Verification | 如何证明真的完成? |
| Observability | 如何排查Agent失败? |
因此我认为 Harness 开源最重要的影响并不是:
"大家可以自己魔改 Codex。"
而是它会推动越来越多开发者意识到:
Agent 工程的核心能力正在从 Prompt Engineering 迁移到 Harness Engineering。
如果本文对你理解 Codex Harness 有帮助,欢迎点赞、收藏、关注。后续可以继续深入拆解Agent Harness 的完整落地方案。