本文基于 OpenAI 2026 年 8 月 19 日发布的官方文章,以及 openai/codex 仓库 2026 年 8 月 25 日的源码(commit:70b5cfc)整理。

最近几天,“OpenAI 全面开源 Codex Harness”的消息几乎刷屏了。

不少文章把它描述成:OpenAI 把 Codex 的“发动机”整个送了出来,以后人人都能复刻一个 Codex。

这个说法有一半是对的,也有一半容易让人误解。

真正值得关注的,不是 OpenAI 突然上传了一个全新的仓库,而是它正式把原本服务于 Codex CLI 的执行系统,定位成了一个可以嵌入其他产品的开放 Agent 平台。

我实际读了一遍官方说明,并顺着 openai/codex 仓库看了 Agent Loop、工具路由、上下文压缩、沙箱审批和 App Server 协议。本文想回答三个问题:

  1. 这次到底开源了什么,哪些东西并没有开源?

  2. 一个真正能干活的 Agent Harness,比“调一次大模型 API”多了什么?

  3. 这些源码能抽象出哪些高频面试知识?


一、先说结论:这不是 Codex 第一次开源

Codex CLI 在 2025 年 4 月发布时就是一个开源的终端编程 Agent。OpenAI 在 2025 年 5 月的介绍文章中,也明确称它为 “lightweight open-source coding agent”。

到了 2026 年 1 月,OpenAI 已经公开介绍 Codex 的 Agent Loop;2 月又专门介绍了 App Server 的设计。也就是说,Agent Loop、沙箱、工具调用和 App Server 并不是 8 月 19 日突然从内部仓库里放出来的。

这次真正的新变化是:

OpenAI 不再只把 openai/codex 描述为一个开源 CLI,而是明确把其中的 Harness 和集成接口当成平台能力,让开发者把 Codex 的 Agent Loop 嵌入自己的产品。

官方现在给出了三层接入方式:

接入层 适合场景 你能获得什么
codex exec CI、脚本、一次性后台任务 非交互执行、结构化结果、明确退出状态
Codex SDK 应用代码里的 Agent 工作流 启动、恢复、流式读取任务
App Server Agent 本身就是产品的一部分 持久会话、事件流、中断、审批、完整生命周期

OpenAI 对边界说得很清楚:开源的是 Harness 和集成层;模型访问和托管服务仍然是独立的。官方开源清单还明确标出,Codex CLI、SDK 和 App Server 有公开源码,但 IDE Extension 与 Codex Cloud 没有开源。

所以,“Codex 全部开源了”并不准确。更准确的说法是:

Codex 的本地 Agent Runtime、核心执行逻辑和产品集成接口已经开放;模型权重、云端调度系统以及 IDE 扩展并没有因此全部开源。

官方资料:


二、Harness 到底是什么?

大模型本身更像一个“大脑”:它读入上下文,生成文字,或者提出一个工具调用请求。

但如果只写下面这段逻辑,它还不能算一个完整 Agent:

String answer = llm.chat(userMessage);

一个能在真实工程里连续工作几十分钟甚至几小时的 Agent,还需要解决很多模型之外的问题:

  • 当前任务、历史消息、文件变化该怎样组织进上下文?

  • 模型要求执行命令时,谁负责解析、校验和调用?

  • 工具失败以后,是结束、重试,还是把错误交还模型修正?

  • 上下文快满了,怎样保留关键状态并继续工作?

  • 删除文件、访问网络等危险动作,谁来审批?

  • 前端怎样实时展示命令、Diff、思考进度和审批卡片?

  • 浏览器关闭或网络断开后,长任务怎样恢复?

包住模型、负责这些执行问题的系统,就是 Harness。

可以把它记成一个公式:

Agent = Model + Context + Tools + Loop + State + Guardrails + Observability

这也是为什么同一个模型放进不同编程工具里,实际效果可能相差很大。模型能力决定上限,Harness 决定它能否稳定地接触真实环境、获得反馈并完成闭环。


三、源码第一课:Agent Loop 本质上是一个循环状态机

Codex 核心循环位于 codex-rs/core/src/session/turn.rs。源码注释直接说明了两种主要结果:

// 模型请求工具:执行工具,并在下一次采样时把结果发回模型
// 模型只返回 assistant message:记录消息,结束这一轮
pub(crate) async fn run_turn(...) -> CodexResult<Option<String>> {
    // ...
}

