最近我在终端里用 Codex 干了几周活,功能是真能打,但月底看 token 消耗的时候,说实话有点心疼。同样是改 bug、补测试、重构代码,有些人能把一个上午的活压在三五万 token 内,有些人的会话随便一滚就是十几万。差别不在模型能力,而在使用 Codex 的方式。
这篇攻略就是我实际踩出来的十个省 token 技巧,全部围绕 Codex 这类终端 AI 编程助手展开。适合每天跟 Codex 打交道、对 API 账单敏感、或者想把上下文机制摸透的人。不需要牺牲功能,也不需要改变代码质量,只要改掉几个使用习惯,消耗就能肉眼可见地降下来。
1. 省 token 先搞明白:Codex 的 token 到底烧在哪儿
很多人的省钱思路是“尽量少提问”,这其实没抓住重点。Codex 的消耗大头从来不是你那几句话,而是它每次请求时,把整个会话历史重新发给模型的那一部分。
1.1 token 消耗的四个主要来源
第一个是输入侧。Codex 每次向你发问或执行任务,都要把当前会话里积攒的全部消息——包括你最初的需求、它之前的回答、中途插入的代码片段、系统提示、工具调用结果——打包成一次请求。会话越往后,这个包裹越大。这是消耗的主力。
第二个是输出侧。模型生成给你的回复、代码 diff、解释说明,都算输出 token。输出虽然单价往往更高,但总量通常比输入少,所以很多时候不是大头,可架不住模型啰嗦。
第三个是缓存价差。服务端会对“开头连续相同的 prompt 前缀”做缓存,命中缓存的部分价格会便宜不少。你用不用得上缓存,取决于会话习惯,这点后面专门讲。
第四个是重试循环。Codex 遇到报错时会在同一会话里反复尝试修复,每轮都背着前面所有失败历史重新来一遍,这是最隐蔽的浪费。
1.2 一个普通会话的账单估算
假设你开一个新会话,让它“帮我把登录模块重构一下”。第 1 轮输入大约 2k token,它输出 1.5k;第 2 轮你追加一句“用户表加个字段”,输入就变成 3.5k;到第 15 轮时,输入很可能已经膨胀到 20k 甚至更高。
把 15 轮输入全部加起来,在没有缓存命中、或者频繁清空重开的情况下,光输入侧就要烧掉 100k 上下的 token。这还只是一个小型重构任务。如果你一天开五六个这样的会话,账单自然下不来。
所以省 token 的核心只有一句话:让 Codex 每次请求都尽量轻装上路,别让无关历史、无关文件、无关工具混进上下文。
2. 会话与上下文管理:按住历史膨胀这只吞金兽
输入 token 会随会话轮数线性增长,这是 Codex 消耗的底层规律。你控制不了模型窗口多大,但能控制它往窗口里塞什么。
2.1 技巧一:给模型上下文设一个硬顶
很多人的第一反应是“我模型窗口大,随便它记”,这正是账单飙升的元凶。如果没有限制,Codex 会把对话历史一路喂到接近模型窗口上限,哪怕窗口是 128k,它就敢给你装满 128k。历史中间那些早已完成讨论的旧消息,每一轮都还在重复计费。
我建议在 Codex 的配置文件里手动设置上下文上限。配置文件通常在~/.codex/config.toml,找到或新增这几个字段:
model = "你的主力模型" model_max_prompt_tokens = 24000 model_max_output_tokens = 4000model_max_prompt_tokens是输入侧预算,model_max_output_tokens是单次回复上限。设完之后,Codex 会尽量把提示词压在这个范围内;历史超限时,它会丢弃或压缩更早的消息,而不是每轮硬扛全部内容。
这里有个度的问题:上限设太高等于没设,设太低又会让 Codex 丢掉刚讨论过的关键背景。我的经验是,先按“一个任务最多聊 20 轮、每轮新增约 1k 输入”来估算,上限设在 20k 到 32k 之间比较合适。输出上限不要低于 3000,否则生成大段代码时容易被截断,反而导致返工。
2.2 技巧二:一个会话只干一件事,任务结束就换血
这是我踩过最深的坑。以前我习惯在同一个会话里连续干活:先改导入逻辑,顺手问一个 Python 语法问题,再让它部署到测试环境。结果从第 8 轮开始,它每回答一个问题,都要背着“改导入、科普语法、部署命令”三坨历史,其中八成和当前问题无关,但每一轮都要重新读一遍。
把这三件事拆成三个会话,效果立刻不一样。功能开发一个会话、语法问答一个会话、部署操作一个会话,每个会话历史短而纯,token 消耗通常只有混用时的三分之一。
判断“一个任务”的边界也很简单:目标是否达成、改动是否提交、是否换了一个功能点。只要其中一项发生,就果断开新会话。
2.3 技巧三:旧任务及时 /clear,别拖着上一题做下一题
任务切换时,直接在会话里输入/clear,把对话历史清空,然后重新描述当前状态。很多人舍不得清,总觉得“之前聊过的东西丢了可惜”。
其实想清楚就明白了:Codex 并不靠聊天记忆保存你的代码,代码都在文件系统里。它靠的是每次重新读取当前文件内容。/clear丢掉的只是聊天记录,项目文件、改动结果、验证命令全部还在。
我现在的习惯是:每当 Codex 开始把很早的消息做摘要压缩时,就说明这个会话已经太长了。与其让它花 token 压缩旧历史,不如我手动/clear,把当前文件状态、目标、约束重新说一遍。新会话第一轮成本很低,而拖着旧会话跑,每一轮都在为已结束的任务买单。
3. 输入侧瘦身:少读、少看、少重复
上下文管理解决的是“历史越滚越大”的问题,但输入侧的浪费不止历史。你喂给它的项目规则、工具描述、提示词质量,都会直接影响单轮成本。
3.1 技巧四:把项目规则写进 AGENTS.md,别每次重复念
Codex 会自动读取项目根目录的AGENTS.md作为固定指令文件。这个机制非常省 token:你只需要在文件里写一次规则,它每轮都会自动带上,不需要你在对话里反复叮嘱。
我项目里的AGENTS.md长这样:
# 项目说明 这是一个命令行小工具,Python 3.11,使用 pytest。 # 代码风格 - 变量命名用 snake_case - 函数需要 docstring - 修改必须补充测试 # 对 Codex 的要求 - 回答用中文简述 - 只输出修改过的文件 diff,不要输出整个文件 - 改动前先说明要改哪几个文件一个 20 行的AGENTS.md大约 300 token,但它能给你省下每一轮都可能出现的“说中文”“只给 diff”“记得跑测试”这类重复指令。这些重复话在没规则时,你每次都要手敲,每次都要计入输入。
需要注意的是,AGENTS.md本身每一轮都会被读取,所以你千万别把它写成项目文档仓库。只写能影响 Codex 回答方式的高频规则,文档细节留给项目 README,否则省下来的 token 又会被这份文件吃掉。
3.2 技巧五:探索用小模型,落地用强模型
方向不明确的时候,让 Codex 排查问题、翻代码、定位逻辑,来回几次上下文就积累起来了,这一段恰恰是 token 消耗的大头。如果你用最强模型做这种探索性工作,差不多等于用跑车去小区门口取快递。
如果你的 Codex 版本支持--model之类的启动参数,可以分别配置两个启动方式:一个默认用小一号、便宜一截的模型做日常探索;等方向确认之后,再用主力模型处理具体实现和重构。我一般会准备两个别名,一个跑探索,一个跑落地。
这里有个容易忽略的点:探索和落地最好分成两个会话,不要在一个会话里中途切模型。因为切模型之后,如果厂商对会话记录的解析方式不同,或者上下文格式有变动,前面积累的缓存红利可能直接失效,得不偿失。
3.3 技巧六:提示词里明确“回答边界”
大多数人的提问方式都太开放了。你问“帮我优化下这段代码”,模型就会默认输出:问题分析、优化思路、备选方案、修改后的完整代码、测试建议。一套下来输出 token 轻松三四千。
有效提问是提前卡死边界:
- “不要解释设计思路,直接给出修改后的函数和一句测试命令。”
- “只需要 diff,不要完整文件。”
- “不要使用任何工具,只基于我贴的代码回答。”
- “如果需求与 src/cache.py 无关,不要读其他文件。”
把“你希望怎么回答”写清楚,比让它先自由发挥、你再让它删掉解释要省钱得多。前者是一轮解决,后者至少两轮到三轮,而且中间这些轮次的历史还会带进后续问答。
4. 工具与文件权限:把无关内容挡在上下文之外
Codex 的强大之处是能自己读文件、跑命令、调用工具。但这也意味着,如果配置不当,它会主动把大量无关内容搬进上下文。
4.1 技巧七:MCP 工具按需挂载,别全局堆满
MCP 服务器的作用是给 Codex 接入外部工具,但它有一个隐藏成本:每挂载一个服务器,它的工具描述、参数 schema 都会全部塞进每次请求的提示词里。哪怕你这个会话完全没用到某个工具,它的定义也已经在每轮计费了。
实测下来,挂一个工具大概会让单轮输入增加几百到两 k token,取决于工具描述多长。如果全局挂十个工具,就相当于每次请求额外多烧几 k token,一年下来是笔不小的数字。
建议一个项目最多挂两到三个真正会用到的 MCP 服务器,比如只挂数据库和内部 API 相关的,其他的全去掉。配置文件里只写当前项目需要的[mcp_servers]段,别把一整套工具都放在全局配置里。
4.2 技巧八:给 Codex 圈定可读清单,别让它随便翻仓库
Codex 自己探索文件系统的时候很随心所欲。你一旦说“帮我看看项目结构”,它可能就会递归列目录,把 README、package.json、一堆配置文件、甚至部分源码全部读进上下文。这些内容对当前任务往往毫无价值。
正确姿势是直接指定可读范围:“只读 src/cache.py 和 tests/test_cache.py 这两个文件。”它需要其他信息时会反问你要,你再开权限也不迟。把哪些文件该看、哪些不需要看写进初始提示词,单轮输入立刻能少一大截。
同时,建议配一个忽略清单文件,类似.gitignore的写法,把node_modules、dist、build、.git这类目录排除掉。这些目录对 Codex 推理没有价值,一旦被它读进去,几千几万行第三方代码瞬间变成上下文垃圾。真需要改构建产物时,再单独指定文件路径给它就行。
5. 缓存与失败止血:两个最容易忽略的省钱死角
聊完输入侧,再讲两个大多数人根本不知道、或者说知道但没想明白的省钱点。这两个点叠加起来,效果比前面所有技巧都明显。
5.1 技巧九:连续交互吃满前缀缓存,别随便开新会话
服务端对“开头连续相同的 prompt 前缀”会做缓存,命中缓存的那部分输入 token,价格比正常输入便宜得多。而 Codex 的对话历史天然就是一个前缀:只要会话开头保持一致,后面每轮新增的内容只占很小一部分成本。
换句话说,同一个任务里连续多轮交互,你越连贯,前面重复读的历史就越便宜。但很多人的直觉是反的:他们觉得历史越长越贵,于是每两轮就清一次上下文、开一个新会话。这么干的结果是每次都把开头重新写入,缓存完全命不中,每一轮都按全价输入计费,成本反而翻倍。
这里有三条实操原则:
- 同一个任务内部的多次迭代,尽量留在同一会话里连续做完,中间别长时间停顿。
- 任务目标变了再
/clear或开新会话,把缓存红利吃干净之后再换任务。 - 绝对不要回头修改对话中部的旧消息,哪怕只是改一个词。前缀缓存要求开头连续,中间的任何改动都会让前面的缓存失效,等于亲手烧掉折扣。
和技巧三配合起来就顺了:一个任务内部用连续会话,靠缓存压低增量成本;一个任务完结后马上清空,防止历史无限膨胀。
5.2 技巧十:失败会话立刻止血,不跟模型死磕
Codex 遇到报错时会自己重试,前两三次重试还属于正常排查,后面基本就是在猜。每次猜测都是一次完整的模型调用,而且每轮都带着此前所有失败理由和历史代码,输入量会以极快的速度膨胀。
我印象很深的一次,是一个 Shell 脚本引号转义问题。Codex 修到第 5 轮还没好,我又不好意思打断它,看它继续试,结果一直滚到第 12 轮。那 12 轮累计消耗了好几万 token,最后我自己看了一眼错误日志,发现只是脚本里一处变量忘加引号。重开一个会话、贴三行报错、一句话描述问题,它一次就给对了。
止损三原则:
- 连续两三轮没有实质进展,立刻停止。
/clear清掉失败历史,只贴真正的错误信号:退出码、报错 tail、相关行号。- 换一个更短的提示重问,并且明确说“如果方案不行就直接告诉我,不要反复猜”。
别心疼那几轮已经花掉的 token,前面损失是沉没成本。继续在失败会话里打转,每一轮都是新的大额支出,止损越早越省。
6. 十个技巧速查与实践建议
这十条技巧听完,可能一时记不住。我把它们整理成一张速查表,按优先级排序,你可以直接对着操作。
| 技巧 | 核心动作 | 优先级 |
|---|---|---|
| 设置上下文硬顶 | config.toml 里配model_max_prompt_tokens和model_max_output_tokens | 高 |
| 一个会话只干一件事 | 功能、问答、部署分开,目标达成即换会话 | 高 |
| 旧任务及时 /clear | 切换任务前清空历史,只保留当前状态描述 | 高 |
| 用 AGENTS.md 沉淀规则 | 把高频指令写进项目级文件,控制在 30 行内 | 中 |
| 探索用小模型落地用强模型 | 用启动参数区分模式,方向确认后再切主力模型 | 中 |
| 提示词里给回答边界 | 明确要 diff、不要解释、不要用工具、只读哪几个文件 | 中 |
| MCP 按需挂载 | 每个项目只留两三个真正用到的工具 | 高 |
| 圈定可读文件清单 | 指定文件路径,配合忽略清单排除构建产物目录 | 高 |
| 吃满前缀缓存 | 单任务内连续交互,别中途清空,别改早期消息 | 中 |
| 失败会话立刻止血 | 两三轮无进展就清空,只贴错误信号重新提问 | 高 |
我个人的体验是,刚上手不用十条全做。先挑速查表里标“高”的那几项——上下文硬顶、会话单一职责、MCP 按需挂载、可读清单——它们最立竿见影,几乎不用改变操作习惯,只是把话说得更精、把权限收得更紧。等这几条变成肌肉记忆之后,再补上缓存和失败止血这两个进阶项,省下来的量就很可观了。
最后分享一个我自己的变化:现在日均 token 消耗大概只有调整前的四成,而且代码质量没有下降。原因也挺简单——Codex 每次请求都吃得更少、看得更准、思考更聚焦,反而比从前那个什么都往上下文里塞的状态靠谱得多。省 token 这件事,本质上就是让 AI 助手学会轻装上路。