2026年3月,openai/codex 官方仓库里开了一个 issue,到9月依然没修复:每次有耗时命令在后台运行,Codex 都会通过重发整段对话历史给 API 来确认它是否结束了,一遍又一遍。一次60秒的构建会变成大约12次完整的 API 调用,却没有任何新工作被完成。
为什么 Codex 光是等一个命令结束就要烧掉 token?
因为这是一个真实的 bug,记录在官方仓库里,至今没修复。当一个命令在后台运行时(比如 cargo build、npm test),Codex 会用一个叫 write_stdin 的工具调用去确认它是否完成。如果还没完成,返回结果是空的,代理就会在一次全新的完整 API 调用里重发整段对话历史,再试一次。
通过阅读 openai/codex 的源码确认:两次轮询之间的等待下限(MIN_EMPTY_YIELD_TIME_MS)直接写死在二进制里,固定为 5000 毫秒,没有配置项可以调整。这和 issue #13733 里的描述完全吻合:一次60秒的 cargo build 会产生大约12轮轮询,每一轮都要把整段对话重新发给 API。
损失有多大,取决于你会话的历史长度
每次轮询的成本,和历史记录大小乘以轮询次数成正比。在一个较短的会话里,这几乎不会被注意到。但在一个较长的会话里,比如涉及遗留代码、大规模重构,或者已经讨论过几十个文件,单是等一个10分钟的构建,就可能产生100多次完整的 API 调用,每一次都带着之前说过的所有内容。
这个 issue 已经链接了4个报告相同模式的重复 issue(#10957、#8656、#6113、#3968),r/codex 上那条"Codex is unusable now"的帖子,不到24小时就拿到700多个赞和300多条评论,足以说明这有多让人烦躁。官方文档记录的 /goal 命令,可以让代理自己设定并恢复一个任务,反而放大了这个问题:每一次自动恢复,都是多一轮重发整段对话。
| 常量或配置项 | 数值 | 控制什么 |
|---|---|---|
MIN_EMPTY_YIELD_TIME_MS | 5000 毫秒 | 空轮询的等待下限,写死在源码里 |
MAX_YIELD_TIME_MS | 30000 毫秒 | 非空 stdin 写入的等待上限 |
background_terminal_max_timeout | 300000 毫秒(默认) | config.toml 里空轮询的可配置上限 |
不等 OpenAI 修复,自己能解决吗?
只能部分解决。目前没有办法把轮询完全消除:5秒的下限写死在二进制里,而实际观察到的行为是,代理自己请求的就是短等待,由它决定了轮询的节奏。能做的,是缩小损失的范围。
在运行一个你已经知道会耗时很久的命令之前,先开一个隔离的对话分支:
/fork
/fork 是 Codex CLI 官方的斜杠命令:它会把当前对话克隆成一个带有独立 ID 的新对话,不影响原始对话。把耗时命令放到 fork 里运行。每次轮询依然会重发历史记录,但重发的是 fork 的历史,不是你主会话的历史,噪音被隔离开,用完即可丢弃。命令结束后回到主会话,如果需要清理 fork,可以用:
/compact
想实时查看损失,/status 会显示当前会话配置和 token 用量。
针对这个行为,还有一个真实存在的配置项 background_terminal_max_timeout,写在 config.toml 里:
background_terminal_max_timeout = 300000
但要注意:这个数值本身就已经是默认值(300000毫秒,5分钟),而且它是一个上限,不是下限。只有当代理自己请求比现在更长的等待时间时,它才有用。实际上,社区反映的这个问题,并不是靠它解决的。
只要这个 issue 还没关闭,每5秒一次的轮询就会持续消耗你在 Codex 上的每周配额,一次10分钟的构建光是等待就可能花掉几十次完整调用。在 Verboo Code 上,这类低效不会让你多花一分钱:所有方案都提供无限 token,即便代理卡在轮询循环里,也不会让你在任务进行到一半时就没了配额。



