ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Codex Token计费陷阱:一句“继续”如何让缓存命中率暴跌、成本翻倍

Codex Token计费陷阱:一句“继续”如何让缓存命中率暴跌、成本翻倍

1. 被“继续”两个字吃掉的预算

先说结论:Codex 这类按 Token 计费的编程助手,最容易被算漏的成本,不在你敲了多少行代码,而在你敲了多少次“继续”。

我前后测过不下十家中转站,从最便宜的按量套餐到包月不限量的,账单差异能到三四倍。一开始我以为是中转站虚标倍率,后来把请求日志一条条拉出来对比才发现,问题出在我自己身上——准确地说,出在我对“上下文”和“缓存命中率”的理解上。

这个标题里的“继续”,不是指你打游戏时按的那个继续,而是你在对话里最常干的一件事:任务没做完,模型停下来了,你顺手补一句“继续”“接着来”“go on”。这三个字本身没几个 Token,但它触发的是整段上下文的重新计费。如果你不懂缓存命中率怎么算,这一句“继续”可能让你为前面几千甚至几万 Token 的上下文再付一次钱。

这篇东西写给两类人:一类是已经在用 Codex、Claude Code 这类 CLI 编程助手,但从来没认真看过账单明细的;另一类是正准备接入中转站,想搞清楚“为什么同样的活,别人花得比我少”的。我会把 Token 计费的底层逻辑、缓存命中率的实际影响、上下文膨胀的几种典型场景,以及我自己踩过的坑,全部摊开讲。看完你至少能做到一件事:知道钱花在哪,以及怎么把它省下来。

2. Token 计费到底怎么算:别被“输入便宜”骗了

2.1 输入、输出、缓存,三笔账分开算

很多人对 Token 计费的理解停留在“输入便宜、输出贵”。这个印象在早期对话模型上大致成立,但在 Codex 这种长上下文编程场景里,输入才是真正的大头。

一次典型的 Codex 请求,结构是这样的:

  • 系统提示词(System Prompt):工具内置的指令,通常几千 Token,每次请求都带。
  • 历史对话(Context):你之前所有的提问、模型的回答、工具调用结果,全部累积。
  • 当前输入:你这次敲的那句话。
  • 输出:模型生成的回复。

计费时,前三项合并成“输入 Token”,最后一项是“输出 Token”。而缓存机制,只作用于输入里的前缀部分。

关键点来了:缓存命中(Cache Hit)的 Token,价格通常只有未命中(Cache Miss)的十分之一甚至更低。不同中转站和官方定价略有差异,但量级基本如此。也就是说,同样一段上下文,命中缓存和没命中,成本差一个数量级。

2.2 缓存命中率为什么是省钱的核心

缓存的工作原理,简单说就是:如果这次请求的开头部分和上次完全一致,服务端可以直接复用上次的计算结果,不用重新算一遍。

这就像你去一家餐厅,每次都点同样的前菜。厨师第一次做要花时间,第二次你还没进门他就把前菜备好了。但如果你每次前菜都换,那厨师每次都得重做。

放到 Codex 场景里:

  • 你的系统提示词是固定的,这部分几乎总能命中缓存。
  • 你的历史对话,如果只是往后追加,前面的部分也能命中。
  • 但如果你在中间插入了新内容,或者改动了前面的对话,从改动点往后的所有内容,缓存全部失效。

这就是“继续”的陷阱所在。表面上看你只加了一句“继续”,但如果这次请求的上下文结构和上一次不完全一致(比如工具调用结果被重新组织、时间戳变了、或者你中途切换了账号),缓存就可能大面积失效,整段上下文按未命中价格重新计费。

2.3 一个真实的账单对比

我拿同一个任务做过对照实验:让 Codex 帮我重构一个约 800 行的 Python 模块。

操作方式总输入 Token缓存命中 Token实际计费 Token(折算)
一次性描述清楚,让它跑完42,00038,000约 7,800
分 6 次“继续”推进156,00051,000约 110,000

