1. Codex CLI 多轮工具回路为什么慢:从 Responses WebSocket 的 continuation 复用说起
如果你用 Codex CLI 跑过稍微复杂一点的任务,比如让它先读几个文件、再改代码、再跑测试、再根据报错继续改,你大概率会有一种体感:单轮问答挺快,但一旦进入多轮工具调用,整体节奏就拖沓起来。每一轮工具结果回传,客户端都要重新发起一次请求,把上下文重新组织、重新上传、重新等待服务端恢复状态。任务越长,这种固定开销被重复的次数越多。
Responses WebSocket 想解决的就是这件事。它不是把流式输出从 HTTP 换成 WebSocket 这么简单,而是把 Agent 多轮工作流里的 continuation 成本压下来。核心机制是:连接建立一次,后续每一轮只发送previous_response_id加上新增的 input items,服务端在活跃连接上保留最近一次响应的内存态上下文,continuation 直接命中这条低延迟路径。官方公开的量化数据是,20 次以上工具调用的 rollout,端到端最多约 40% 提速。
这篇内容聚焦一个具体问题:Codex CLI 通过 TaoToken 统一 Key/API 通道接入 Responses WebSocket 时,config.toml骨架怎么写,以及怎么验证 continuation 复用是否真的生效。适合已经在用 Codex CLI、想让 Agent 多轮回路更顺的人。下面所有配置都以 TaoToken 的 OpenAI-compatible 基址为准,你可以直接复制改 Key 就能跑。
先明确一个概念边界:WebSocket 是传输层协议,Responses events 是应用层协议。Codex CLI 侧通过wire_api = "responses"走 Responses 协议栈,通过supports_websockets = true声明 provider 支持 WebSocket 传输,通过responses_websockets_v2 = true打开客户端侧的新版集成能力。这三者配合,才构成完整的 Responses WebSocket 链路。少任何一个,都会退回到普通 HTTP 流式路径,continuation 复用也就无从谈起。
2. TaoToken 统一 Key 接入 Codex CLI 的前置准备:Base URL、Key 与 Model ID 三件套
在写config.toml之前,先把三件套确认清楚:Base URL、API Key、Model ID。这三样是任何 OpenAI-compatible 接入的根基,Codex CLI 也不例外。
Base URL 用 TaoToken 的 API 地址:https://taotoken.net/api。注意这里不要带任何查询参数,Codex CLI 会在这个基址后面拼接/v1/responses之类的路径。API Key 在控制台的 API Keys 页面创建,创建后只显示一次,复制下来存好。Model ID 填你实际要用的模型标识,比如gpt-5.4这类,具体以你账号下可用的模型为准。
如果你还没有 Key,先去控制台建一个。地址是 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite ,登录后点创建,把生成的 Key 复制到本地环境变量里。推荐用环境变量而不是直接写进配置文件,这样配置文件可以进版本库而不会泄露密钥。
export OPENAI_API_KEY="sk-你的TaoToken密钥"设置完之后用echo $OPENAI_API_KEY确认一下,输出非空即可。这一步看起来简单,但后面 401 报错十有八九是这里没生效,比如你在一个终端里 export,却在另一个终端里跑 Codex CLI,环境变量根本没传过去。
关于模型选择,Codex CLI 的 Agent 场景对推理能力要求比较高,建议选支持 reasoning 的模型。如果你不确定用哪个,可以先在模型对话页面手动试一轮,确认模型能正常响应、工具调用格式正确,再写进配置。模型对话入口: https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite 。
还有一个容易被忽略的点:Codex CLI 的requires_openai_auth和env_key要对应上。env_key = "OPENAI_API_KEY"表示 Codex CLI 会去读名为OPENAI_API_KEY的环境变量作为 Bearer Token。如果你用的是别的变量名,这里要同步改,否则鉴权会失败。
3. 可复制的 config.toml 骨架:Responses WebSocket 与 continuation 复用配置
下面这份config.toml骨架可以直接用。路径通常在~/.codex/config.toml,如果你用的是项目级配置,也可以放在项目根目录下,Codex CLI 会按优先级合并。先给完整骨架,再逐段解释。
model = "gpt-5.4" model_provider = "taotoken" disable_response_storage = true approval_policy = "never" sandbox_mode = "danger-full-access" model_supports_reasoning_summaries = true rmcp_client = true model_reasoning_effort = "xhigh" personality = "pragmatic" service_tier = "fast" [model_providers.taotoken] name = "taotoken" base_url = "https://taotoken.net/api" env_key = "OPENAI_API_KEY" wire_api = "responses" supports_websockets = true requires_openai_auth = true [features] unified_exec = true shell_snapshot = true steer = true skills = true powershell_utf8 = true collaboration_modes = true fast_mode = true multi_agent = true responses_websockets_v2 = true逐段说明关键项。model_provider = "taotoken"指向下面[model_providers.taotoken]这一段,名字要一致。base_url = "https://taotoken.net/api"是 TaoToken 的 API 基址,Codex CLI 会在此基础上拼接 Responses 路径。env_key = "OPENAI_API_KEY"指定从哪个环境变量读 Key。wire_api = "responses"是关键,它让 Codex CLI 走 Responses 协议栈而不是 Chat Completions 兼容层,只有走 Responses 才有previous_response_id这套 continuation 语义。
supports_websockets = true声明这个 provider 支持 Responses over WebSocket。responses_websockets_v2 = true在[features]段里,是客户端侧的功能开关,打开后 Codex CLI 才会尝试用 WebSocket 传输 Responses 事件。这两个要同时为 true,链路才会真正走 WebSocket。
disable_response_storage = true对应store=false,服务端不持久化响应状态,最近响应只保留在连接本地内存里。这对隐私和 ZDR 友好,但代价是更依赖活跃连接——连接断了或者引用的previous_response_id不在缓存里,就会收到previous_response_not_found。所以生产环境必须做好重连和续跑。
model_reasoning_effort = "xhigh"和service_tier = "fast"是 Agent 场景的调优项,前者拉高推理投入,后者走快速通道。approval_policy = "never"和sandbox_mode = "danger-full-access"让工具调用不需要人工审批,适合自动化回路,但你要清楚这意味着 Codex CLI 可以自由执行命令,别在敏感环境里这么配。
配置写完后,用codex --version确认 CLI 能正常启动,再用codex config get model_provider之类的命令确认配置被读到。不同版本命令略有差异,以你本地codex --help为准。
4. 验证 continuation 复用是否生效:从单轮请求到多轮工具回路的实测动作
配置写完不代表链路就通了,得实际验证。验证分两步:先确认 WebSocket 连接能建立、单轮请求能返回,再确认多轮 continuation 真的复用了连接和响应状态。
第一步,跑一个最简单的单轮请求,观察是否走 WebSocket。启动 Codex CLI 后发一条简单指令,比如让它读一个文件。如果配置正确,你会看到连接建立、事件流返回。如果这里就报错,先看第 5 节的排障。
第二步,构造一个必然触发多轮工具回路的任务。比如让 Codex CLI 做这样一件事:先列出当前目录的文件,再读取其中一个文件的内容,然后基于内容生成一段总结。这个任务至少会触发两次工具调用(列目录、读文件),加上最终的文本生成,构成一个典型的多轮回路。
在 WebSocket 模式下,第一轮response.create会返回一个response.created事件,里面带response.id,比如resp_abc123。工具调用完成后,第二轮response.create会带上previous_response_id = "resp_abc123",input 里只放新增的function_call_output,而不是把整段历史重发。如果你能在日志里看到第二轮请求的 payload 里只有增量 items,说明 continuation 复用生效了。
怎么看到这个 payload?Codex CLI 一般有 verbose 或 debug 日志开关,具体参数看codex --help。打开后,日志里会打印每一轮发送的事件。重点看两处:一是第二轮请求是否带previous_response_id,二是 input 数组长度是否明显小于完整历史。如果第二轮把整段历史又发了一遍,说明 continuation 没生效,可能退回到了 HTTP 路径。
还有一个更直接的验证方式:观察连接是否复用。WebSocket 连接建立一次后,多轮请求应该在同一条连接上完成。如果每轮都重新握手,说明supports_websockets或responses_websockets_v2没生效。你可以在日志里搜101 Switching Protocols或Upgrade: websocket,正常应该只在开头出现一次。
实测下来,一个 3 到 5 轮工具回路的任务,在 WebSocket 模式下第二轮之后的请求体明显更小,响应也更快进入response.output_text.delta。这就是 continuation 复用带来的直接体感。如果你跑的是 20 轮以上的长任务,提速会更明显。
5. 本篇常见错排查:401、local proxy failed、reading choices 与 OAuth 报错对照
接入过程中最常见的几类报错,这里逐个对照。
401 Unauthorized。这是鉴权失败,九成是 Key 问题。先确认echo $OPENAI_API_KEY有输出,再确认env_key写的变量名和实际 export 的一致。如果你在配置文件里直接写了 Key 而不是用环境变量,检查有没有多余空格或引号。还有一种情况是 Key 被撤销或过期,去控制台重新建一个。注意 TaoToken 的 Key 是 Bearer Token 形式,请求头是Authorization: Bearer <key>,不要写成别的格式。
local proxy failed。这个报错通常出现在客户端尝试建立连接但本地网络层出问题的时候。先确认base_url写的是https://taotoken.net/api,没有多余路径或拼写错误。再确认本机网络能正常访问该地址,可以用curl -I https://taotoken.net/api看返回。如果 curl 通但 Codex CLI 不通,检查是不是有本地环境变量覆盖了配置,比如HTTP_PROXY之类的设置干扰了连接。
reading choices 相关报错。这类报错一般出现在响应解析阶段,说明客户端拿到的响应格式和预期不符。最常见的原因是wire_api没设成responses,导致 Codex CLI 按 Chat Completions 的格式去解析 Responses 的返回。检查wire_api = "responses"是否写对。另一个原因是模型返回了非标准事件,比如工具调用格式不对,这时候换一个模型试试,或者降低model_reasoning_effort看是否是推理过程干扰了输出格式。
OAuth 相关报错。Codex CLI 有些版本会尝试走 OAuth 流程,如果你用的是 API Key 接入,要确保requires_openai_auth = true配合env_key走 Key 鉴权,而不是触发 OAuth。如果报错里出现 OAuth 字样,检查是不是 CLI 版本默认走了登录流程,可以在配置里显式指定 provider 和鉴权方式,或者用codex login --api-key之类的命令直接注入 Key。具体命令以你本地版本为准。
previous_response_not_found。这个报错说明 continuation 引用的previous_response_id不在连接本地缓存里。原因通常是连接断了重连后,缓存已经清空,但客户端还在引用旧的 response id。解决办法是重连后开启新的 response chain,不要跨连接引用旧的 id。如果你用了disable_response_storage = true,这个问题会更常见,因为服务端没有持久化回退路径。
websocket_connection_limit_reached。WebSocket 连接有 60 分钟时长上限,到点会返回这个错误。生产环境必须做重连和续跑,长任务要分段。重连后从最近的 checkpoint 继续,而不是从头再来。
排障时如果拿不准,先去接入文档对照一遍配置项: https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 。文档里有完整的参数说明和示例。
6. 把 Responses WebSocket 用进长期 Agent 工作流:Coding Plan 与统一 Key 的配合
单次验证通过之后,下一步是把它用进长期的 Agent 工作流。这里有两个方向值得考虑。
一是把 Codex CLI 的配置固化下来,作为团队的标准接入方式。统一 Key 的好处是所有成员用同一个 TaoToken Key,计费和权限集中管理,不用每个人各自申请。配置文件可以放进项目模板,新成员拉下来改一下环境变量就能跑。注意 Key 不要硬编码进配置文件,用环境变量注入。
二是针对长任务做续跑设计。WebSocket 连接 60 分钟上限、store=false下缓存依赖活跃连接,这两点决定了长任务必须能断点续跑。建议在 Agent 工作流里加一层 checkpoint,每完成若干轮工具调用就记录一次状态,连接断了之后从最近的 checkpoint 重建 response chain。这样即使连接中断,也不会丢掉全部进度。
如果你跑的是高频、长时间的编码 Agent 任务,可以了解一下 Coding Plan: https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite 。它针对长期编码和 Agent 场景做了额度与通道优化,配合 Responses WebSocket 的 continuation 复用,多轮回路的成本会更可控。
最后回到技术本身。Responses WebSocket 的价值不在于流式输出更花哨,而在于把 Agent continuation 的固定成本压到足够小。你配置里的wire_api = "responses"、supports_websockets = true、responses_websockets_v2 = true这三项,就是打开这条低延迟路径的钥匙。验证时盯住第二轮请求是否只发增量、连接是否复用,这两点确认了,链路就通了。剩下的,就是把它用进你真实的 Agent 工作流里,让多轮工具回路跑得更顺。