OpenAI 的 Codex Harness 是怎么搭的:把另一家的 Agent 骨架拆给你看

发布时间:2026/10/18 18:57:58
OpenAI 的 Codex Harness 是怎么搭的:把另一家的 Agent 骨架拆给你看
1. Codex Harness 的 app-server 到底在做什么一次请求的完整链路拆解很多人第一次听到 Codex Harness会以为它只是 OpenAI 给自家 CLI 套的一层壳。实际拆开看它更像一套把「界面」「Agent 执行」「模型推理」三层彻底解耦的骨架。你发一条消息模型只是链路上的一环真正决定体验的是 harness 怎么组装上下文、怎么暴露工具、怎么管理执行生命周期、怎么在轮次之间搬运状态。Codex Harness 的核心是两条协议。第一条是 app-server跑在界面和 harness 之间基于 stdio 的 JSON-RPC原语是 thread / turn / item。第二条是 Responses API跑在 harness 和推理之间是 OpenAI 为 Agent 工作流重做的接口内置网页搜索、图像生成等能力。这两条协议把三层隔开界面层可以是 CLI、桌面 App、IDE 插件Agent 层是 Rust 实现的 harness推理层可以是 OpenAI 也可以是任何兼容 Responses 的提供方。这个设计对想理解 Codex 工具链内部设计的开发者来说价值在于它把「换模型要重写执行逻辑、换前端要重写 Agent 循环」的耦合成本提前变成了「写一份协议文档并守住它」的治理成本。app-server 的 thread / turn / item 原语、item 的序列化格式、错误码约定一旦定下就成了生态契约。我试过用 app-server 协议接自己的前端最直观的感受是你不需要重新实现一遍 Agent 循环只需要按协议发 JSON-RPC 消息就能复用 Codex 的整套执行机械结构。Codex App 本身也跑在 app-server 上所以它不是演示用的旁支接口而是产品能力的入口。上下文构建是 harness 里最容易被低估的一环。推理之前harness 要先组装上下文三个约束同时起作用尺寸、灵活度、可缓存性。尺寸不只是 token 预算问题上下文越长里面出现互相矛盾信息的概率越高模型越容易混乱。灵活度要求无论装了多少 skill、插件、MCP server体验都应稳定。可缓存性要求组装方式要保住降低延迟和成本的机会。这三条会互相打架。一个把所有能力都方便暴露出来的注册表同时也可以变成每个请求里又大又常变的一块而这一块恰恰会破坏前缀缓存。Codex 的两条对策是延迟加载工具和给 skill 列表设上限。部分工具被标记为 deferred不直接进上下文窗口之后通过 tool search 被发现。可用 skill 列表被限制在最大上下文窗口的 2%超出之后逐步缩短每条描述。这里有个关键细节tool search 把新发现的工具注入窗口末尾而不是中间目的是保住已缓存的那一段。推理侧前缀缓存按「从开头起有多少 token 完全一致」命中任何每次请求都可能变的东西只要放在前缀里就会让缓存整体失效。异步动作是 harness 要管理的第二类问题。spawn_agent 创建一个新 Agent 实例send_input 向它继续投递内容父 Agent 可以等某个子 Agent也可以把它关掉。后台终端用同一套生命周期启动进程、通过 stdin 持续喂输入、需要结果时等待指定时长。子 Agent 因此是一个持续交互不是一次返回完成答案的函数调用。这对自建 harness 的启示是生命周期管理带出三个具体工程要求。身份每个子 Agent 或后台终端都要有稳定标识否则后续输入不知道投给谁。终止父 Agent 必须能主动关掉一个子任务不能只依赖它自己结束。等待语义send_input 之外还要有「等待指定时长」的接口表达「我最多再等你这么久」而不是无期限阻塞。文件系统工具的设计思路也值得单独拎出来工具设计要贴合模型已经学会使用的接口而不是贴合工程师觉得优雅的接口。从 GPT-5 起的模型被训练成用 apply_patch 编辑和创建文件把改动表达成 diff搜索和导航走 shell 工具模型会本能地去用 ripgrep所以 harness 在你没装时自带一份 ripgrep。Windows 上模型被训练过原生用 PowerShell跑在 Windows 就直接写 PowerShell。所有文件系统交互都要过沙箱层实现随操作系统而变。macOS 用 SeatbeltLinux 用 BubblewrapWindows 用自研账户沙箱。Windows 那套最终方案是「提升权限沙箱」安装时创建两个专用本地账户 CodexSandboxOffline 和 CodexSandboxOnline命令在受限令牌下以这些账户运行文件系统用 ACL、网络用防火墙规则、凭据用 DPAPI 存放。原则是每个 Codex 命令从一开始就进沙箱其所有子孙进程都留在同一个边界内。审批疲劳是沙箱带来的实际问题。长任务反复停下来要权限会把用户推向 full access 模式。Auto Review 的目标是对合理动作减少打断同时把数据外泄挡在外面。它跑成独立子 Agent只读权限且不能再派生子 Agent。评审者拿到授权与风险分类的指引、对话记录、拟议工具调用。关键在于授权与影响是两个独立问题删 .git 可能是用户明确要求的操作没人要求时删掉它就是销毁用户本以为保留着的项目历史。2. 接入 TaoToken 统一 Key/API 通道的前置准备理解了 Codex Harness 的骨架之后下一步是把它接到一个统一的 API 通道上。TaoToken 提供的就是这样一个通道一个 Key 可以走多家模型Base URL 统一省去你在多个平台之间来回切换配置的麻烦。官网入口是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 。前置准备分三件事拿到 Key、确认模型 ID、确认你的客户端支持自定义 Base URL。这三件事缺一不可尤其是第三件很多人在配置阶段卡住就是因为用的客户端把 Base URL 写死了。先说拿 Key。进入控制台后创建 API Key复制出来保存好。这个 Key 只在创建时完整显示一次关掉页面就看不到了。如果你同时用多个工具建议按工具分别建 Key方便后面排查问题时定位是哪个工具在消耗额度。控制台地址是 https://taotoken.net/console API Keys 管理页是 https://taotoken.net/api-keys 。再说模型 ID。TaoToken 的模型 ID 命名和上游保持一致你在配置里填的 Model ID 必须和平台文档里列出的完全一致大小写、连字符都不能错。常见的错误是把展示名当成 Model ID 填进去结果请求返回模型不存在的报错。建议先在模型对话页面确认一下你要用的模型能正常响应再把它写进配置文件。模型对话入口是 https://taotoken.net/chat 。第三件事是确认客户端支持自定义 Base URL。Codex CLI 本身支持通过配置文件指定 providerClaude Code 支持通过环境变量指定 Base URLCline 这类插件在设置里有专门的 API Provider 选项。如果你用的是把 Base URL 写死的客户端那就没法接进来需要换一个支持自定义端点的工具。这里要提醒一点TaoToken 是统一的 API 通道不是让你绕过什么限制的工具。它的价值在于把多家模型的接入收敛到一个 Key、一个 Base URL 上减少配置管理成本。你在使用时要遵守各模型提供方的使用条款不要用它做违反条款的事情。配置之前建议先做一次最小验证用 curl 直接请求一次确认 Key 和 Base URL 都能通。这一步能帮你把「Key 错了」和「客户端配置错了」两类问题分开。命令如下curl https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: gpt-4o-mini, messages: [{role: user, content: ping}] }如果返回正常的 JSON 响应说明 Key 和通道都没问题接下来就可以进入客户端配置环节。如果返回 401先检查 Key 有没有复制完整、有没有多余空格如果返回 404检查 Base URL 是不是写成了 https://taotoken.net/api 而不是别的路径。对于长期编码和 Agent 场景如果你打算把 Codex 或 Claude Code 这类工具持续跑起来可以考虑 Coding Plan它在额度使用上更适合高频调用。入口是 https://taotoken.net/coding-plan 。如果你只是想先验证模型能不能通用模型对话页面就够了。3. 可复制的 app-server 与 Codex 配置片段这一节给你可以直接复制的配置片段。分两部分一部分是 Codex CLI 的配置文件把 provider 指向 TaoToken另一部分是 app-server 的启动参数让你能用 JSON-RPC 协议接自己的前端。先看 Codex CLI 的配置。Codex 的配置文件通常放在 ~/.codex/config.toml你需要新增一个 model provider 段落把 Base URL 指向 TaoToken并指定 API Key 的环境变量名。片段如下# ~/.codex/config.toml model gpt-4o-mini model_provider taotoken [model_providers.taotoken] name TaoToken base_url https://taotoken.net/api env_key TAOTOKEN_API_KEY wire_api chat这里有几个点要说明。base_url 填 https://taotoken.net/api 不要在后面多加 /v1具体路径由客户端拼接。env_key 指定的是环境变量名不是 Key 本身这样你的 Key 不会明文写在配置文件里。wire_api 填 chat 表示走 Chat Completions 形状的接口如果你的客户端版本支持 Responses 形状可以改成对应的值但要注意 TaoToken 当前对 Responses 形状的支持范围以平台文档为准。然后设置环境变量。在 shell 的配置文件里加上export TAOTOKEN_API_KEYsk-你的实际Key如果你用的是 Windows PowerShell对应写法是$env:TAOTOKEN_API_KEY sk-你的实际Key设置完之后重新打开一个终端或者 source 一下配置文件让环境变量生效。可以用 echo $TAOTOKEN_API_KEY 确认一下有没有值。接下来是 app-server 的启动。Codex 的 app-server 通过 stdio 跑 JSON-RPC启动命令通常是codex app-server --config ~/.codex/config.toml启动之后你的前端程序通过标准输入输出和它通信。一个最小的 JSON-RPC 请求长这样{ jsonrpc: 2.0, id: 1, method: thread/create, params: { model: gpt-4o-mini, modelProvider: taotoken } }发出去之后app-server 会返回一个 thread 对象里面包含 thread id。后续的 turn 和 item 都挂在这个 thread 下面。这个结构就是前面说的 thread / turn / item 三层原语thread 是一次会话turn 是一轮交互item 是轮次里的具体条目比如用户消息、模型回复、工具调用。如果你用的是 Claude Code配置方式不一样是通过环境变量指定 Base URL。片段如下export ANTHROPIC_BASE_URLhttps://taotoken.net/api export ANTHROPIC_API_KEYsk-你的实际Key注意 Claude Code 用的是 ANTHROPIC_BASE_URL 和 ANTHROPIC_API_KEY 这两个变量名不要和 Codex 的混用。如果你同时装了 Codex 和 Claude Code建议在各自的启动脚本里分别设置避免互相覆盖。对于 Cline 这类 VS Code 插件配置在设置界面里完成。API Provider 选 OpenAI CompatibleBase URL 填 https://taotoken.net/api API Key 填你的 KeyModel ID 填你要用的模型。Cline 的 MCP 配置如果需要单独指定也是在设置里加但注意不要让 MCP 直连生产数据库这是安全底线。如果你用 Codex 的 auth.json 方式管理凭据文件通常在 ~/.codex/auth.json结构大致是{ OPENAI_API_KEY: sk-你的实际Key }但更推荐用环境变量方式因为 auth.json 是明文存储一旦文件权限没设好容易泄露。用环境变量的好处是 Key 不进版本控制也不容易被误提交。配置完成后建议做一次完整的三件套检查Base URL 是不是 https://taotoken.net/api Key 是不是从 https://taotoken.net/api-keys 拿的Model ID 是不是平台文档里列出的。这三样任何一个错了请求都会失败而且报错信息不一定直观。4. 本地验证请求与成功结果确认配置写完之后必须做一次端到端验证确认请求真的能通。这一步不能省因为配置文件写对了不代表运行时环境变量生效了环境变量生效了不代表客户端真的读到了。第一步验证环境变量。在终端里执行echo $TAOTOKEN_API_KEY如果输出是空的说明环境变量没生效检查你是不是在设置之后重新打开了终端或者有没有 source 配置文件。如果输出是 Key 但带了多余空格用 export 重新设置一次去掉空格。第二步用 curl 直接打一次接口绕过客户端确认通道本身是通的curl -s https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: gpt-4o-mini, messages: [ {role: system, content: You are a helpful assistant.}, {role: user, content: 用一句话说明什么是 Agent harness} ], max_tokens: 100 }成功的话你会看到一段 JSON里面 choices 数组的第一项 message content 就是模型的回复。如果这一步就失败了那问题在 Key 或 Base URL不在客户端配置。第三步用 Codex CLI 跑一次真实请求。命令codex exec 列出当前目录下的文件并说明每个文件的作用如果配置正确Codex 会启动读取你的 config.toml用 taotoken 这个 provider 发请求然后返回结果。你会看到它调用了 shell 工具来列目录这就是 harness 在起作用它不只是把问题转发给模型还负责暴露工具、执行工具、把结果带回给模型。第四步验证 app-server 模式。启动 app-servercodex app-server --config ~/.codex/config.toml然后在另一个终端里用 echo 管道发一个 JSON-RPC 请求进去观察输出echo {jsonrpc:2.0,id:1,method:thread/create,params:{model:gpt-4o-mini,modelProvider:taotoken}} | codex app-server --config ~/.codex/config.toml如果返回了带 thread id 的 JSON说明 app-server 正常启动并且能读到你的 provider 配置。这一步验证的是协议层不是模型层所以即使模型请求失败只要 thread 创建成功就说明 app-server 本身没问题。成功结果的判断标准有三条curl 返回了 choices 数组且 content 非空Codex CLI 能完成一次带工具调用的任务app-server 能返回 thread 对象。三条都过说明从 Key 到通道到客户端到协议层整条链路是通的。如果某一步失败先看报错信息里的关键词。401 是认证问题检查 Key。404 是路径问题检查 Base URL。model not found 是 Model ID 问题检查拼写。connection refused 是网络问题检查你的网络环境能不能访问 https://taotoken.net/api 。timeout 是超时问题可能是模型响应慢也可能是网络链路不稳定。验证通过之后你就可以在这个基础上做更复杂的事情比如用 app-server 协议接自己的前端或者把 Codex 塞进你现有的工具链里。协议层的价值就在这里一旦通了上层怎么换都不影响底层。5. 本篇常见错误排查401、local proxy failed、reading choices、OAuth配置过程中最容易踩的坑集中在四类报错上。这一节按报错原文对照排查每条都给出可操作的检查步骤。第一类401 Unauthorized。报错原文通常是{error:{message:Invalid API key,type:invalid_request_error}}或者401 Unauthorized: Incorrect API key provided排查顺序先确认环境变量有没有值echo $TAOTOKEN_API_KEY 看输出。如果为空说明环境变量没生效重新 export 一次。如果有值但报 401检查 Key 是不是复制时带了首尾空格或者复制不完整。再检查 Key 是不是已经过期或被删除去 https://taotoken.net/api-keys 确认一下。最后检查 Authorization 头的格式必须是 Bearer 加空格加 Key少一个空格都会失败。第二类local proxy failed。报错原文通常是local proxy failed: dial tcp 127.0.0.1:7890: connect: connection refused这类报错说明你的客户端配置了本地代理但代理没启动。检查你的环境变量里有没有 HTTP_PROXY 或 HTTPS_PROXY 指向本地端口。如果有要么启动对应的服务要么把这两个变量清掉。在 Codex 的配置文件里也要检查有没有 proxy 相关字段有的话去掉。注意这里说的是清理本地代理配置不是让你去搭什么通道只是把多余的配置项删掉让请求直连。第三类reading choices 相关报错。报错原文通常是failed to parse response: reading choices: unexpected end of JSON input或者cannot read property choices of undefined这类报错说明客户端收到了响应但响应结构不是它预期的形状。常见原因是 Base URL 路径不对比如你填了 https://taotoken.net/api/v1 客户端又自己拼了一次 /v1变成 /api/v1/v1/chat/completions返回 404 的 HTML 页面客户端拿去解析 JSON 就报这个错。检查你的 Base URL 是不是 https://taotoken.net/api 不要多加路径。另一个原因是 wire_api 配置和实际接口形状不匹配比如你配了 responses 但通道返回的是 chat 形状改成 chat 再试。第四类OAuth 相关报错。报错原文通常是OAuth token exchange failed或者authentication failed: invalid_grant这类报错说明客户端在走 OAuth 流程而不是用 API Key。Codex 和 Claude Code 都支持 OAuth 登录方式但如果你要用 TaoToken 的 Key就必须关掉 OAuth 流程改用 API Key 方式。检查你的配置里有没有 auth 相关字段指向 OAuth有的话删掉改成 env_key 方式。Claude Code 的话确认你设置的是 ANTHROPIC_API_KEY 而不是走登录流程。除了这四类还有几个零散但常见的坑。模型 ID 大小写错误比如 gpt-4o-mini 写成 GPT-4O-MINI会报 model not found。配置文件路径错误Codex 找不到 config.toml 会回退到默认配置表现是配置没生效但也不报错。环境变量在 IDE 里不生效因为 IDE 启动时读的是它自己的环境不是你的 shell 环境需要在 IDE 的设置里单独配。排查的通用方法是分层定位先用 curl 确认通道通不通再用客户端确认配置读没读到最后用 app-server 确认协议层正不正常。每一层单独验证不要混在一起猜。报错信息里的关键词是最重要的线索401 看认证404 看路径parse 看响应形状OAuth 看认证方式。如果你在排查过程中需要确认模型本身能不能用可以去 https://taotoken.net/chat 发一条消息试试。如果模型对话页面能通但客户端不通问题一定在客户端配置不在通道。如果模型对话页面也不通问题在 Key 或通道去 https://taotoken.net/api-keys 检查 Key 状态。接入文档在 https://taotoken.net/doc 里面有各客户端的详细配置说明。6. 从 Harness 拆解到实际接入把统一通道用起来把 Codex Harness 的骨架拆完之后你会发现它的设计思路和统一 API 通道的价值是相通的都是把变化的部分隔离开让不变的部分稳定下来。Harness 把界面、Agent、推理三层用协议隔开换任何一层都不影响其他层。统一通道把多家模型的接入收敛到一个 Key、一个 Base URL 上换模型不用换配置。实际接入时建议按这个顺序推进。先把 curl 验证跑通确认 Key 和 Base URL 没问题。再配 Codex CLI 的 config.toml用 codex exec 跑一次带工具调用的任务确认 harness 能正常工作。然后启动 app-server用 JSON-RPC 发一个 thread/create确认协议层能通。最后再考虑接自己的前端或者做更复杂的集成。对于长期编码和 Agent 场景如果你打算把 Codex 或 Claude Code 持续跑起来Coding Plan 在额度使用上更适合高频调用入口是 https://taotoken.net/coding-plan 。如果你只是想先验证模型能不能通用模型对话页面就够了入口是 https://taotoken.net/chat 。API Key 的创建和管理在 https://taotoken.net/api-keys 接入文档在 https://taotoken.net/doc 。有一个细节值得单独提醒Codex 的 app-server 协议是开源的你可以把它当蓝图也可以当底座。当蓝图的意思是你参考它的 thread / turn / item 设计在自己的项目里实现一套类似的协议。当底座的意思是你直接启动 app-server用 JSON-RPC 和它通信复用它的整套执行机械结构。两种用法都合法选哪种取决于你想投入多少工程成本。如果你选择当底座那么配置的重点就是让 app-server 能读到正确的 provider 配置。config.toml 里的 model_provider 指向 taotokenmodel_providers.taotoken 里的 base_url 指向 https://taotoken.net/api env_key 指向 TAOTOKEN_API_KEY。这三处对齐了app-server 就能用统一通道发请求。如果你选择当蓝图那么配置的重点是理解协议分层的意义。界面层和 Agent 层之间用 JSON-RPCAgent 层和推理层之间用 HTTP 或 WebSocket。两层协议各自独立各自可替换。你的实现里也可以照这个结构来把变化隔离在协议边界上。最后说一个实际经验配置类问题里90% 的失败都出在三个地方Base URL 多写了路径、Key 带了空格、Model ID 拼错了。每次改配置之后先用 curl 验证一遍再动客户端。这样能把问题范围缩小到最小不用在多个环节之间来回猜。统一通道的价值就在于它把变量减少了一个 Key、一个 Base URL、一组 Model ID配置项越少出错的地方越少。