用大白话展开,就是:

  1. 收集用户输入、历史消息、当前工作区状态和可用工具;

  2. 请求模型;

  3. 如果模型返回工具调用,就执行工具;

  4. 把工具结果写回会话;

  5. 带着新结果再次请求模型;

  6. 直到模型不再调用工具,输出最终消息,或者任务被取消、失败。

很多入门 Agent Demo 也有 while 循环,但 Codex 的难点不在“有循环”,而在循环周围的工程状态。

例如 run_turn 接收了 SessionTurnContext、输入、预热后的模型连接和 CancellationToken。这说明一次任务至少同时关心:

  • 会话状态:历史、持久化、待处理输入;

  • 本轮快照:模型、权限、工具和环境配置;

  • 连接复用:同一轮的多次模型请求尽量复用会话;

  • 协作式取消:长任务必须能被安全中断;

  • 用户中途追加输入:运行中的 Agent 仍可能收到新的 steering。

源码里还有一个容易被忽略的细节:每次采样前会捕获一次 step_context,让“模型看到的上下文、当时声明的工具、稍后执行的工具调用”基于同一份请求视图。这和数据库的一致性快照很像——如果模型看到的是 A 配置,真正执行时却偷偷变成 B 配置,就会出现很难排查的竞态问题。

可迁移到普通后端的思想

不要把长任务写成一个无法观察、无法恢复的大方法。更好的设计是:

  • 把过程建模为显式状态;

  • 为每个动作生成稳定 ID;

  • 状态变化写入事件或持久化记录;

  • 支持超时、取消和幂等;

  • 区分“本轮不变量”和“可以动态变化的输入”。

这套思想不只适用于 Agent,也适用于订单工作流、任务调度、审批流和异步编排。


四、源码第二课:工具调用不是一堆 if-else,而是一条可扩展流水线

Agent 的工具越来越多以后,最容易出现这种代码:

if (name.equals("shell")) {
    // ...
} else if (name.equals("web_search")) {
    // ...
} else if (name.equals("apply_patch")) {
    // ...
}

这种写法很快会把业务逻辑、权限、日志、异常处理全部揉在一起。

Codex 用 ToolRouter + ToolRegistry + ToolRuntime 拆开这些职责。在 tools/router.rs 中,Router 同时维护:

pub struct ToolRouter {
    registry: ToolRegistry,
    model_visible_specs: Arc<[ToolSpec]>,
}

这里有个很值得学的划分:

  • model_visible_specs 决定模型“知道哪些工具、参数长什么样”;

  • registry 决定一个工具调用到来后,“真正交给哪个运行时执行”。

也就是说,工具描述工具实现被分开了。这类似 Java 后端里的接口契约与实现类,也类似 Spring 的 Controller 参数模型与 Service Bean。

Router 先把普通 Function Call、Tool Search Call、Custom Tool Call 统一转换成内部 ToolCall,再交给 Registry 分发。分发过程并不是只调用一次 handler,而是形成了完整生命周期:

解析调用 → 查找工具 → 校验 payload → PreToolUse Hook
→ 发出 started 事件 → 执行并记录 telemetry
→ PostToolUse Hook → 发出 completed/failed 事件 → 返回模型可见结果

tools/registry.rs 可以看到几个关键动作:

  • 工具不存在时,不让程序直接崩溃,而是构造模型能够理解的错误;

  • 调用 payload 与工具类型不匹配时快速失败;

  • 执行前的 Hook 可以阻断调用,甚至修改输入;

  • 执行阶段统一记录耗时、成功状态和标签;

  • 执行后的 Hook 可以追加上下文或拒绝将结果继续交给模型。

这里对应了几个高频设计模式:

  • 注册表模式:用名称找到对应执行器;

  • 策略模式:不同 Tool Runtime 实现同一执行契约;

  • 责任链 / 拦截器模式:前置 Hook、执行、后置 Hook;

  • 适配器模式:把不同来源的工具调用统一为内部结构;

  • 事件驱动:执行过程通过 started、completed、failed 暴露给外部。

为什么失败结果也要回填模型?

因为对 Agent 来说,工具报错不一定代表整个任务失败。

例如模型执行测试,发现编译错误。正确做法不是立刻结束任务,而是把 stderr 作为新观察结果写回上下文,让模型定位错误、修改代码、再次测试。

因此,Agent Loop 的核心不是“模型会调用工具”,而是:

环境反馈能否被结构化地带回下一轮推理,形成观察—行动—再观察的闭环。


五、源码第三课:上下文管理不是粗暴删除历史

Agent 工作时间越长,历史消息、代码片段和命令输出就越多。但模型的上下文窗口不是无限的。