注意第二行:总输入 Token 是第一行的近 4 倍,但缓存命中只多了 1.3 万。原因就是每次“继续”都让上下文重新膨胀,而且因为中间有工具调用结果的插入,缓存前缀被打断,命中率暴跌。

折算下来,第二种方式的成本是第一种的十几倍。而这两种方式,最终产出的代码质量几乎没有差别。

提示:缓存命中率不是你能直接控制的参数,但你的操作习惯会直接决定它。上下文越稳定、追加越规律,命中率越高。

3. “继续”背后的上下文膨胀机制

3.1 上下文是怎么一步步变胖的

Codex 这类工具和普通聊天机器人最大的区别,是它会读写文件、执行命令、返回结果。每一次工具调用,结果都会被塞进上下文。

举个具体例子。你让 Codex 修一个 bug:

  1. 你描述问题(约 200 Token)。
  2. Codex 决定先读文件,调用读取工具,返回文件内容(约 3,000 Token)。
  3. Codex 分析后决定搜索相关函数,调用搜索工具,返回结果(约 1,500 Token)。
  4. Codex 修改代码,调用写入工具,返回确认(约 100 Token)。
  5. Codex 说“已修复,请测试”。

到这里,上下文里已经累积了约 5,000 Token 的工具结果。如果你测试后发现还有问题,说一句“继续修”,那么上面所有内容都会作为历史上下文,再次进入下一次请求。

第二次请求的输入 = 系统提示词 + 第一次的全部历史 + 你的“继续修”。如果这次 Codex 又读了几个文件,上下文再涨几千。第三次、第四次……每说一次“继续”,前面所有内容都要重新过一遍。

这就是上下文膨胀:Token 用量不是线性增长,而是随着对话轮次加速累积。

3.2 缓存为什么救不了频繁“继续”

有人会想:不是有缓存吗?前面内容不变,应该能命中啊。

理论上对,实际上有几个坑:

第一,工具调用结果往往带时间戳或随机 ID。很多工具返回的内容里包含执行时间、请求 ID、临时路径等。这些内容每次都不一样,导致缓存前缀在工具结果处就断了。

第二,上下文重排。有些工具在组装请求时,会把历史消息重新排序或压缩,顺序一变,缓存全废。

第三,账号或会话切换。如果你用 cc-switch 之类的工具切过账号,或者会话 ID 变了,缓存直接归零。这也是为什么热词里那么多人在问“切账号后上下文加载不了”——不是加载不了,是缓存没了,重新算一遍。

第四,中转站的缓存策略差异。不同中转站对缓存的支持程度不一样。有的只缓存系统提示词,有的缓存整段前缀,有的干脆不缓存。你测 N 家才发现差异,很大一部分就来自这里。

3.3 哪些操作会让缓存命中率暴跌

我把常见操作按对缓存的影响排了个序,从最伤到最不伤:

操作对缓存的影响原因
切换账号/会话完全失效缓存按账号或会话隔离
修改历史消息从修改点起失效前缀不匹配
插入新的系统指令从插入点起失效前缀被破坏
工具调用返回动态内容部分失效时间戳/ID 变化
纯追加对话基本保留前缀稳定
新开会话重述需求完全失效无历史可复用

看懂这张表,你就明白为什么“继续”这么贵了——它本身是纯追加,按理说应该保留缓存。但它触发的后续工具调用,会插入动态内容,把缓存打断。你省下的那点追加成本,远远抵不过后续工具结果带来的缓存失效。

4. 实测:三种推进方式的钱差多少

4.1 实验设计

我选了一个中等复杂度的任务:给一个 Flask 项目加一套 JWT 鉴权,涉及 5 个文件的新增和修改。用同一个中转站、同一个模型、同一份代码库,分别用三种方式推进,记录每次请求的输入 Token、缓存命中 Token 和实际计费。

