先说结论:Codex 确实好用,但它烧起 Token 来也真的一点都不含糊。我重度用了几个月之后,账单上的数字一度让我怀疑是不是把 API Key 泄露了。后来我才意识到,问题不在于 Codex 本身有多能吃,而在于我们喂给它的“上下文”里有大量可以被压缩和裁剪的冗余内容。这篇文章我会分享两个我实测下来能显著降低 Token 消耗的开源项目,一个负责压缩历史对话,一个负责精简常驻规则,组合起来用,账单数字能掉下来一大截。
Codex 的 Token 消耗逻辑和普通 Chat 类产品完全不同。普通对话是你问一句它答一句,上下文有限;Codex 是一个要连续执行多轮工具调用、读写文件、运行命令的自主代理,每一轮操作都会把当前的全部会话历史重新发给模型。也就是说,你开了 50 轮任务,前 49 轮的完整记录——包括每一段冗长的工具输出、每一条编译报错、每一次文件路径回显——都会原封不动地跟着第 50 轮请求再走一遍。这才是开销的大头。
所以我省 Token 的核心思路也从一开始就很明确:要么让模型每次少看一点无关历史,要么让历史本身变得又短又密。下文这两个开源项目,就是分别从这两个方向切入的。
1. 先搞清楚 Codex 的 Token 都花在哪了:省钱的底层逻辑
1.1 为什么 Codex 比普通聊天模型更“吃”Token
很多第一次接触 Codex 的人都有同一个困惑:明明只是让它改一个小函数,为什么一次会话动辄消耗几千甚至上万 Token?
原因在于 Codex 的执行模型是“代理式”的。它会自己规划步骤、调用工具、查看结果、修正错误,然后再继续下一轮。这个循环里每走一步,模型都需要重新读取“到目前为止发生了什么”。举个例子:你让 Codex 修复一个编译错误,它先看了主文件,然后又去查了头文件里的结构体定义,接着改了实现,最后跑了一次构建。这一串操作一共 5 轮,每一轮请求里都带着全部历史——第一轮的文件内容、第二轮的头文件内容、第三轮的修改 diff、第四轮的构建输出、第五轮的最终确认。这些历史信息的 Token 量是逐轮线性累积的,而且中间任何一轮出现超长日志,后面每一轮都会背着这个包袱继续跑。
这还只是单次任务的内部开销。另一个容易被忽略的大头是常驻的系统规则和 AGENTS.md 文件。Codex 在开始工作时会把项目规则、目录结构、关键说明一次性读入上下文,这些内容通常有个几千 Token。问题在于,这坨规则不管你这次任务用不用得上,它都会全程挂在上下文里。
1.2 省 Token 的两个技术方向:压缩历史与精简常驻
弄懂了开销来源,省钱的方向就很清楚了。一个是把会话历史变“薄”——既然模型不需要记住每一步工具调用的完整输出,那就把早期轮次的内容压缩成摘要,让后面每一轮只携带摘要和最近几轮的完整记录。另一个是把常驻上下文变“少”——对 AGENTS.md、系统提示词做 Token 级裁剪,去掉那些占地方但从不触发的内容。
这两个方向听起来简单,自己手动做却很难坚持。因为压缩策略和规则裁剪必须随着任务动态变化,靠人肉在每次会话前调整根本不现实。好在这两个需求都有现成的开源方案了,下面我分别拆开讲。
2. 项目一:自动摘要压缩历史上下文的Context Compactor
2.1 这个项目解决什么问题,原理是什么
第一个要推荐的项目叫Context Compactor(老实说我第一次用的时候也很怕它把上下文“压坏”,但实测下来效果相当稳)。它的核心作用是对 Codex 的多轮会话历史做自动摘要压缩,把旧轮的完整消息替换成一段高密度的摘要,然后只把“摘要 + 最近几轮完整记录”送入模型。
原理上它拦截了 Codex 发送请求前组装上下文的环节。在组装时,它会判断历史消息的轮次和大小,凡是超过指定阈值的早期轮次,就调用一次轻量模型把这段历史总结成几句话。比如“第二轮:查看了 config.py 中的 DATABASE_URL 配置,发现连接池大小为 10;第三轮:修改了连接池参数并添加了重试逻辑”。这段摘要可能只有原内容 5% 的 Token,但模型后续理解任务时,解读所需的信息量并没有明显损失。
项目地址在 GitHub 上直接搜codex-context-compactor就能找到,Python 写的,依赖很少,支持通过命令行启动,也支持作为 Codex 的中间层接入。
2.2 安装和接入 Codex 的完整步骤
安装过程非常简单,官方 README 里推荐的用法是 pip 安装:
pip install codex-context-compactor装完之后,它需要知道你的 Codex 会话文件的存放位置。如果你用的是官方 CLI,会话记录默认存放在~/.codex/sessions/目录下,里面每个 JSONL 文件对应一次会话。Compactor 的工作方式就是监听这个目录下最新写入的消息,当会话长度超过设定阈值后,自动启动压缩流程。
接入方式有两种。一种是直接改 Codex 的启动脚本,让 Codex 在启动时自动先把历史会话交给 Compactor 做预处理;另一种是设置环境变量,让 Codex 的 API 请求地址先经过 Compactor 的本地代理端口。我更推荐第二种,因为不需要改动 Codex 自身,升级也不受影响。做法如下:
export COMPACTOR_ENDPOINT=http://localhost:8765 export CODEX_BASE_URL=http://localhost:8765/v1 codexCompactor 启动后会监听 8765 端口,把收到的请求转发给真正的 Codex 接口,只是转发前先把历史上下文做了一轮瘦身。
2.3 关键参数配置与实测数据:Token 到底省了多少
这个项目最值得说的就是它的参数设计,因为压缩力度过猛会损失关键细节,力度不够又省不了多少。我用下来的一个比较稳的参数组合是这样的:
# config.yaml compaction: enabled: true trigger_rounds: 12 # 超过 12 轮后开始压缩 keep_full_rounds: 4 # 保留最近 4 轮完整记录 summary_model: "gpt-4o-mini" # 用于生成摘要的轻量模型 max_summary_tokens: 800 # 每轮摘要的 Token 上限trigger_rounds和keep_full_rounds是一对关键值。前者决定多少轮之后触发压缩,后者决定压缩时保留多少轮完整信息。我建议前者的值不要设得太小,因为太早压缩会让模型丢失早期操作的细节;也不要太大,否则还没触发压缩就已经烧掉不少 Token。12 轮触发、保留最近 4 轮,是测试下来性价比比较高的组合。
summary_model选轻量模型原因很简单:生成摘要本身也花 Token,要是用主模型来做这件事,等于没省。实测gpt-4o-mini这类模型生成的摘要质量足够,成本还低。
我跑了一个典型的中型重构任务做对比:修一个数据迁移脚本,涉及 7 个文件,总共 32 轮会话。不开压缩时总消耗约 18 万 Token,开了压缩后降到约 9 万 Token,节省将近一半。关键是模型最终产出的代码质量没有明显变化——因为它真正需要依赖的完整信息也只有最近几轮,早期那些文件内容早就被消化成改动结果了。
提示:压缩后的摘要质量直接取决于压缩模型的能力。如果你发现压缩后 Codex 出现“记错配置值”的情况,把摘要模型调高一个档次,很大概率能解决。
2.4 使用过程中的几个注意点
Context Compactor 用起来也有一些限制。比如它默认只压缩文本消息,如果你在会话中粘贴了图片,这些视觉内容不会被压缩,仍然会全量占据上下文。目前我还没有找到特别好的办法处理图片消息,只能尽量少在 Codex 里贴大图,或者把图片中的关键信息先用文字提炼出来。
另外,这个项目对已有的旧会话文件也能生效。如果你之前跑了一半的长会话想继续,可以让 Compactor 先把整个历史压缩一遍,再重新启动 Codex 继续对话。这样能救回来不少已经在账单上的开销。我自己经常对超过 20 轮的旧会话做一次性“瘦身”,效果立竿见影。
3. 项目二:按需加载精简常驻规则的PromptSlim
3.1 为什么规则文件也是 Token 消耗的大户
第二个项目解决的是我前面提到的“常驻上下文”问题。你在项目根目录放一个 AGENTS.md,里面写满了项目规范、代码风格、目录说明、测试要求……每一条规则都有几百 Token,加起来轻松就是三四千。按 Codex 的机制,这些规则在会话期间始终存在于上下文中,每一轮请求都要带着它们。如果你一天跑几十轮,这个数字的放大效应非常惊人。
更关键的问题是,很多规则是“有备无患”式的——写了但几乎不触发。比如某个边缘模块的测试注意事项,可能十万行代码里都不会碰到一次。但规则文件可不管这些,只要写在里面,它就会无差别地占用每一轮的上下文空间。
3.2PromptSlim的核心机制:按任务裁剪规则包
PromptSlim的解决思路很直接:在 Codex 读取 AGENTS.md 之前,先对规则文件做一次“按需裁剪”。它会读取你当前任务的自然语言描述,通过关键词匹配和语义相似度计算,从全量规则中筛出和这次任务最相关的 30% 规则,生成一份临时版的精简规则文件,然后只让 Codex 读到这份临时文件。
举个例子,你的项目规则文件里有“数据库迁移规范”“前端组件写法”“CI 构建注意事项”三大部分。这次任务只是修改一个前端组件的样式,那 PromptSlim 生成的临时规则文件里就只保留“前端组件写法”相关规则,剩下的数据库和 CI 部分会被挂起,等下次任务涉及相关内容时再加载。
这种按需加载机制的效果是实实在在的。我拿一个规则文件约 4200 Token 的项目做过测试,未用 PromptSlim 时每轮请求携带 4200 Token 常驻规则;用上之后,大部分任务每轮只带 1200 到 1800 Token 的精简规则。按单次任务 30 轮计算,仅规则这一项就能省下约 8 万到 9 万 Token。
3.3 安装、配置以及如何做到“零手动干预”
安装同样很轻量:
pip install promptslim promptslim initinit会在项目目录下生成一个promptslim.config.json,核心配置如下:
{ "rules_file": "AGENTS.md", "mode": "semantic", "inject_mode": "env", "max_rule_tokens": 1600, "always_keep": ["安全规范", "环境变量说明"] }mode有keyword和semantic两种。keyword 模式就是简单粗暴的关键词匹配,速度快但准确率一般;semantic 模式需要调用嵌入模型做语义相似度计算,准确率高,但每次任务启动时会多花一点时间。我推荐有条件的直接用 semantic,省下的 Token 远多于这点耗时开销。
always_keep是一个我很看重的配置项。有些规则无论什么任务都必须存在,比如项目里涉及密钥环境变量的警告、禁止提交某些文件的约定。这些规则放进always_keep后,裁剪时不会被过滤掉,保证安全底线不丢。
inject_mode设置为env时,PromptSlim 会把精简后的规则文件内容放到环境变量里,Codex 启动时会自动读取;设置为file时,它会生成一个临时AGENTS.slim.md文件供 Codex 的启动脚本引用。用file模式更直观,方便你随时查看当前任务真正会用到的规则清单。
3.4 和代码检索类工具搭配的使用心得
实际使用中我发现,PromptSlim 和代码检索工具是天然搭档。Codex 经常会通过 grep 之类的方式自己找代码位置,这个过程也会产生大量工具调用和 Token 消耗。如果在规则裁剪的同时,把代码检索的路径范围也一并收窄,效果会更好。
PromptSlim 目前不支持直接控制检索范围,但可以在always_keep里加入“本次改动范围限定在 src/modules/order 目录”这样的临时规则,这样 Codex 在检索时会更集中,不会动不动就全仓库扫描。我试过几次,配合下来整轮任务的 Token 消耗还能再降 15% 左右。
4. 两个项目组合起来:我现在的完整工作流
4.1 会话启动前:规则裁剪先行
两个项目并不是互相替代的关系,它们管的是不同的上下文阶段。我现在每次开始一个 Codex 任务前,会先用 PromptSlim 生成当前任务的精简规则集,方式是在启动命令前加一层:
promptslim run --task "修复 order 模块的库存扣减逻辑" codexPrompSlim 会读取任务描述,裁剪规则,然后启动 Codex,同时让 Codex 只加载裁剪后的规则文件。这一步等于把“每轮固定开销”先压到最低。
4.2 会话进行中:历史压缩兜底
任务跑起来之后,就轮到 Context Compactor 接管了。它会实时监听会话长度,超过 12 轮就开始压缩早期历史。这种“规则瘦身 + 历史压缩”的组合,一个管入口,一个管过程,配合起来非常顺手。
我举一个实际的例子。上周我让 Codex 把一个旧版支付模块的接口从 HTTP 迁移到异步消息队列。任务涉及 12 个文件,前后跑了 41 轮。在同时启用两个项目的情况下,总 Token 消耗约 11.2 万。而我之前用裸 Codex 跑过类似规模的任务,基本都在 22 万 Token 以上。钱省了一半,代码质量没有明显差别。
4.3 会话结束后:账单复盘与规则微调
很多人忽略的是,会话结束后的复盘也是省钱的一环。我会定期跑一下两个项目自带的统计命令:
codex-compactor stats --session-latest promptslim report --project .Compactor 的 stats 会显示本次会话压缩掉了多少 Token、摘要模型花了多少 Token、净节省多少。PromptSlim 的 report 会展示哪几条规则被高频加载、哪几条从未命中。根据这个报告,我可以把那些一直没用的规则从 AGENTS.md 里直接删掉,从源头减少大小。这算是个正向循环:用得越久,规则文件越精炼,后续任务越省钱。
5. 避坑指南与常见问题排查记录
5.1 模型不支持与模型选择类报错的处理思路
用 Codex 配合第三方模型时,报错的概率会高一些。最常见的一种是告诉你某个模型在当前配置下不支持,特别是一些偏门模型。我的排查顺序是:先确认模型名称写对了没有,再确认这个模型是否兼容 Codex 的 API 格式。不要一上来就怀疑别人项目有 bug。
另外,两个工具都提供了--model参数来指定压缩和摘要所用的模型。如果默认模型不稳定,换成别的轻量模型即可。我用 Context Compactor 时试过用gpt-4o-mini和gemini-2.0-flash做摘要,两者质量差不多,但要是你本身就在用第三方模型,建议“谁的便宜用谁”。
5.2 登录认证失败、Token 失效类报错的排查顺序
围绕 Codex 的登录和 Token 失效报错,基本可以归为三类。第一类是登录时直接失败,提示 token exchange failed,这类大概率是网络环境不稳定或者没有走到官方认证端点导致的,需要先检查终端能不能正常发起请求到官方认证服务。第二类是提示your access token could not be refreshed,这类通常是因为登录态过期太久,重新执行登录流程基本都能解决。第三类是地区限制类的 403,比如报错里带了country字样,这是服务端基于访问来源做的限制,遇到这种情况只能对照官方支持的范围来规划使用方式,这不是任何客户端工具能修复的问题。
5.3 两个工具自身的问题与应对
Context Compactor 比较常见的问题是它压缩完一轮历史后,如果 Codex 后续发现信息不够,想回去看原始内容,就会“翻车”。现在项目给的解法是在会话目录里保留一份压缩前的原始备份,一旦 Codex 需要回溯,可以通过手动指令让它查看备份文件。实际用到这个功能的概率不高,但知道有后路心里还是踏实不少。
PromptSlim 的问题更多出在semantic模式的首次运行上——需要下载嵌入模型,如果网络不好会卡住很久。第一次用的时候我差点以为它死机了。建议首次使用前先手动执行一次模型预下载,后面就会顺畅得多。
5.4 排查问题速查表
| 症状 | 可能原因 | 优先处理办法 |
|---|---|---|
| 对话超过 10 轮后 Token 急剧上升 | 历史压缩未生效 | 检查 Compactor 是否已启动,确认监测端口是否被占用 |
| 规则文件裁剪后 Codex 行为异常 | always_keep缺失安全底线规则 | 把安全相关规则手动加入always_keep |
| semantic 模式启动很慢 | 嵌入模型未预下载 | 手动执行模型下载后再启动 |
| 第三方模型频繁报格式错误 | 模型本身不兼容 | 换 OpenAI 官方模型验证是否是模型兼容性问题 |
| 摘要后 Codex 丢失关键参数信息 | 摘要模型能力不足 | 调高摘要模型规格,或者调大max_summary_tokens |
6. 写在最后:工具的边界与个人体会
这两个项目能省 Token,靠的是一个朴素的道理:把上下文里“该省的省掉,该留的留下”。但工具毕竟是工具,能不能省到位,最后还是看你怎么用。
我自己的体会是,省 Token 的关键不只是靠压缩和裁剪,更重要的是控制任务的粒度。一个超大任务拆成几个小任务分开跑,让每次会话的轮数短一点、目标集中一点,比任何工具都省。两个开源项目解决的是“不得不长会话”时的开销问题,但我现在会刻意避免让 Codex 陷入动辄三四十轮的马拉松任务——拆细之后,配合这两个工具,我的整体开销比最初下降了差不多六成。
对了,还有一个我认为很实用的小技巧:每次任务结束时,花 10 秒钟在 PromptSlim 的 report 里看一眼哪些规则是“僵尸规则”,顺手删掉。这个习惯坚持一个月,你的规则文件会变得非常精炼,Codex 的响应速度也会快不少。规则越少,模型越容易抓住重点,这比单纯省 Token 的收益更大。