最简单的处理是只保留最近 N 条消息,但这可能把“用户最初要做什么”“修改了哪些文件”“有哪些约束”一起删掉。

Codex 的 run_turn 会在采样后计算上下文 Token 状态。只有任务还需要继续,并且达到限制或收到切换窗口请求时,才触发自动压缩:

let should_roll_over = needs_follow_up
    && (new_context_window_requested || token_limit_reached);
​
if should_roll_over {
    run_auto_compact(..., CompactionReason::ContextLimit, ...).await?;
    continue;
}

对应源码位于 session/turn.rs。这里最重要的不是某个阈值,而是三个设计:

1. 压缩是循环的一部分,不是事后清理

上下文满了以后,Agent 并不一定结束当前任务。它压缩历史、建立新的上下文窗口,然后 continue 回到循环继续工作。

2. 保留任务状态,而不是保留所有原文

压缩的目标不是逐字保存过去,而是保存后续决策真正需要的状态:目标、约束、完成项、关键发现、文件变化和未解决问题。

3. 压缩以后仍要恢复必要环境

源码把 world_statestep_context 作为初始上下文重新注入。原因很直接:一份摘要只能告诉模型“发生过什么”,当前目录、权限、工具等环境状态仍需要重新对齐。

这和业务系统里的 Event Sourcing / Snapshot 很像:

  • 完整事件日志保留事实;

  • 快照降低恢复成本;

  • 快照不是凭空生成的新事实,而是已有状态的紧凑表示;

  • 恢复后仍要加载当前配置和外部世界状态。

OpenAI 给出的实验也说明 Harness 会直接影响模型效果:在 ARC-AGI-3 上,保留推理状态与上下文压缩让 GPT-5.6 Sol 的得分从 13.3% 提升到 38.3%,同时输出 Token 降低到原来的约六分之一。这个结果并不能直接等同于所有编程任务都会提升三倍,但它证明了一个方向:上下文工程不是单纯省 Token,也可能决定任务能不能做对。


六、源码第四课:安全不能只依赖 Prompt,必须由执行层兜底

如果 Agent 能运行 Shell、修改文件和访问网络,仅在 Prompt 里写一句“不要执行危险命令”是不够的。

Prompt 属于软约束:模型可能误解、遗忘,也可能被外部内容诱导。真正可靠的边界必须由模型无法绕过的代码和操作系统机制实现。

Codex 把安全拆成两层:

  • Sandbox:决定进程在技术上能读什么、写什么、能否联网;

  • Approval Policy:决定遇到边界动作时,是询问用户、自动拒绝,还是按规则放行。

protocol.rs 中,审批策略被建模成明确枚举,而不是散落的布尔值:

pub enum AskForApproval {
    UnlessTrusted,
    OnRequest,
    Granular(GranularApprovalConfig),
    Never,
}

其中 GranularApprovalConfig 还会分别控制沙箱提权、规则审批、Skill 脚本、权限请求和 MCP 交互。

这体现了几个安全原则:

  1. 最小权限:默认只给完成任务所需的权限;

  2. 策略与执行分离:模型提出动作,Harness 决定是否允许;

  3. 高风险动作人机协同:让用户确认具体命令、目录和原因;

  4. 默认拒绝优于默认放行:不支持的审批类型不能偷偷降级;

  5. 审计友好:每次工具调用都有 call ID、状态和事件记录。

另外,Codex 官方特别提醒:Codex 自带的 Shell 可以进入 Codex 的沙箱,但第三方 MCP 工具不一定受同一个沙箱保护,它们需要自己实现权限边界。这一点做企业 Agent 时非常重要:接上 MCP 不等于自动安全。


七、App Server:为什么 Agent 产品不能只返回一个 HTTP 响应?

传统接口通常是:客户端发起请求,服务端返回一次响应。

但一个 Agent 任务可能持续很久,中途产生几十个事件:

  • 模型开始生成;

  • 命令开始执行;

  • 输出持续增加;

  • 文件 Diff 更新;

  • Agent 请求用户审批;

  • 用户追加要求;

  • 任务完成、失败或被中断。

这显然不是一个简单的 POST /chat -> String 能优雅表达的。

Codex App Server 使用基于 stdio/JSONL 的双向 JSON-RPC 风格协议,并抽象出三个核心层级:

  • Thread:一段可持久化、恢复、分叉的会话;

  • Turn:用户的一次任务输入;

  • Item:一轮中产生的消息、工具调用、Diff 等具体条目。