三种方式:

  • A 方式:一次性把需求、约束、验收标准全部说清楚,让 Codex 连续执行,中途不打断。
  • B 方式:分阶段推进,每完成一个文件说一次“继续下一个”。
  • C 方式:每改一个文件就新开一个会话,重新描述需求。

4.2 数据记录

指标A 方式B 方式C 方式
请求次数3119
总输入 Token38,500142,00096,000
缓存命中 Token34,20048,00012,000
缓存命中率88.8%33.8%12.5%
折算计费 Token约 7,300约 104,000约 88,000
任务完成质量完整完整有遗漏,需补

4.3 数据背后的三个结论

结论一:请求次数比总 Token 更能预测成本。A 方式请求 3 次,B 方式 11 次,成本差 14 倍。请求次数越多,每次都要重新过一遍上下文,累积效应惊人。

结论二:缓存命中率是放大器。A 方式命中率 88.8%,B 方式 33.8%。命中率每降 10 个百分点,实际成本大约涨 30% 到 50%。这个杠杆比选哪家中转站重要得多。

结论三:新开会话不一定省钱。C 方式看起来每次上下文都短,但因为没有历史可复用,缓存命中率只有 12.5%,而且需求要反复描述,反而比 B 方式省不了多少,质量还下降了。

提示:如果你发现自己的账单比预期高,先别怀疑中转站。把请求日志拉出来,看两个数:请求次数和缓存命中率。这两个数基本能解释 80% 的成本差异。

5. 把“继续”用对的四个实操技巧

5.1 一次性把话说清楚,减少打断

最省钱的做法,是在第一次请求时就把需求描述完整。包括:

  • 要改哪些文件,改成什么样。
  • 有什么约束(比如不能引入新依赖、必须兼容某个版本)。
  • 验收标准是什么(比如跑通某个测试、返回特定格式)。

你描述得越清楚,Codex 需要你中途补充的概率越低,请求次数越少,缓存命中率越高。

我自己的习惯是,在描述需求前先花两分钟列个清单,把“它可能会问我的问题”提前回答掉。这两分钟能省下后面十几次“继续”。

5.2 用“批量指令”代替“逐条继续”

如果任务确实需要分步,不要每步都说“继续”,而是一次性给出接下来几步的指令。

比如不要说:

继续 (等它做完) 继续 (等它做完) 继续

而是说:

接下来按顺序做三件事:第一,给 login 接口加 JWT 签发;第二,给需要鉴权的路由加装饰器;第三,写一个测试用例验证。做完一起告诉我。

这样 Codex 会在一次或少数几次请求里完成多步,上下文追加更规律,缓存更容易命中。

5.3 控制工具调用的动态内容

如果你有能力调整工具配置(比如自己写 wrapper),尽量让工具返回的内容去掉时间戳、随机 ID、临时路径这些每次都变的东西。这些内容看着不起眼,但它们是缓存杀手。

具体做法:

  • 读取文件时,只返回文件内容,不要带“读取于 XX 时间”。
  • 执行命令时,如果输出里有时间戳,考虑过滤掉。
  • 搜索结果里如果带行号或临时索引,确认是否必要。

这些细节在中转站层面你控制不了,但如果你用的是可配置的客户端,值得花时间调一调。

5.4 会话该断则断,但别乱断

会话不是越短越好。判断标准是:如果接下来的任务和当前上下文强相关,就继续用;如果完全无关,就新开。

强相关的例子:同一个模块的连续修改、同一个 bug 的多轮排查。 无关的例子:修完 A 模块,去写 B 模块的文档。

乱断会话的代价是缓存归零、需求重述。该断不断,代价是上下文里塞满无关内容,每次请求都多花钱。

6. 常见问题与排查速查表

6.1 账单异常时的排查顺序

