我最近把 codex 的流量跑了一遍,上传量直接飙到 GB 级别,一开始我还以为是网速统计坏了。后来把日志拉下来逐段看,才发现真正的浪费根本不是网络问题,而是 token 在以一种相当隐蔽的方式被烧掉。如果你也在用 codex 做自动化编码,这篇文章应该能帮你省下不少 token,也能让你对自己的项目流量到底花在哪有底。
先说结论:codex 这类 agent 工具的上传流量,绝大部分不是你的代码,而是上下文重放和工具调用记录。你看到的每一个字节上传,最终都会变成 token 计费。所以搞清楚流量构成,本质上就是在搞清楚 token 消耗模型。
1. codex 的 token 消耗到底怎么产生的
1.1 拆开 codex 的工作机制:它为什么需要上传那么多东西
codex 是一个 agent 型编程工具,它和我们平时直接调用大模型 API 聊天完全不同。普通聊天是一次请求一次响应,上下文就那几百行对话;codex 则是一个不断循环的自动化工作流:它读取你的代码库、扫描文件、理解任务、调用命令行工具、查看结果、修改文件,然后再读再改。每一步,它都要把当前的完整状态发给模型,让模型根据这些状态做下一步决策。
这个机制决定了它的 token 消耗天然比普通编程辅助高一个量级。举个例子,你让 codex“帮我把这个模块的重试逻辑加上”。它先要读取整个模块文件,这可能是几百上千行;然后它需要理解项目里相关工具的用法,可能要读配置文件和依赖清单;接着它开始改代码,改完还不算完,它要运行测试、观察输出、根据报错继续修改。这些过程中,每一次模型调用都带着完整的上下文快照——文件内容、指令、历史决策、工具输出——全都要重新上传一遍。
关键问题就在这个“重新上传”上。大模型的 API 大多是无状态的,服务端不会替你记住上一次的对话,所以 codex 客户端必须把此前累积的所有消息、文件内容、工具结果,原封不动地随每次请求发出去。这就是为什么你的上传流量会呈线性甚至超线性增长:会话越长,单次请求的 payload 越大,上传的总流量就越惊人。
我自己抓到的实际数据是:一次大约 40 分钟的任务,总计上传约 1.2GB 流量,而项目源代码总共不到 2MB。这中间的差距,全是重复发送的上下文和过程数据。
1.2 别把 token 和流量混为一谈,但它们的计算逻辑是联动的
很多人有个误区:觉得 token 是算出来的,和网络流量没关系。实际上 token 和流量是强相关的。模型 API 计费按 token 算,但 token 是从你上传的文本内容里切出来的,你的请求体多大、内容多少,直接决定这次请求消耗多少 token。
上传 1GB 流量是什么概念?换算一下,如果这些内容全部是文本(codex 的请求体基本都是 JSON 文本),1GB 大约对应 2.5 亿到 3 亿个 token——当然现实中不是所有流量都能直接计费,有些是协议开销和压缩,但即便打个一折,也是千万级别的 token 消耗,按常见费率计算,一次任务烧掉几十美元非常正常。
这里要给一个基础换算公式:大致来说,1 个 token 约等于 0.75 个英文单词,或者 0.5 个中文字符(不同分词器有差异)。你上传 1000 行代码,约 3 万字符,可能切成 8000 到 15000 个 token。这还只是一次上传,而 codex 在一次任务里可能上传几十次甚至上百次相同内容。
所以当你看到上传流量飙到上 G 时,第一反应不应该是“网络问题”,而应该是“我的上下文在膨胀”。流量只是表象,token 才是真正的账单。
2. 一次真实流量拆解:上 G 的流量到底去了哪
2.1 把一次 codex 会话的流量按阶段分类
我为了摸清流量去向,做了一次完整的实验。做法不复杂:在本地起一个日志记录服务,把 codex 所有外发请求记录下来,统计每个阶段的请求大小和频次,然后按功能分类。
我选了一个略复杂的任务:让 codex 为项目添加一个数据库重试机制,包含单元测试。整个任务耗时约 40 分钟,最终上传 1.2GB,下载约 300MB。按阶段切分后,数据分布如下:
| 阶段 | 上传占比 | 说明 |
|---|---|---|
| 系统提示与内置工具说明 | 约 5% | 每次请求都会携带,固定开销 |
| 历史消息重放 | 约 45% | 之前所有对话和决策记录,不停重复上传 |
| 文件内容读取 | 约 30% | 读取项目文件、配置、测试代码 |
| 工具调用结果与日志 | 约 15% | 命令行输出、测试结果、错误信息 |
| 其他(协议头、认证信息等) | 约 5% | JSON 序列化开销、HTTP 元数据等 |
这个分布让我非常意外的是:真正首发的“新内容”只占很少一部分,绝大部分是旧内容的重放。也就是说,你在第 30 分钟看到的流量,可能 80% 都是第 5 分钟就已经上传过的东西,只是每次都跟着请求再走一遍。
2.2 一个请求的 payload 解剖
再看看单次请求。一个典型的 codex 请求体大致长这样:系统消息、用户任务描述、之前的 assistant 回复、工具调用记录、工具返回内容、当前需要模型处理的文件内容,以及各种元数据。
我用一个简单的任务做测试:让 codex 读取一个 300 行的文件并修改其中 3 行。这个任务 model 实际只调用了 4 次,但上传总量大约是文件本身的 20 倍。
第一次请求:上传完整文件(300 行,约 12KB),加上系统提示词和任务描述,总体约 20KB。后续每次调用,都会包含:系统提示词、任务描述、上次生成的回复、上次工具调用的详细参数和结果、以及再次上传的文件内容——因为工具结果里就包含了文件内容。所以第三次请求时,payload 已经到了 60KB,第四次进一步膨胀。一个只改了 3 行的任务,上传了约 80KB 的有效数据,其中 60KB 以上是重复内容。
这个比例推演到真实项目,就是你代码量越大、会话越长,重复上传的内容指数级增长。codex 本身没有做深度的上下文去重,它按照最原始的方式:把整个消息历史原样打包发送。
3. token 浪费的五大重灾区
3.1 系统提示与内置指令:每轮都交的“过路费”
codex 和其他编程 agent 一样,每次模型调用前都要附带一份完整的系统提示词。这里面包含:模型的身份设定、行为规范、工具使用规则、输出格式要求、安全限制等。这份提示词在每次请求里都会出现,一个字不少。
我实测下来,codex 的系统提示词加上工具定义,一次大约 3000 到 6000 个 token。如果一次任务触发 40 次模型调用,这就意味着你为这份固定提示词支付了 12 万到 24 万 token。它不算最多,但它是纯消耗——你什么实际工作都没多做,只要模型被调用,它就要收费。
问题在于,这部分没法手动删减。你没办法让 codex 不带系统提示词。但你可以通过减少不必要的模型调用来降低它的影响占比。比如避免让 codex 反复检查同一份文件、避免问一些它明显不需要问的问题。
3.2 历史消息重放:最隐蔽的流量黑洞
这是最让我肉疼的一块。codex 在长会话中会把整个对话历史反复上传,而且这里的历史不仅是用户和 assistant 的文本对话,还包括中间所有工具调用的输入和输出。
举一个真实场景:你让 codex 执行一项任务,它决定先看项目结构,调了一次 ls 类命令,返回了目录树;接着它读了三个文件,每个文件的完整内容都进了工具结果;然后它改了一个文件,diff 也进记录;再跑测试,输出 200 行日志。这些全被存进消息历史。
问题在于:当模型下一次需要决策时,它需要看到之前的步骤。但 codex 并没有对历史做任何摘要或剪枝,它会原样把上面这一大坨全部重新上传。我数过,一次真实任务中,某个测试日志片段被原样上传了 7 次。你为这段日志付了 7 次 token,而不是 1 次。
这种浪费在代码量大、反馈多的任务里格外致命。模型每多跑一个工具调用,后续所有请求的体积就更大一分。会话越长,单次请求越重,最终累积出 GB 级的上传流量。
3.3 工具调用结果:日志和报错被无限放大
codex 运行命令时,会把标准输出和标准错误全部捕获,传给模型。如果命令输出本身很大——比如跑全量测试、构建项目、查看大日志——这部分内容会完整进入上下文。
更尴尬的是,模型有时候只是需要“确认一下是否有报错”,但整段输出已经全部打包上传。我见过一个极端案例:一次构建失败只有一行关键错误,但 codex 把整个构建过程的 5000 行日志全传上去了,因为它的工具设计就是“捕获完整输出”。
这部分的优化空间主要在任务设计上:让 codex 执行精准命令,避免全量测试和全量构建;用 grep 先过滤再查看;把输出重定向到文件再让 codex 读取文件的特定行。这些看起来很基础的操作,能把工具调用的 token 消耗降低一个量级。
3.4 大文件全量读入,而不是按需读取
codex 读取文件时,默认会把整个文件塞进上下文。对于一个 1000 行的文件,这可能是 1 万到 2 万个 token。如果这个文件只有 50 行和任务相关,剩下 950 行全是无关内容,那这 950 行就是纯浪费。
我在流量分析里发现了一个现象:很多任务中,某个文件被反复读取多次,每次都是完整读入。哪怕它只是几百行的小文件,在长会话里被读 5 次、10 次,token 消耗也会明显膨胀。
解决思路是给 codex 提供更精确的上下文集。你可以主动告诉它“只需要看 src/retry.ts 的 30 到 80 行”,或者提前把关键代码片段粘贴到任务描述里,而不是让它自己去找。codex 在文件选择上做得比较机械,你喂给它的信息越精准,它浪费的 token 就越少。
3.5 连续重试与失败调用:钱花了,事情没办成
最后一个重灾区是重试。codex 在遇到模型输出格式不对、工具调用出错、网络异常时,会自动重试。每次重试都是一次完整的模型调用——同样的上下文,再烧一遍同样的 token。
我在日志里看到:一次任务中,codex 因为一个工具参数格式错误,连续重试了 4 次。每次重试都带了完整的上下文,约 8 万 token。也就是说,这个小错误直接造成了 32 万 token 的额外消耗。这还是一次任务里的一个小插曲。
重试问题很难完全避免,但可以减少发生率:确保项目环境干净、依赖完整,减少工具报错;任务描述尽量清晰,减少模型“猜”的概率;如果你发现 codex 在反复重试同一个动作,立刻中断它,改写任务描述再继续,不要让它自己在泥潭里打转。
4. 怎么量化定位你自己的 token 浪费
4.1 从日志里找突破口
想分析 codex 的 token 消耗,不需要什么高端工具。codex 本身有日志输出,你可以在启动时开启 verbose 模式,或者把日志落盘。日志里能看到每次请求的时间、模型、以及大致的输入输出 token 数。
我建议先跑一个短任务,把日志打开,观察三个指标:单次请求的输入 token 数、请求频率、累计输入 token 增速。如果单次请求的 token 数随任务进展急剧上升,说明历史重放在加速膨胀;如果请求频率过高,说明模型在频繁地做小决策,效率偏低。
另外一个笨但有效的办法:在本地起一个轻量流量统计工具,记录 codex 所有外发请求的字节数。不需要解析内容,只看体积曲线。曲线平缓说明正常,曲线陡增说明某一步引入了大体积内容,可能是因为读取了大文件或者捕获了超长输出。
4.2 控制变量法:一次只改一个条件
定位 token 浪费,最靠谱的是控制变量法。我做过一组对比实验,帮你理解哪种因素影响最大:
| 实验条件 | 任务相同但变量不同 | 总 token 消耗 |
|---|---|---|
| 基线 | 直接让 codex 干活 | 100% |
| 精简任务描述 | 明确到文件与行号 | 约 70% |
| 拆分任务 | 拆成 3 个短会话 | 约 55% |
| 全程限制命令 | 禁止全量测试,用精准查询 | 约 40% |
数据来自我自己的项目,不完全代表所有场景,但趋势非常明显:会话越短、输入越精准、工具输出越少,token 消耗下降越显著。这四个变量里,会话时长的影响最大。长会话在 token 消耗上是灾难级的。
所以我在分析完自己项目的流量后,第一件事就是把原来“一个会话干到底”的习惯改了。现在遇到复杂任务,我拆成多个短会话,每段只做一件事,token 消耗直接降了一半多。
4.3 善用请求计数与 token 统计字段
几乎所有 OpenAI 兼容 API 的响应里都带 usage 字段,里面是 prompt_tokens、completion_tokens 和 total_tokens。如果你接入的是第三方中转服务或者自己的网关,通常也能在管理面板看到这部分统计。
但 codex 客户端本身不会把这些数据汇总给你看,你需要自己从日志里提取。写一个简单的脚本,正则提取 usage 字段,按时间累加,就能得到整个任务的 token 消耗曲线。
我自己写的统计脚本大概是这样的思路:读取 codex 日志文件,匹配包含“usage”的行,解析出 prompt_tokens 和 completion_tokens,把每次调用的数据累加起来。再按会话维度分组,就能看到每个会话的消耗对比。这个脚本不复杂,但能让你对 token 消耗有完全透明的掌控。有了数据,优化就不是拍脑袋,而是有的放矢。
5. 实战优化:让每一颗 token 都花在刀刃上
5.1 改变任务描述方式,减少模型“摸黑探索”
优化 token 消耗,第一优先级不是改配置,而是改你说话的方式。我自己最大的经验是:不要在任务描述里只给目标,要给路径和边界。
反面例子:让 codex“看看项目的错误处理有没有问题”。这会让它从头扫描整个项目,读取大量文件,全部塞进上下文,然后给你一个泛泛而谈的结论,token 烧掉一大片。
正面例子:“请阅读 service/user.ts 第 100 到 180 行的 catch 块,检查异常捕获是否存在吞错问题,只针对这个文件。不要查看其他文件。”这样 codex 的探索范围被严格限定,它不需要读无关文件,历史上下文的膨胀速度也会慢很多。
这个方法有效的原因很简单:codex 本身对“应该看哪里”没有强判断力,它倾向于多读多看来获取安全感。你替它划定了边界,它的信息获取就精准了,token 浪费自然减少。
5.2 用短会话替代长会话,从根上切断历史重放
代码上我推荐一个“单任务单会话”原则:一个会话只做一件事,做完立刻开新会话。
为什么这是核武器级别的优化?因为历史重放的 token 消耗和会话长度成正比,短会话从源头上砍掉了大部分重放。我之前测试过长任务拆分为 3 个短会话,总 token 消耗比单会话直接少了约 45%。那种 20 多个来回的长对话,超过一半的 token 都消耗在“回忆过去”上。
实际操作中,你可以在一个任务完成后,先清空会话,把新任务的关键背景(文件路径、需要注意的约定)重新贴在任务描述里。这看起来多花了一点首轮 token,但远比长会话里反复重放整套历史要省钱。
5.3 从配置层面压缩模型调用成本
codex 本身支持在配置里指定模型。不同模型的定价差异很大,一些新模型的输入 token 单价可能比老模型高一截。如果你只是做常规重构或单文件修改,不需要用最强的模型,选个性价比高的就够用。
我目前的做法:简单任务用一个中等模型,复杂架构设计再用强模型。切换成本不高,却能在不改变行为模式的情况下,让 token 单价直接降下来。
还有一个细节:模型支持上下文压缩能力。有些模型会把中段的历史消息做摘要,减少后续请求的体积。如果你的模型支持这类特性,可以考虑开启,代价是模型可能丢失一些细节,适合对精确度要求不高的任务。
另外,我在配置里把 temperature 调低了,让模型输出更稳定、重试更少。重试少一次,等于省一次完整上下文的 token,往往比调低温度带来的输出质量变化更有价值。
5.4 限定工具输出与文件读取范围
在上文的重灾区里,工具输出和大文件读入是两大块。实操上,我会在任务描述里直接写清楚命令边界:
- “只运行
python manage.py test tests/test_retry.py -k test_single_retry,不要跑全量测试。” - “如果命令输出超过 100 行,请先 grep 关键词,不要直接把完整输出贴进来。”
- “只读取这些文件:
src/config.ts、src/retry.ts,其他文件不要碰。”
这些指示看起来琐碎,但对于控制上下文体积非常有效。codex 的指令遵循能力很不错,它会在工具调用时更克制,输出的内容也更精简。让模型“少看、少跑、少传”才是真正的省钱逻辑。
我见过一个团队,把“禁止读取文件目录树”写进了团队的 codex 默认任务模板,原因是目录树在每次工具调用后都会进入上下文,积少成多,一个会话下来能省 5 万 token。
6. 常见问题与排查实录
6.1 token 相关报错与登录问题
在分析流量和 token 消耗的过程里,很多人卡在第一步就没有进行下去——codex 根本登录不上,报各种 token exchange 错误。我整理一下常见的报错场景和解决方法:
| 报错特征 | 常见原因 | 处理方式 |
|---|---|---|
| sign-in could not be completed, token exchange failed, token endpoint returned status 403 forbidden | 登录令牌无效、账号状态异常或临近过期 | 退出登录,重新走一遍登录流程,必要时清理本地缓存凭据 |
| your access token could not be refreshed, token was revoked | 刷新令牌被服务端吊销,通常是因为设备权限变更或账号在别处重新登录 | 登出后重新认证,不要手动粘贴旧令牌 |
| login server error, token exchange failed, error sending request | 登录过程本地网络不稳定,导致请求失败 | 检查网络连通性,重试;如果频繁出现,考虑是否是本地代理配置导致请求被拦截 |
| codex auth token is unavailable | 客户端没拿到有效 token,可能初始化流程未完成 | 重新执行登录,确认 auth 服务正常启动,查看日志中是否有更详细的错误码 |
抛开敏感的网络话题,我的经验是:这类报错九成是令牌过期或登录态缓存损坏。先做最简单的“退出登录、清缓存、重新登录”,大部分能解决。如果还不行,再看日志里具体的 HTTP 状态码。
6.2 流量大但没干多少活:先看请求体积曲线
很多人问:为什么我的 codex 跑了一会儿就上传了几百 MB,但它明明没做什么事?答案通常在请求体积曲线里——请求次数不多但单次请求很大,说明上下文已经膨胀;请求次数极多但每次都不大,说明模型在无效决策。
这两种情况处理方式完全不同:前者要缩短会话、减少文件读取;后者要简化任务、明确指令、减少瞎折腾。所以排查的第一步永远是先看数据,而不是猜。
6.3 接入第三方模型的 token 消耗对照
现在很多人用 codex 搭配兼容 OpenAI 接口的第三方模型(比如 DeepSeek 等)。这里要提醒一个坑:codex 的流量模型和它内置的提示词是固定的,你换模型并不会让它的系统提示词变短,也不会让它的工具调用输出变小。
所以第三方模型的优势主要是单价更低,而不是token 消耗更少。如果你发现接入第三方模型后流量依然巨大,不要惊讶,本质原因是 codex 的工作机制没有变,只是计费方变了。省钱的正确姿势是用第三方模型承担高频的中小任务,让主力模型处理更复杂的架构设计。另外注意,部分模型在 codex 中不可用,比如系统提示“the 'gpt-5.6-sol' model is not supported”之类的报错,出现时换一个支持列表内的模型即可。
关于 token 计划的模型选择,我常用的一个策略:预算充足时,任务按“探索-执行-复核”拆分,探索和执行用不同模型,复核用最强模型看细节。预算紧张时,全部使用中等模型,把长会话拆短,效果往往比用强模型硬扛一个长会话更好。
7. 一些实操体会
7.1 先搞清楚自己的消耗曲线,再谈优化
我第一次分析 codex 流量时,最大的意外是发现“我以为的优化”和“实际的消耗占比”完全对不上。我原先以为省 token 的关键是少问问题,实际上省 token 的关键是缩短会话、减少历史重放。没有数据支撑的优化,基本都是瞎忙。
所以如果你也想分析自己的 codex token 消耗,我给你一个最低成本的入手方式:直接看一次 20 分钟任务的日志,记录首尾两次请求的输入 token 数。如果尾部请求是首部的几倍甚至十几倍,你的浪费点已经找到了——历史重放。
7.2 优化 token 不是抠门,而是提升效率
很多人会觉得,省 token 就是省钱,就是抠门。但在我自己的实践里,token 优化的本质是让 codex 更专注、更高效。上下文越精简,模型被无关信息干扰越少,它的判断反而更准,重试更少,任务完成质量更高。
你可以把 codex 的上下文想象成一个工作台。工作台越大,你能同时摆开的东西越多,但真正影响你决策的还是眼前这一小块。codex 的上下文也是同理,塞满无关内容并不会让它更聪明,只会让它更难抓到重点。把工作台收拾干净,它干活反而更快。
7.3 最后一个小技巧
我会在每次任务开始前,用一句话告诉自己这个任务的边界、手头的关键文件和预期的产出。这个习惯看起来是给自己看的,但当你把它粘贴到 codex 的任务描述里时,效果立竿见影:codex 的探索变少了,工具调用更精准了,token 消耗肉眼可见地下降。
用一句话总结实际操作中的体会:codex 的 token 浪费,七成是机制决定的,三成是使用习惯决定的。机制那部分要靠选短会话、换合适模型来规避,习惯这部分靠你每次喂给它的任务描述来改善。两边都抓好,你会发现自己的模型调用成本可以降一半以上,而且任务完成质量并不下降。