Codex 第一次让我留意 Token 账单,是在一次看起来特别简单的修复任务里。任务只是把某个接口的超时时间从 30 秒改成 5 秒,结果后台实际消耗接近 3 万 Token。我当时的第一反应是模型抽风了,后来把调用日志一条条翻出来看,才发现 Codex 在一路“摸代码”:列目录、grep 关键词、读文件、跑测试、看报错、再读文件……每个动作都在往模型上下文里塞内容,Token 就是这么烧掉的。
后来我把 Codex 的工作方式彻底研究了一遍,又在自己项目里反复调整配置和用法,最终攒出了一套能稳定省 Token 的方案。这篇文章不聊虚的,只讲实操:Token 到底消耗在哪、怎么选模型、怎么拆任务、怎么调 CLI 参数,以及遇到登录报错和 token 失效时怎么处理。适合正在用 Codex CLI 的开发者,也适合准备把 Codex 接入到日常开发流程里、但对成本还没底的人。
1. 先搞清楚 Codex 的 Token 到底“烧”在了哪里
1.1 你看得到的回复和你没看到的“后台开销”
Codex 不是普通聊天机器人,它是 agent 形态的编码助手。用户发一句自然语言指令之后,它会经历一整条执行链路:先理解任务,再决定要不要列目录、搜索关键词、读文件;读到文件之后要根据内容制定修改方案;改完代码还可能跑命令、跑测试,再把执行结果拿回来继续推理。这中间任何一步都不是免费的。
很多人只关注模型“最后回复”那一段文字,但真正的账单大头在你看不到的地方。系统提示词和工具定义是每次请求都要携带的固定开销,对话历史是越滚越大的累计开销,文件内容和搜索结果属于高频读取开销,而推理模型在输出最终答案前,还会在内部生成一大段思考链。拿 OpenAI 的 o 系列模型举例,思考链消耗的 Token 经常是最终回复的 3 到 8 倍,你看到几千字输出的时候,模型内部可能已经“想”了好几万字。
更麻烦的是多轮交互下的重复计费。Codex 每推进一轮,都会把此前所有轮次的系统提示、用户指令、文件内容、工具结果、历史推理再送进模型一次。也就是说,一次任务跑到第 10 轮时,前 9 轮的内容大概率会被重新编码并再次计费。这有点像打车绕城一周只为去隔壁小区,路程全是真实距离,账单一分不少。
注意:Codex 烧 Token 不是因为它“废话多”,而是因为它每次都在重复搬运大量历史上下文,再加上推理模型内部的开销。理解这件事,比急着调任何参数都重要。
1.2 最容易被忽略的隐形消耗:重复读取和错误重试
真正让账单失控的,往往不是单次请求,而是 agent 的无效劳动。Codex 在不确定代码位置时,会反复执行 grep 和文件搜索,每次搜索结果都作为输入 Token 算钱;在大型 monorepo 里,它可能读一个文件觉得不对,又读另一个,读完再回头读第一个,文件读取量成倍增加。最典型的场景是:你让它修复订单模块的慢查询,它先从根目录开始 grep,然后读了 controller、service、repository、mapper 五六个文件,中途看到无关的缓存配置又顺手读了一层,最后才定位到真正的问题。这一路下来,光文件读取就消耗了上万 Token。
修改后的验证失败同样烧钱。Codex 改完代码,跑测试报错,它需要重新读取错误堆栈、重新定位文件、再次修改。每失败一次,相当于把整个任务重新跑一遍。更隐蔽的是网络或认证问题导致的重试——登录态失效、网络链路中断,请求已经发出去了,上下文数据已经全部送进模型,但结果没回来,这些 Token 不会退给你。我见过最极端的情况:一个任务因为登录态失效连续重试了 4 次才真正执行,前 3 次白烧了大概 4 万 Token。
下面这张表是我给团队做成本培训时用的,把 Codex 单次任务的 Token 去向按占比拆开看,问题会很清楚:
| 消费环节 | 大概占比 | 为什么贵 |
|---|---|---|
| 系统提示词 + 工具定义 | 5% - 10% | 固定开销,每次请求都带 |
| 任务描述 + 对话历史 | 15% - 25% | 轮次越多,累计越大 |
| 文件读取与搜索结果 | 25% - 40% | 最容易被忽视的大头 |
| 模型思考链 | 20% - 40% | 推理模型内部开销 |
| 最终回复与代码改动 | 5% - 15% | 用户真正“看得见”的部分 |
这组比例会随任务类型浮动,但规律很稳定:文件读取和历史上下文的合计占比,通常超过最终回复的两倍。很多人纠结“模型输出长度”,其实省输出的空间远不如省输入来得大。
1.3 先量化你当前的成本,再谈节省
很多优化方法听起来有道理,但如果没有基线数据,改了配置之后根本不知道有没有效果。我的做法是,在每台机器上第一次装好 Codex 后,先做一次“5 分钟成本体检”:用一个中等难度的真实任务跑一遍,记录返回的 usage 数据,包括总输入 Token、总输出 Token、缓存命中 Token、最终回复 Token,同时记录任务耗时和是否一次成功。调优一周后,再跑同一个任务,前后对比。
具体怎么看数据?Codex 的日志里通常会输出 usage 字段,如果你是走 API 接入的方式,也可以在调用记录里找到每次请求的prompt_tokens、completion_tokens、total_tokens。把这些数据存到本地文件,周末花十分钟扫一眼,优化方向会非常清晰。没有基线,你很容易陷入“好像省了”的错觉。
2. 模型选型和任务拆解:从源头上砍掉一半消耗
2.1 不同模型的 Token 单价与推理开销差异很大
很多人一提到省 Token,第一反应是压缩回复长度,其实最划算的一刀是换模型。Codex 允许通过环境变量或配置文件指定模型名称和 endpoint,这就给成本控制留出了很大空间。不同模型在输入输出单价、推理链长度、上下文支持这几个维度上差异巨大,盲目用同一个模型处理所有任务,等于用高射炮打蚊子。
以我自己用过的配置为例,做一个粗略对比:
| 模型类型 | 输入成本(百万 Token 级别) | 输出成本 | 推理链特点 | 适合场景 |
|---|---|---|---|---|
| 旗舰推理型 | 偏高 | 更高 | 思考链很长 | 跨模块重构、复杂架构设计 |
| 轻量型 | 中 | 中 | 思考链相对短 | 日常修 bug、单个文件改动 |
| 第三方兼容模型(如 DeepSeek 系) | 明显更低 | 更低 | 取决于具体模型 | 大批量代码阅读、低成本跑量 |
我实测过接入第三方模型做代码阅读和简单修改的场景:对于一个接近 3 万行代码的中型仓库,处理一个跨文件重构任务,输入价格更低的模型能把单次任务的费用降到原来的三分之一左右。但这里有一个大前提:第三方服务提供的模型必须支持足够的上下文长度,否则 Codex 读几个文件就顶到上下文上限,任务中断重来,反而更贵。
更合理的策略是分级使用:日常小任务用轻量模型或第三方兼容模型,成本低、速度快;需要深度重构、跨模块定位问题时,再上旗舰推理模型。值得提一句,热搜里频繁出现的“codex 接入 deepseek”,本质上就是把 Codex 的配置指向兼容的模型服务,并在配置里指定支持代码场景的模型名称。具体参数各家服务商不同,参照官方接入文档操作即可,重点确认上下文长度和工具调用能力这两个硬指标。
2.2 任务拆解:让 Codex 一次只做一件事
这是整套方案里性价比最高的一条,免费,而且立竿见影。
我刚开始用 Codex 时,习惯一次性给一个大而全的任务,比如:“帮我看看订单模块为什么慢,顺便补一下注释,然后写个单元测试。”结果就是灾难。它在“为什么慢”这个主问题上花了大量 Token 翻代码,翻完之后又得重新理解“补注释”需要看哪些文件,接着又是一轮文件读取。任务之间没有共性,所有上下文都要重复计算。整体跑下来,消耗的 Token 是拆分后执行的三倍以上。
任务拆解的核心只有三条:一次只做一件事;在任务描述里写清边界;给出明确的验收标准。比如“排查订单模块慢查询”和“为订单模块补注释”就是两个完全不同的任务,前者需要看调用链和 SQL,后者只需要看类和方法结构,拆开之后各自的上下文需求都小很多。
边界描述尤其关键。我现在的习惯是在每条指令里带上“只修改src/services/order/目录下的文件,不要动前端和数据库层”这类限定,Codex 漫游的范围就能被严格圈住。验收标准则能减少它反复确认“自己改得对不对”的开销,比如“改完后运行npm test src/services/order里的三个用例必须通过”。
任务拆小之后,上下文变小,模型在窗口内的注意力更集中,修改质量反而更高。我把团队工作流改成“先拆任务,再交给 Codex”之后,报错量明显下降,这部分收益比参数调优来得更直接。
2.3 长任务里的“冷启动”比“续聊”更划算
还有一个经验可能反直觉:当一个任务已经超过 5 轮对话,或者历史里躺着好几段大文件读取记录时,开新会话比你硬着头皮继续聊更省 Token。
原因还是重复计费。如果前面已经读了五个大文件,后续每次提问都会把这五个文件的内容再算一遍钱。这时候不如直接/clear或者新开一个会话,把上一个会话得到的结论整理成一段精炼说明,作为新一轮任务的起点。我做过一次对照实验:同一个修复任务,A 方案在原会话里一路修到成功,B 方案每两轮清理一次会话、用精炼结论续接。最终 B 方案的总 Token 消耗只有 A 方案的六成左右,而且代码质量更高,因为上下文更干净,模型没有被前面的错误尝试带偏。
注意:清理会话前先保存进度。Codex 已经改好的文件不会因为清理而回滚,但“下一步打算做什么”这类规划信息会丢。我的习惯是清理前把当前状态和剩余计划写进
TODO.md,然后再继续。
3. 配置与 CLI 实操:把每次调用的消耗压到最低
3.1 用好上下文压缩与清理机制
Codex 自带的会话管理命令里,/compact和/clear是我最常用的两个。/compact会尝试把对话历史压缩成更短的摘要,适合“历史太长但还不想完全断开”的场景;/clear则是彻底清空当前上下文,适合“已经完成了阶段目标、马上要开新任务”的场景。
但要注意,/compact不是免费的午餐。压缩动作本身要消耗一次模型调用,而且如果上下文已经爆炸,压缩也可能失败。热搜词里那条 “error running remote compact task: codex ran out of room in the model's context” 讲的就是这个问题——上下文窗口满了,连压缩任务都无法放进去。这时候唯一有效的处理方式是先/clear或者手动删掉历史文件,释放出上下文空间,再重新开始。
我的触发时机很固定:每完成一个子任务,马上清理一次会话;发现 Codex 开始重复读取同一个文件时,立刻清理;模型开始“忘记”任务描述里的关键限制条件时,也大概率是上下文过长导致注意力被稀释,该清就清。
3.2 利用文件白名单/黑名单减少漫游成本
Codex 在定位问题时会按需搜索文件,但“按需”这件事有很大随机性。仓库里有成百上千个文件时,它可能把跟任务完全无关的目录也扫一遍。解决思路是把搜索范围提前圈住。
常见的做法有两种,建议叠加使用。一是维护一份忽略规则,把第三方依赖、构建产物、文档目录等不需要分析的内容排除在外,思路和.gitignore一致,但有些项目会单独给 Codex 配一套规则,避免影响正常 git 操作。二是在任务描述里显式限定范围,比如“只分析src/backend/下的 Python 文件,忽略assets/和node_modules/”。
我更推荐把重点放在第二种“正向引导”上,因为黑名单只能排除你明确列出的目录,而正向引导能让 Codex 在未知情况下更保守。另外还有一个隐藏消耗源:有些实现会一次性把文件内容尽量多地带进上下文。如果你仓库里有上千行的大文件,这会非常伤。我的处理方式是先让 Codex 用 grep 定位具体函数,再让它只对某个区间做修改,而不是整个文件扔进去。
3.3 接入第三方模型时的成本与稳定性权衡
Codex 默认对接 OpenAI 服务,但为了控制成本,很多人会把模型请求指向兼容的第三方服务。这个方向的收益很直接:第三方服务如果按更低的输入单价计费,在长上下文读取场景里能省下不少。但稳定性的坑也不少,我踩过之后总结出三个必须提前确认的点。
第一,服务端是否完整支持 Codex 用到的工具调用协议。Codex 依赖文件搜索、内容读取、命令执行这些工具,如果第三方服务对工具调用的兼容性不好,任务会频繁中断。第二,模型的上下文窗口是否足够容纳“系统提示词加项目文件加历史”的体积,太小的话读几个文件就满了。第三,账号权限和模型白名单是否匹配。热词里那条 “the 'gpt-5.6-sol' model is not supported when using codex with a chatgpt account” 就是典型例子——配置里写了某个模型,但当前账号的可用模型列表里没有它,自然报不支持。
我的建议是不要把第三方接入当作万能省钱法。先在小任务上验证接口兼容性和输出质量,确认没问题后,再把一部分低频、简单的任务切过去,核心复杂重构还是留在官方模型上更稳妥。接入配置本身不复杂,核心就是 endpoint、模型名、API key 这三件事,前提是本地 Codex 进程能正常访问到目标服务。
3.4 登录态与 Token 失效:别让报错消耗你的耐心和额度
使用 Codex 的过程中,登录和鉴权问题出现的频率比预期高。这类问题表面和“省 Token”无关,实际牵扯很大——一次登录失败或 token 刷新失败,可能导致整个任务反复重试,每次重试都重新计费。所以我把它放进优化方案里一起讲。
常见的几种情况都很有规律:登录时token exchange failed,多半是本地缓存的登录信息和服务器端状态不一致;刷新 token 报 400 且提示refresh_token为空,说明本地凭据被破坏或者有多个客户端在抢写同一个 token 文件;your access token could not be refreshed基本就是凭据过期了。我对这类问题的处理原则很明确:先把所有会话停掉,再处理登录态,修好之后重新开任务。不要带着半死的登录态反复重试,那是最烧 Token 的操作。
4. 常见问题与排查技巧实录
4.1 登录与 Token 校验类错误速查表
把实践中遇到过的错误整理成速查表,可以直接对号入座:
| 报错特征 | 常见原因 | 处理办法 |
|---|---|---|
token exchange failed+status 403 | 账号权限不足或环境受限 | 检查账号权限,必要时改用 API key 方式认证 |
token exchange failed+error sending request | 本地网络链路异常,请求未送达服务端 | 检查网络连通性,稍后重试,避免多次连续重试 |
invalid 'refresh_token': empty string | 本地凭据文件为空或损坏 | 退出登录、清理凭据缓存、重新登录 |
your access token could not be refreshed | 凭据长期未使用导致过期 | 退出登录并重新走一遍登录流程 |
Blocked deletion of token file | 文件权限不足或进程占用 | 检查凭据目录权限,关闭其他占用进程 |
the model is not supported when using ... | 账号可用模型与配置不一致 | 修改配置中的模型名,或切换认证方式 |
这张表解决的是“能不能正常跑起来”的问题。从省 Token 的角度看,遇到任何一条报错,都不要直接重试超过两次。正确姿势是先排查原因,再重试。
4.2 模型上下文溢出与任务中断类问题
除了登录类问题,实际使用中最折磨人的就是“任务跑到一半挂了”。典型报错是error running remote compact task: codex ran out of room in the model's context,上下文窗口满了,连压缩任务本身都进不去。这种情况通常发生在一次任务里读了太多大文件,或者对话历史过长。
处理流程分四步:停止当前任务;手动清理会话历史或新开会话;把已经完成的代码改动记录下来写进任务说明;重新发起任务时缩小范围,限制文件读取量。尤其最后一步最关键,同一个小改动反复触发 context 溢出,说明文件读取策略有问题,光压缩不解决根本。
另一种中断是模型能力与操作不匹配,比如指定了某个模型但当前账号不支持。这类问题不是参数配错了,而是账号权限和模型白名单不一致。排查方向就是两条:检查配置里的模型名是否真实存在,检查当前认证方式是否有该模型的访问权限。
4.3 关于 Token 计量的几个认知误区
最后讲几个认知误区,这些观念比任何配置项都重要。
误区一:只盯着输出 Token。很多人关心模型回复了多少字,但实际账单里输入 Token 往往更占大头。系统提示词、历史上下文、文件内容、搜索结果都在算钱,省输入比省输出高效得多。
误区二:以为多轮对话不会重复计费。Codex 每一轮请求都会携带此前所有轮次的内容,第 10 轮时前 9 轮的 Token 很大概率会再结算一遍。会话越长,历史越贵。
误区三:以为压缩一定省钱。/compact能压缩上下文,但压缩动作本身消耗一次调用。偶尔超长时压缩划算,如果每个任务都超长,说明任务拆解或文件读取策略有问题,该从源头解决。
误区四:忽视缓存命中的价值。很多模型服务对“之前处理过的上下文”按更低的缓存价格计费。也就是说,如果一批请求之间重复使用同一份系统提示词和项目背景,让上下文高比例重叠,平均成本会显著下降。批量做代码审查时,我会刻意固定相同的背景说明,让缓存命中率尽量高。
我在实际项目里把这套方案完整跑了一个月,效果最明显、也最持久的不是某个参数开关,而是“任务拆解加勤清理历史”这两个习惯。它们几乎不花额外成本,却让模型每次调用面对的都是干净、有针对性的上下文,Token 用量降下来之后,整个调试流程的速度也明显快了。
最后再分享一个小技巧:每天开工前,把前一天残留的 Codex 会话文件清掉,再开始新任务。这样一个动作比研究任何高级参数都更省 Token——模型不会记得你昨天做过什么,但昨天的上下文会默默出现在今天每一笔账单里。