发现这个月 Token 用量暴涨,按这个顺序查:

  1. 看请求次数:是不是比平时多了很多?多出来的请求是不是都是短指令(“继续”“go on”)?
  2. 看缓存命中率:如果低于 50%,说明上下文前缀不稳定,找原因。
  3. 看单次请求的平均输入 Token:如果某几次特别高,看看那几次是不是触发了大量工具调用。
  4. 看是否有账号切换:切账号会导致缓存完全失效,这是最隐蔽的坑。
  5. 看中转站策略:换一家对比,确认不是服务端缓存支持的问题。

6.2 高频问题速查

现象可能原因处理方式
同样任务成本比别人高几倍请求次数多、缓存命中率低合并指令,减少“继续”
切账号后上下文加载不了缓存按账号隔离重要任务不要中途切账号
工具调用后缓存命中率骤降工具返回动态内容过滤时间戳/ID
长对话后期每次都很贵上下文膨胀适时新开会话,重述需求
中转站之间价格差异大缓存策略不同实测缓存命中率再选
输出质量下降上下文过长导致注意力分散拆分任务,控制单次上下文长度

6.3 几个容易忽略的细节

细节一:系统提示词也占 Token。有些工具的默认系统提示词很长,这部分每次请求都带,虽然通常能命中缓存,但如果中转站不支持系统提示词缓存,成本会明显上升。

细节二:输出 Token 贵,但输出通常不多。编程场景里,模型输出主要是代码和简短说明,输出 Token 占比不高。真正的大头永远是输入。

细节三:包月套餐不一定划算。如果你的使用模式是高频短请求(大量“继续”),包月可能看起来划算,但实际上你消耗的资源远超预期,中转站迟早会限速或调整策略。按量计费反而能让你看清真实成本。

细节四:上下文长度有硬上限。当上下文接近模型窗口上限时,有些工具会自动截断或压缩历史,这会导致缓存失效,而且可能丢失关键信息。长任务要主动管理上下文,别等它自己爆。

7. 我踩过的几个坑

第一个坑是迷信包月。早期我买了一个号称不限量的套餐,结果用了两周就被限速,客服说我的请求模式“异常”。后来看日志才明白,我大量的“继续”导致请求次数极高,虽然每次 Token 不多,但请求频率触发了风控。

第二个坑是频繁切账号。有段时间我用 cc-switch 在几个账号之间切换,想分摊用量。结果发现每次切换后,第一轮对话都特别贵,因为缓存完全没了。后来固定用一个账号跑长任务,成本反而降下来了。

第三个坑是不看日志。中转站后台一般都有请求明细,但我一开始从来不看。直到有一次账单翻了三倍,我才去翻日志,发现有一半的请求都是“继续”“接着”“go on”这种。把这些合并之后,成本立刻回到正常水平。

第四个坑是以为新会话省钱。前面实验里的 C 方式就是我的真实经历。当时觉得每次新开会话上下文短,应该便宜。结果因为要反复描述需求,加上缓存命中率极低,实际成本比连续对话还高,而且产出质量不稳定。

8. 给不同阶段使用者的建议

如果你刚开始用 Codex 这类工具,先别急着优化成本。第一步是建立看日志的习惯。每次任务结束后,花一分钟看看这次用了多少 Token、缓存命中多少、请求了几次。看多了,你自然会对“哪些操作贵”有直觉。

如果你已经用了一段时间,账单开始让你肉疼,那就从**减少“继续”**开始。把能合并的指令合并,把能一次说清的需求说清。这一个动作通常能省下 50% 以上的成本。

如果你在选型阶段,对比不同中转站,别只看单价。实测缓存命中率才是关键。同样的任务,在 A 家命中率 80%,在 B 家只有 30%,那 B 家单价再低也不划算。

如果你是自己搭工具链的,那可以做得更细:控制工具返回内容、优化上下文组装顺序、给系统提示词做缓存友好设计。这些优化一次投入,长期受益。

最后说个我自己的判断:Token 成本管理的本质,不是抠单价,而是管理上下文。谁能让上下文更稳定、更可预测,谁就能用更少的钱做更多的事。那句轻飘飘的“继续”,背后是一整套上下文工程的问题。想明白这一点,账单自然就下来了。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进