app-server-protocol 可以看到 thread/startthread/resumethread/fork;同一协议还声明了 turn/startturn/steerturn/interrupt,以及 item/starteditem/completedturn/completed 等通知。

一个典型流程是:

客户端 thread/start
  → 客户端 turn/start
  → 服务端持续发送 item/started、delta、item/completed
  → 必要时服务端反向发起 approval request,并等待客户端回复
  → 服务端发送 turn/completed

这里最关键的是 双向:不仅客户端能请求服务端,服务端也能在需要审批时反向请求客户端,并暂停当前 Turn。

这和你做过的 SSE Agent 项目非常接近,但可以再向前一步理解:

  • SSE 很适合服务端向浏览器持续推送事件;

  • 双向 JSON-RPC 同时表达请求、响应、通知和反向审批;

  • 真正的长任务系统还必须把服务端状态作为事实来源,不能依赖浏览器页面一直开着;

  • 客户端重连后,应按持久化 Thread 恢复一致的时间线。

这也是 App Server 比“封装一次 API 调用”更有价值的地方:它暴露的是 Agent 生命周期,而不只是模型文本。


八、从源码再抽象一步:一个生产级 Agent Harness 至少要有七层

读完这些代码,可以把 Harness 简化成下面七层:

层次 责任 Codex 中的对应概念
接入层 CLI、SDK、产品 UI Exec、SDK、App Server
协议层 生命周期与事件契约 Thread / Turn / Item、JSON-RPC
编排层 推理与工具之间循环 run_turn
上下文层 历史、状态、压缩 Session、Context、Compaction
工具层 注册、路由、执行、Hook ToolRouter、Registry、Runtime
安全层 沙箱、权限、审批 Sandbox、Approval Policy
可观测层 事件、日志、指标、Diff lifecycle events、telemetry

这张表比背某个 Agent 框架的 API 更重要。

框架会变,但这些问题不会消失。以后无论面试官问 LangGraph、Spring AI、MCP,还是让你设计一个自动修 Bug Agent,你都可以沿着这七层展开,而不是只说“调用大模型然后调用工具”。


九、这份源码最值得普通开发者学什么?

不建议一上来从头读完整个 Rust Monorepo。投入产出比最高的顺序是:

  1. 先读 core/src/session/turn.rs,看懂一次 Turn 怎样循环;

  2. 再读 core/src/tools/router.rsregistry.rs,看工具怎样统一分发;

  3. 接着看 Context 与 Compaction,理解长任务怎样续命;

  4. 再看 Sandbox、Approval,理解 Agent 的安全边界;

  5. 最后看 App Server Protocol,把内部执行映射成产品事件。

如果你是 Java 后端方向,不需要为了读 Codex 专门把 Rust 学到很深。重点是用熟悉的概念翻译它:

  • Rust trait ≈ Java interface;

  • Tool Runtime ≈ 策略实现类;

  • Registry ≈ Bean 容器 / Handler Map;

  • Hook ≈ 拦截器 / 责任链;

  • Thread 状态 ≈ 长任务聚合根;

  • lifecycle event ≈ 领域事件;

  • CancellationToken ≈ 协作式取消信号;

  • Compaction ≈ 事件日志上的状态快照。

能完成这种翻译,才算真正从源码里学到了东西。


十、面试 Q&A

Q1:什么是 Agent Harness?它和大模型有什么区别?

大模型负责根据上下文进行推理和生成;Harness 是包在模型外面的执行系统,负责上下文、工具调用、循环控制、状态持久化、权限、安全和可观测性。模型提出“做什么”,Harness 决定“怎样安全、可靠地做完”。

Q2:Agent Loop 的基本流程是什么?

把用户输入和上下文发给模型。若模型返回工具调用,Harness 执行工具并把结果追加到上下文,再次调用模型;若模型返回最终消息且没有后续动作,则结束。工程上还必须处理取消、重试、超时、并发输入和上下文限制。

Q3:为什么 Agent Loop 可以看成状态机?

因为它有明确状态与转移:等待输入、模型推理、等待工具、等待审批、继续推理、完成、失败、中断。每次事件都会推动状态迁移。显式状态机比嵌套回调更容易观测、恢复和测试。

Q4:Codex 的工具系统使用了哪些设计模式?

Tool Registry 是注册表模式;不同 Tool Runtime 是策略模式;统一不同工具消息是适配器模式;Pre/Post Tool Hook 是责任链或拦截器模式;生命周期通知则体现事件驱动思想。

