1. 先搞清楚:"良心"的 Codex token 都烧在哪了
先说个结论:Codex 这工具本身不是吞 token 的妖怪,真正烧钱的是你给它喂进去的"环境"。我见过不少朋友一上来就抱怨"Codex 太贵了,一个任务几百上千 token 就没了",但把会话日志拉出来一看,问题几乎都出在同一个地方:模型每轮都要重新读一遍超长的上下文,而你压根没告诉它哪些东西不需要看。
拿我自己的实测数据举例。一个普通的 Python 后端接口改造任务,如果直接在工作目录里运行codex,没有任何优化手段,它会默认扫描项目结构、读取相关文件、生成计划,然后再开始改代码。这一步下来,光输入侧就可能吃掉 8000 到 15000 token。要是项目再大一点,带着 node_modules、dist、.git 这种目录一起扫,两万 token 打底是很正常的。但这里面的绝大部分内容,跟你要改的接口一点关系都没有。也就是说,你是在为模型的"好奇"买单。
还有一类隐蔽的消耗是工具调用结果。Codex 不是光靠"猜"来写代码的,它会真的去执行命令、读文件、跑测试,然后把结果拼进对话历史。每一条命令的 stdout 和 stderr 都会累计到后续请求的上下文里。比如说你让它跑一个测试,结果测试框架打印了几百行日志,这些日志下一次请求又要重新计算一遍 token。一次两次无所谓,跑上十几轮,积少成多,那就相当吓人了。
另外,Codex 的对话历史不是"增量计费",而是每次请求都把全部消息重新发给模型。这意味着:历史越久,单轮请求就越贵。哪怕最新的回复只有几百 token,由于它前面挂着几万 token 的对话记录,实际计费还是按全部长度来算。想省钱,本质上就是在控制"每一轮请求时模型要重新读多少内容"。
我自己总结下来,token 消耗的高速增长点主要有四个:
- 启动扫描阶段:项目结构、忽略列表没配好,目录树和无关文件全被读了一遍
- 任务理解阶段:需求描述含糊,模型反复确认,来回拉扯好几轮
- 工具执行阶段:命令输出太长,日志刷屏,全被塞进上下文
- 多轮对话阶段:没有及时压缩,历史消息无限膨胀
这四项每一项都有对应的优化手段。后面我会逐个展开讲,但你先记住一个核心判断标准:任何一次请求,如果模型看到的内容里有一半以上跟当前任务无关,这个会话就是在烧钱。把无关内容砍掉,比任何花里胡哨的配置都管用。
2. 减少输入:上下文瘦身比任何技巧都见效快
2.1 别让 Codex 自动扫描整个仓库
Codex 默认的行为是"勤快过头"。它恨不得把你整个项目都读一遍,好让自己心里有数。但对 token 账单来说,这个习惯非常致命。绝大多数场景下,你只希望它关注某个模块、某几个文件,而不是全仓库。
最直接的办法是善用忽略配置。在项目根目录创建一个.codexignore文件,语法和.gitignore类似。我会把下面这些内容放进去:
node_modules/ dist/ build/ .venv/ __pycache__/ *.min.js *.map coverage/ .git/我在几个不同类型的项目里做过对比。一个中型 Node.js 项目,加上这些忽略项之后,Codex 启动时的上下文体积能压缩掉 40% 到 60%。原因很简单:node_modules 里的代码对模型理解你的业务逻辑没有任何帮助,它只需要知道依赖关系就够了,而这些信息完全可以通过 package.json 获得。
有一种情况例外:你明确要让 Codex 排查某个依赖内部的 bug。这时候你才需要给它指到具体的依赖包目录去读,比如node_modules/xxx/src/index.js。但即便这样,我也建议用codex exec --read-only或者聊天里直接用@文件路径的方式精准指定,而不是放开整棵目录树。
2.2 用文件引用替代"你自己描述需求"
很多人在让 Codex 改代码时,习惯用一大段自然语言描述:"请帮我看一下 src/services/user.js 里的 getUserInfo 函数,它现在返回的数据结构不对,需要增加一个字段...",这句话本身不贵,贵的是 Codex 为了确认你说的是哪个函数,可能要多读几个相关文件来猜测。
更省的做法是直接引用文件和符号。在 Codex 的聊天界面或者命令行里,直接把文件路径拖给它,或者用@src/services/user.js这样的语法把文件内容喂进去。一旦文件内容已经在上下文里,你就只需要说"把这个函数改成返回 xxx 结构",剩下的语义模型自己就能对上,不用再额外探索。
我见过有人把需求描述写成五六百字的小作文,然后 Codex 还要反问他确认。原因就是描述里有模糊地带,模型不确定改哪个位置、影响哪些模块。与其这样,不如直接给文件、给行号、给期望的输入输出。一旦双方在"改哪、怎么改"上达成共识,整个会话的轮次能减少三分之一,token 自然就省下来了。
2.3 任务拆小:一次只让它做一个完整的动作
这是我从踩坑里悟出来的。早期我用 Codex 搞一个跨模块重构,一句话里包含了"改 A 接口、顺便调整 B 模块的引用、再更新一下 C 的测试"。Codex 确实都做了,但代价是它需要把 A、B、C 三块的代码全部读进上下文,还要反复验证它们之间的调用关系。整场会话下来,token 消耗是分开做的三倍还多。
拆小任务不等于降低效率。你可以在一个会话里连续下指令,比如:
- 先让它修改 A 接口的返回结构
- 跑一遍测试,确认 A 没问题
- 再让它根据新的 A 接口调整 B 模块的调用
- 最后让 C 的测试适配新逻辑
每一步的上下文都比"大锅烩"要小,且每步完成后的验证结果可以即时反馈,模型不用猜测中间状态。实测下来,拆成四步的总 token 消耗,比一口气全做完要低 30% 到 50%。原因是:大任务里面模型会产生大量"中间猜测",而这些猜测本身也占据上下文空间,猜错了还要推倒重来。
2.4 清理会话历史的技巧
Codex 的多轮会话是一个持续膨胀的过程,不是每次开新对话就自动归零。如果同一个项目里你已经聊了二十几轮,这个上下文早就臃肿不堪了。这时候与其继续往下聊,不如开一个新会话,把之前的结论浓缩成一段话带过去。
我自己常用的做法是:每当会话开始出现"模型重复提及早期内容"或者"回答速度变慢"的现象,就说明上下文已经堆得差不多了。这时候我会主动开新会话,然后写一条类似这样的引导:
这是一个新会话。之前的任务已经完成:src/services/user.js 的 getUserInfo 函数已改为返回 { id, name, email } 结构,相关测试已通过。现在需要继续做订单模块的适配。
这段引导虽然消耗几百 token,但相比带着几万 token 的老会话继续跑,省钱省得不是一星半点。而且新会话里模型的历史负担小,生成质量反而更高。
3. 减少重复:缓存、续跑和子任务怎么用才省
3.1 利用 Codex 的 session 续跑,而不是频繁"再来一次"
Codex 官方客户端提供的 session 恢复机制,可能是被大家忽略最多的省钱功能之一。它的原理是:当你中断任务或者网络出问题之后,重新打开时会尝试恢复之前的会话记录,而不是让你从头再来。
有人可能会觉得"从头再来"更省钱,反正刚开始上下文少。这个想法是错的。因为你刚跑过的代码修改、工具执行结果、测试输出,如果在新会话里又要重新执行一遍,这几轮的操作和 token 消耗是实打实重复花费的。Session 续跑的核心价值在于避免"重复劳动",让上一轮已经花出去的 token 变成沉淀资产。
我在实际使用中发现,续跑时 Codex 会对之前的消息做一些截断或摘要处理,所以恢复后的上下文不会和中断前完全一样。但代码文件的修改结果会保留在工作区里,模型能通过读取文件来确认当前状态,这比从零开始要省得多。要注意的是,如果你的工作区里有一些未提交的临时改动,续跑时不要手动去动它,否则恢复后的模型会对文件状态产生误判。
3.2 让代码仓库当"外置记忆",减少对话里的闲聊
模型不需要你口头告诉它"之前我们改了哪些文件"。只要你把改动提交到 Git,模型通过git diff和git log就能自己看到。这也是很多 Codex 教程里强调"保持小步提交"的原因之一。
小步提交不只是代码管理习惯,它还直接影响 token 消耗。设想两种情况:
情况 A:你一口气改了五个文件,然后让 Codex 检查代码逻辑。此时它要看五个文件的完整 diff,甚至在 context 不足时还要读完整的当前文件。上下文很拥挤。
情况 B:你先提交第一个文件的改动,让 Codex 只针对这个文件的 diff 做审查或补测试,通过后再继续下一个。每一次的 diff 都很小,上下文量自然小。
有人担心小步提交会增加会话轮次,导致总 token 增加。我实测下来的结论是:只要每个提交的改动是"逻辑完整"的,轮次的增加幅度很小,但每轮的上下文量下降非常明显。净收益是省钱的。而且小步提交还有个额外好处:万一 Codex 改错了,你可以直接git revert,不用再来回解释让它修复,又能省下一大笔 token。
3.3 子任务隔离:用独立沙箱处理探索性问题
有一些任务的本质是"探索",比如"这个第三方库的源码里到底怎么处理超时的?""这个报错日志是什么意思?"。这种任务如果放在主会话里做,会污染整个主上下文——大量无关的三方库源码和日志会被塞进去,导致后续主任务的质量下降、token 直线上升。
我的做法是:遇到探索性问题,另开一个临时的、和环境隔离的会话去处理。搞明白之后,回到主会话里只把结论告诉 Codex,或者直接把解决方案写进代码里,让它去 review。这相当于用很小的代价完成情报收集,再让主会话以干净的环境执行最终任务。
如果你是使用命令行模式,可以配合codex exec --sandbox这样的安全隔离参数。这既是为了安全,也是为了 token 控制——sandbox 模式下工具调用产生的输出往往会被更严格地裁剪,不会被完整地一股脑塞回上下文。
3.4 关于"续写"要不要单独说
Codex 有时会在一轮回复里停在中途,比如"我准备修改以下内容..."然后就断掉了。很多人的第一反应是直接输入"继续"或者"接着写"。这两个字本身很便宜,但问题在于:模型要"接着写",必须重新读取之前的全部代码和修改,如果上下文已经很大,这个"继续"的实际成本根本不便宜。
更好的做法是给它一个明确的小锚点,比如:
继续,从刚才修改的 getOrderList 函数开始,下一步需要补充错误处理逻辑。
这句话能帮模型精准定位到它要接续的位置,减少它重新扫描"我上次写到哪了"的消耗。另外,如果你不需要它继续在原有回复上追加,而是想让它换个方式重新生成,直接说"重新生成,这次只输出修改后的函数",反而更能控制输出长度。
4. 压住输出:模型的话越多,你的额度越薄
4.1 输出 token 同样花钱,而且更容易失控
很多人只盯着输入侧省 token,忽略了输出侧。实际上,按 OpenAI 的定价模型,输出 token 的单价通常比输入 token 更高。Codex 这种 agent 型工具的输出尤其夸张:它会生成计划、解释原因、展示 diff、写测试、总结结果,有时候还会附带一些"温馨提示"。
我做过一次对比实验。同样的任务,不加任何输出约束时,Codex 生成的回复里约有 40% 的内容是解释性文字;加上明确的输出格式要求后,解释性文字能压缩到 10% 左右。任务完成度没有区别,但输出 token 少了接近一半。
控制输出的核心手段是给它强约束,而不是堵它的嘴。我常用的句式是:
只输出需要修改的代码块,不要解释理由。修改前先输出变更文件列表,每行一个文件。
这样 Codex 就知道它不需要写长篇大论来证明自己懂了。这不算对抗模型,而是一种沟通协议:你明确告诉它"我要什么形式的答案",它就不会自作多情地补充背景信息。
4.2 用 AGENTS.md 或项目说明文件约束行为
Codex 对项目内文档的响应相当积极。如果你在项目根目录放一个AGENTS.md,里面写清楚"编码规范、输出格式、禁止事项",模型会在每次会话中默读这些约束。这本身会消耗几十到几百 token,但它能压住后续输出侧的失控,性价比极高。
我在 AGENTS.md 里一般会写:
- 修改代码时只返回 diff 和必要的 commit message,不要附赠项目分析
- 需要解释时用不超过三句话
- 测试输出不要全量打印,只返回失败的用例和错误摘要
- 执行 shell 命令前先说明用途,但不要输出命令的预期结果
写完这些之后,Codex 的输出风格会明显收敛。坦白讲,让模型保持沉默比让它干活更难调教,项目文档是效率最高的"调教工具"。
4.3 让 Codex 先写计划再动手,避免返工产生的重复输出
这看起来像悖论:多写一个计划不是更费 token 吗?但实际场景里,恰恰是"没有计划的直接动手"最容易造成输出浪费。Codex 一旦开始改代码,改了一版发现方案不对,它会在后续回复里继续输出第二版,这两版之间的重写成本远高于开头多写一个计划。
我的习惯是分为两阶段。第一阶段:要求 Codex 输出一个简洁的实施方案,用五个以内的步骤描述,不写代码。第二阶段:确认方案没问题后,再让它按步骤执行。第二阶段每步的输出都会被方案约束住,几乎不会出现"突然换思路"的失控情况。方案本身几百 token 造价,换来的是执行阶段少走 50% 的弯路,怎么看都值。
这个思路在 Codex 里还有一个落地方式:你可以先用一次调用要求只输出方案,跑完确认后另起一个 session,把方案内容作为 system prompt 引导新 session 执行。这样执行 session 的上下文非常干净,几乎只在方案框架内运转。
5. 止损:这几类报错正在悄悄掏空你的 Token
5.1 context 超限与 "ran out of room"
很多人在热搜里看到过一个报错:error running remote compact task: codex ran out of room in the model's context。这个报错的本质是,当前会话的上下文已经超过了模型的窗口长度,Codex 尝试通过"远程压缩任务"来精简上下文,结果压缩过程本身也失败了。
出现这个报错时,如果继续在同一会话里重试,只会不断烧 token 而没有任何产出。正确的止损方法是立刻停止当前会话,手动开启新会话并按第 2.4 节的方式撰写一份"变更摘要"带过去。不要指望 Codex 自己能压缩好,也别反复点重试,那是最贵的无效操作。
5.2 token 刷新与登录类问题
Codex 作为账号类工具,会涉及 token 刷新机制。热搜词里那些sign-in could not be completed token exchange failed、failed to refresh token: 400 bad request之类的报错,绝大多数情况下不是你项目配置的问题,而是本地登录状态过期或服务端会话失效。
我的经验是:出现这类报错,直接退出登录然后重新登录,通常五分钟内解决。如果重新登录之后还是报invalid 'refresh_token': empty string,那大概率是本地缓存中的凭证文件损坏。这时候清理掉 Codex 本地保存的会话凭证,重新做一次完整的登录认证。这里要特别注意:网上有些教程会让你手改配置文件里的 token 字段,我强烈不建议这样做——手改之后模型客户端会以为你已经登录,但实际上服务端不认,下一次请求照样失败,还白白多烧一轮确认请求的 token。
5.3 模型不支持与端点配置类错误
the 'gpt-5.6-sol' model is not supported when using codex with a chatgpt account这类报错,是因为账号权限和当前请求的模型不匹配。比如你用 ChatGPT 订阅账号去请求一个仅供 API 账号使用的模型,或者反过来。解决问题的方式是切换账号类型,或者在配置里把模型选回当前账号支持的型号。
还有一些本地端点配置错误,比如热搜词里的cc switch local proxy failed while handling codex endpoint /responses,本质上是本地网关服务不可用。排查思路是"先确保本地服务活着,再看配置指向是否正确",而不是反复重试请求。重试一次,就是烧一次 token,没有任何意义。
5.4 403 与区域限制类报错
有相当多的人会撞见类似token endpoint returned status 403 forbidden: country这样的认证报错。请记住:这类报错是账号层面的限制,通常跟你的订阅类型、账号区域设置、支付方式有关,不是修改本地代理或者换 endpoint 能绕过去的。本地反复换配置只会导致一连串失败请求,token 全被无效消耗。
正确处理方式只有两个:一是确认账号的订阅区域和当前使用环境是否一致;二是如果账号本身确实受限,就不要在这里继续耗 token 了,换用其他合规的模型服务或者 API 账号。另外要注意,一旦账号在 Web 端出现"token 无法刷新"的提示,先到官方账号设置里检查订阅状态和账单,而不要动本地配置。
这些报错和 token 消耗的关联,本质上是"一个失败请求也会按请求长度计费"。还没干正事,就先把自己的额度烧没了。所以止损的核心原则就是一条:遇到认证和账号层面的错误,不要在本会话内重试超过两次,立刻转入排查/重新登录流程。
6. 落地配置:一套可以照着抄的省钱模板
6.1 项目级配置示例
下面是我在自己项目里常用的一套.codexignore模板。你可以直接抄,再根据项目情况增删:
# 依赖和构建产物 node_modules/ venv/ .venv/ dist/ build/ out/ *.min.js *.min.css *.map # 测试与覆盖率数据的临时输出 coverage/ .pytest_cache/ .mypy_cache/ .ruff_cache/ # IDE 配置与本地环境 .idea/ .vscode/ .DS_Store *.local .env # 版本管理内部文件(如果项目里存在) .git/ .svn/对应地,AGENTS.md我建议从下面这个版本起步:
# 项目说明 - 描述技术栈和启动命令 - 描述测试运行命令 # 修改规范 - 先给方案,再改代码 - 修改内容只输出 diff 和必要说明 - 不要批量重命名或重构不相关文件 - 每次修改后运行相关测试,只报告失败项 # 输出规范 - 解释性文字尽量简短 - 不要输出完整的文件内容 - 遇到不确定需求时,用一句话确认,不要长篇分析这两个文件一旦放进去,Codex 在之后的所有会话中都会自动读取并遵守。它们像是给模型的"操作手册",既提升了生成质量,又压制了无效输出。
6.2 工作流层面的省钱习惯
配置是死的,习惯才是决定 token 账单的关键。我整理了一份给自己用的 check list,每次开 Codex 会话前过一遍:
- 这次会话的目标是不是单一、明确的?
- 项目目录里有没有构建产物和依赖目录没被忽略?
- 是否可以直接引用目标文件,而不是让模型自己扫描?
- 有没有写 AGENTS.md 明确输出格式?
- 上一次会话的结论是否需要先做一次总结,再开新会话?
做完这五项检查再动手,通常可以稳定地把一次开发任务的 token 开销控制在"必要范围"内。这个必要范围是多少?没有一个固定数字,因为任务复杂度差异很大,但你会明显感觉到:同样一个任务,优化前后的 token 消耗能差出 2 到 5 倍。
6.3 替代方案与成本比较
如果你的项目对 Codex 的依赖很深,且日常 token 消耗确实降不下来,可以考虑把它和更便宜的模型混用。Codex 的配置支持切换不同的模型服务商或模型型号,比如接一些开源模型的兼容端点,把"阅读理解类"的小任务分流出去,让 Codex 专注在它最擅长的代码生成和工具调用上。
这里也要提一句成本对比的意识:在同一个任务里,如果让 Codex 做一次正确率 90% 的编辑,比起"先用便宜模型做一版可能不对的编辑,再让 Codex 修正",前者往往更低。因为后者包含两轮完整上下文开销。所以混用模型的前提是,便宜模型真正能独立完成一部分任务,而不是"生成一个半成品再让 Codex 擦屁股"。
6.4 我踩过的最后一个坑:过度优化
最后想泼一盆冷水。Token 优化是有边际收益递减的。我见过一些人为了省 token,把会话拆得极端零碎,每换一个函数就开新 session,然后花大量时间写上下文摘要,结果单轮 token 是降下来了,整个项目的沟通成本却翻了一倍,最终总成本反而更高。
我的建议是:把优化目标设定为"让每次请求的内容都有必要",而不是"让每次请求的内容越少越好"。一个完整任务该读的文件就要读,该验证的测试就要跑,不要为了省 token 牺牲 Codex 对任务的理解程度。模型答非所问、反复确认、改写重来,比多读几个文件的成本高多了。
我自己经过几轮调整后,最优的平衡点是:
- 一个中等功能开发,控制在 5 到 10 轮会话内
- 每个会话聚焦一个逻辑闭环
- 上下文里只保留"本会话需要的文件 + 相关测试输出 + 简短的历史决策记录"
这套模板我使用到现在,token 消耗比最开始的野蛮使用方式省了大约 70%,任务完成速度反而更快了。项目里的 Codex 使用成本,从"每次跑完看账单都心疼"变成了"可以轻松预估"的稳定状态。如果哪天发现某个会话又开始异常燃烧,马上排查是不是又有构建日志进了上下文——八成都是这个原因。