Q5:为什么工具描述和工具实现要分离?

模型只需要看到稳定的名称、描述与参数 Schema,而执行侧需要权限、依赖、超时和实现细节。分离后可以独立控制工具是否对模型可见、替换实现、延迟加载,并减少模型契约与运行时逻辑的耦合。

Q6:工具调用失败以后应该直接结束 Agent 吗?

不一定。参数非法、权限拒绝或不可恢复错误可以终止;编译失败、测试失败等环境反馈通常应结构化返回模型,让模型修正方案后继续。关键是区分可恢复错误与致命错误,并设置次数、时间和 Token 预算,避免死循环。

Q7:为什么长任务需要 Context Compaction?

因为消息、代码和工具输出会持续占用有限上下文。Compaction 把旧历史压缩成目标、约束、已完成工作、关键结论和待办,让 Agent 能在新窗口继续任务,同时降低 Token 成本。

Q8:Compaction 有哪些风险?

摘要可能遗漏约束、错误合并事实,或丢失工具调用之间的因果关系。应保留原始事件日志,使用结构化摘要,重新注入当前环境状态,并通过评测检查压缩前后的任务完成率,而不只是看 Token 降低多少。

Q9:Sandbox 和 Approval 有什么区别?

Sandbox 是技术执行边界,限制进程实际能访问的文件、网络和系统资源;Approval 是决策流程,决定越界动作是否需要用户或策略授权。审批不能替代沙箱,因为模型或客户端都可能出错;沙箱也不能替代审批,因为有些高风险动作在业务上需要明确同意。

Q10:为什么安全规则不能只写在 Prompt 里?

Prompt 是概率性的软约束,可能被误解或被提示注入干扰。文件权限、网络策略和危险命令拦截必须由模型无法跳过的执行层实现,遵循最小权限、默认拒绝和可审计原则。

Q11:Thread、Turn、Item 分别是什么?

Thread 是可持久化的一段会话;Turn 是用户的一次输入及其完整执行;Item 是 Turn 内部的消息、命令、工具调用、Diff 等事件单元。分层以后,系统可以恢复会话、流式渲染进度、定位单次工具调用并支持中断。

Q12:为什么 App Server 使用双向协议,而不是普通 REST?

Agent 一次任务会持续产生事件,还可能主动向用户请求审批。双向协议能同时表达客户端请求、服务端通知和服务端反向请求。普通 REST 仍可用于外围接口,但通常还要配合 SSE、WebSocket 或消息队列承载长任务事件。

Q13:怎样避免 Agent 无限调用工具?

设置最大轮数、总 Token、总耗时和单工具预算;区分可重试错误;检测重复调用与状态无进展;支持人工中断;在结束条件满足时由状态机终止,而不是完全相信模型会主动停下。

Q14:怎样设计 Agent 的可观测性?

至少记录 Thread ID、Turn ID、Call ID、模型耗时、Token、工具参数摘要、工具耗时、结果状态、审批决定、错误分类和文件 Diff。日志用于排错,指标用于发现趋势,Trace 用于还原一次完整任务链路。

Q15:Codex Harness 开源后,开发者是不是可以免费复刻完整 Codex?

不能这样理解。Harness 代码使用 Apache-2.0 许可证,可以研究、修改和嵌入产品,但模型推理仍需要相应服务,Codex Cloud、IDE 扩展等托管产品也没有因此全部开源。开源的是可复用执行层,不是所有模型与云端基础设施。


结语

Codex Harness 开源最值得关注的地方,不是“又多了一个 Agent 框架”,而是我们终于能较完整地观察一个成熟编程 Agent 怎样处理真实工程问题。

源码给出的答案并不神秘:循环、状态机、工具注册、拦截器、事件流、权限策略、上下文快照——很多都来自传统软件工程。

真正的新难点,是把这些成熟思想组合在一个概率性模型周围:模型会犯错,工具会失败,上下文会溢出,用户会中途改变要求,任务还必须可以观察、暂停、恢复和审计。

所以,对开发者最有价值的结论可能是:

Agent 的竞争不只是谁接入了更强的模型,而是谁能为模型建立更可靠的环境、反馈回路与安全边界。

这也是 Harness 工程真正值得学习的原因。


参考资料

Logo

葡萄城是专业的软件开发技术和低代码平台提供商,聚焦软件开发技术,以“赋能开发者”为使命,致力于通过表格控件、低代码和BI等各类软件开发工具和服务,一站式满足开发者需求,帮助企业提升开发效率并创新开发模式。

更多推荐