Claude Code :如何最大化你的 Session 价值
前两天,Claude Code 官方发了一篇文章,专门讲怎么提高 Session 的使用效率,把每个 token 都花在刀刃上。
这算是 Claude 少见的偏技术实操型文章。我看的时候直接肾上腺素飙升了,因为里面这些 tips,确实太有用了。

来看看 Claude Code 到底讲了啥。
太长不看
如果你嫌下面太长,可以先直接看结论:
- 不同任务之间使用
/clear,不要让上一个任务遗留下来的无关上下文,随着新任务一起发给模型。 - 开始任务前先用
/model和/effort确认模型与思考强度。在对话中途切换,可能会破坏 Prompt cache,增加 token 消耗。 - 引用文件时使用
@-mention,不要只告诉 Claude 一个文件名,让他自己去读,直接使用 @ 的方式能省掉一次 Read 调用。 - 测试、构建、日志这类命令尽量加 quiet 参数,或者让 Subagent 来处理。命令输出和文件内容一样,进入对话后会一直留到 Session 结束。
- 新开一个 Session 后运行一次
/context,看看还没开始时,CLAUDE.md、MCP 工具定义等内容已经占了多少上下文。 - 如果你要离开键盘的话,先执行
/compact。Prompt cache 会在一个小时后过期,趁缓存还在的时候压缩上下文,成本会低很多。
(讲真,我看完这些 tips 之后,感觉非常受用,必须好好跟大家讲讲了。)
最大化 token 价值
以前写代码时,使用代码编辑器通常是买断、订阅或者干脆用免费的。你下午改一个 bug 还是改五十个 bug,编辑器并不会给每个任务单独计价。这说的一般是我们古法编程期间的编辑器。
但是到了 Claude Code 这类 Agentic Coding 工具,这件事变了。同一个任务,用法不同,消耗的 token 可能会差很多。
(下面给出了两个不同的 Session,来验证不同使用方式下请求和 token 消耗的对比)
在一个 Session 里,Claude 直接读取测试文件和对应的源代码,改完之后运行测试,只需要几轮就结束了。
换一个 Session,Claude 会先在仓库里搜索,为了找到相同的两个文件,顺手读了十几个不相关的文件。而且这些文件一旦进入了上下文中,后面的每一轮都会带着这些文件。
最后的修复结果可能一致,但两次任务消耗的 token 数量差得很多。
所以,提高 token 效率不等于一味的少用 token。而是确保让花出去的每一个 token 都能起到实际作用。
要理解这件事,得先搞清楚两个问题:一个是 token 为什么有贵的有便宜的,另外一个是 Session 为什么会越来越重。
token 的价格由什么决定
Claude 按 token 来计费,你实际需要付钱的是 token 背后的推理费用,即 GPU、TPU 或其他设备,需要花多长时间来处理你的这些 token。
一个 token 到底有多贵,主要有三个因素影响:用了什么模型、它是输入还是输出,以及它有没有命中缓存。
模型
模型的参数量越大,处理输入和生成输出时需要的计算量就越多。
哪种任务该用哪种模型,这本身就值得单独拿出来一篇来讲讲了。Claude Code 也发过一篇相关内容(那篇我还没读,看大家划线的情况吧,如果有人感兴趣,我再单独出一篇)。
这篇文章里只需要记住一点就行了:后面讲到的所有 token 消耗,最后都要乘以模型本身的价格。
遇到困难问题、模糊,需要做复杂判断时,再上更大的模型。
只是改变量名、跑测试、做一些重复性工作,用小一点的模型就够了。
下面两幅图,左侧是简单任务下,不同模型和 effort 程度下的对比;右侧是复杂任务下,不同模型和不同 effort 的程度对比。

输入和输出 token
一次模型请求会经过两个阶段,它们的成本不一样。
第一个阶段叫 prefill,也就是预填充。模型会先读取这次请求里的全部内容,包括 system prompt、CLAUDE.md、你刚刚发送的消息,以及此前已经进入对话的文件和命令输出。这些都属于输入 token。
简单理解就是:Claude Code 每准备回答一次,或者决定下一步工具调用之前,都要先把当前上下文读一遍。
第二个阶段叫 decode。模型开始生成内容,包括内部 thinking、Read 或 Edit 之类的工具调用,以及最终展示给你的回答。这些都属于输出 token。
Decode 是逐个 token 生成的。一个包含 200 个 token 的响应,需要模型连续生成 200 次。这个过程会让 GPU 一直在工作,所以输出 token 的价格大约是输入 token 的 5 倍。
下面两幅图,上图是 prefill 阶段的输入 token 包含哪些东西,下图是 decode 阶段输出 token 包含哪些东西。
这里还有一个很容易忽略的地方:Session 里的输出 token,不只包含最后展示给你的那几句话,还有模型每一轮使用的 thinking token。
/effort 控制的就是 Claude 每一轮愿意投入多少思考。任务越复杂,越需要使用较高的 effort ;如果只是重复性的工作,就没必要让模型增加无关消耗,使用低一级 effort 即可。
新开 Session 后,可以先运行一次
/model和/effort,确认当前设置。它们会记住你上一次的选择,别一上来就直接拉满了。如果你已经确定这次 Session 全是重复、明确、不需要深入推理的工作,可以用 MAX_THINKING_TOKENS=0 启动 Claude Code,Claude 会把这一次 Session 的额外 thinking 预算关掉。这个档位比
/effort low还低,不过 Fable 5 除外。
Prompt cache
Prompt cache 的原理其实不复杂。
如果这一次请求的开头,与服务器刚刚处理过的请求完全一致,服务器就不必重新计算这部分内容,这就是 Prompt cache。它可以直接加载上一次保存的 state ,只对后面新增的内容执行 prefill 预填充。
缓存读取的价格,大约是普通输入 token 的 0.1 倍。第一次把 token 写进缓存会贵一点,最高可能达到普通输入价格的 2 倍,因为服务器除了计算,还得把 state 保存下来。
Claude Code 会自动管理 Prompt cache,不需要你手动开启。但问题是,缓存虽然会自动管理,但也有可能会造成人为主动的破坏。
文章用 utils.test.ts 举了一个具体的例子。
假设我们输入:
Fix the failing test in utils.test.ts
整个过程是这样的:
▲ 一个小修复背后的五次模型请求:历史上下文走缓存,新内容逐轮追加。
- Claude Code 先把工具定义、system prompt、
CLAUDE.md和你发送的消息组装成第一个请求。这时缓存还是空的,所以所有输入都要完整 prefill,并写入缓存。 - 模型还没看过需要测试的文件,所以没法直接改。它会先想一会儿然后生成一个 Read 工具调用。工具调用属于输出 token。Claude Code 读取文件,把 Read 调用和文件内容追加到对话末尾,然后发起第二次请求(这些都是输入缓存)。这一次,上一步骤已有的内容会以 0.1 倍计价,唯一需要正价 token 的是 prefill 的新内容:Read 工具调用和文件。
- 模型看完测试文件后,还需要读取测试对应的源代码文件,于是又生成一次 Read 调用。Claude Code 读取第二个文件,继续追加到对话末尾,再发起第三次请求。前两轮出现过的内容继续走缓存,第二个文件是新内容,需要正常 prefill。
- 两个文件都看完后,模型生成 Edit 工具调用。Claude Code 执行修改,把 Edit 和修改结果追加到对话里,再发起第四次请求。之前已有的内容继续走缓存,本轮新增内容按正常输入价格处理。
- 接着模型生成工具调用,运行
npm test。Claude Code 把测试输出追加到对话中,发起第五次请求。此时只有新的测试结果需要正常 prefill。 - 测试通过,模型生成一段简短总结。因为这次没有新的工具调用,也就没有新的工具结果需要追加,所以不会再产生第六次请求,任务到这里就结束了。
也就是说,上面只是一个看起来很简单的 prompt ,背后却实际发出了五次模型请求。每一次请求都会带上到当前为止的完整对话。
依此可见,每一轮请求其实及其不对称:输入可能有几万个 token,输出只有几百个 token。
每一轮的 token 成本大概由三部分组成:
- 历史上下文:按缓存读取价格计算。
- 本轮新增内容:按正常输入 token 价格计算。
- 模型生成的 thinking、工具调用和回答:按输出 token 价格计算。
即使你使用的是订阅套餐,这套机制也一样存在。你可能看不到每一笔 token 的具体价格,但这些请求会实打实地消耗你使用的额度。
Prompt cache 必须从请求开头开始匹配。Claude Code 发送请求时,前面通常依次是工具定义、system prompt,然后才是以 CLAUDE.md 开头的对话内容。如果靠前的部分发生变化,后面的内容都要重新 prefill。工具结果追加在末尾是最理想的一种情况,因为它不会改变前面的任何东西。
真正容易让缓存失效的,是下面这些操作:
/model:每个模型都有它自己的缓存,所以在长对话中途换模型,下一轮可能需要按正常价格重新 prefill 整个会话。/effort:effort level 也是缓存 key 的一部分。中途修改 effort,效果和切换模型类似。- Fast mode:是否开启 Fast mode 同样属于缓存匹配条件。中途开启 Fast mode 后,旧缓存无法继续匹配,整个会话会按照 Fast mode 的价格重新 prefill。所以要用就尽量从 Session 一开始用。
/compact:上下文压缩,只有前面的 system prompt 能继续保留,上下文都被压缩了。不过,只要旧对话还在缓存里,上下文压缩成本开销不大。所以准备离开很久时,最好先 compact,再离开。- Time:每一轮都会重新计算缓存时间。订阅模式下,缓存通常一小时后过期;API key 默认是五分钟,设置了
ENABLE_PROMPT_CACHING_1H=1参数可以延长到一小时。如果超过了这个时间,下一轮往往需要重新 prefill 整个对话。恢复旧 Session 通常也一样,因为缓存大概率已经过期,system prompt 在启动时也会重新构建。
这不代表 model、effort 或 Fast mode 永远不能切换。开销最小的切换时机是 Session 刚开始的时候,或者执行 /clear 之后。开销最大的时候,是在一段已经很长的对话中间突然切换。。。
如果最近几轮已经走跑偏了,但又不想保留的话,可以用
/rewind回到走跑偏之前。它只会从末尾剪掉几轮对话,前面的内容依然走匹配缓存。相比之下,
/compact会重写整个对话,必然产生新的成本。
一个 Session 到底会发送多少 token
Claude 读取过的文件、运行命令得到的输出,都会在后续每一轮中再次发送,直到 Session 结束。
它们大部分能命中缓存,所以重复发送比较便宜。但便宜不等于免费。更重要的是,这些内容还会一直占着上下文窗口,模型每一轮思考都得把它们加进去。
说到底,一个 Session 的成本模型是这样考量的:到底有多少 token 进入了上下文,它们在里面待了多少轮会话,以及你同时运行了多少个上下文。
什么样内容会进入上下文中
在你输入第一句话之前,上下文里其实已经有东西了:工具定义、system prompt、CLAUDE.md,以及启动时加载的其他内容。
可以在一个新开的 Session 中运行
/context,看看还没开始工作时,上下文里面已经有啥了。CLAUDE.md尽量只保留具体、长期有效的规则。某个工作流才需要的说明,可以用 Skill ,用到时再加载。如果当前任务根本用不到某个 MCP server,就通过/mcp把它关掉。
Session 开始之后,最容易塞满上下文的基本都是工具调用结果:Claude 读取的文件,以及它运行命令产生的输出。
Claude 到底会读多少文件,取决于它自行决定要解决多少问题。
如果你只说一句“测试失败了”,它首先得找出哪个测试失败。它可能先跑一两个 grep,再打开几个文件判断哪个具有相关性。
如果你直接说:
Fix the failing test in utils.test.ts
Claude 这时会跳过搜索,直接 Read 这个文件。
如果你再进一步写成:
Fix the failing test in @utils.test.ts
文件会在第一条消息发出前直接附加进去,连 Read 调用都省了。
(这个细节我之前真是没想到,使用 @-mention 的方式,能省 Read 调用)
使用
@-mention和让 Claude 自己 Read,文件本身占用的上下文空间其实一样。区别在于@-mention能省掉搜索和工具调用。同一个文件在一段对话里提一次就够了,因为它会一直留在上下文中。后面再次@,通常会把同一个文件再附加一份。
另外一个上下文开销大户,是命令输出。
Claude 每次运行测试、构建或者 git log,终端打印出来的内容都会像文件一样被追加到对话中,并在后面的每一轮继续存在。
超过 30,000 个字符后,Claude Code 会把完整输出写进文件,只在对话里保留一小段预览和文件路径。这个阈值可以通过 BASH_MAX_OUTPUT_LENGTH 来修改。
比较麻烦的是那些没超过 30,000 个字符,但又臭又长的输出。
比如测试工具会把 400 个通过的测试逐行打印出来,因为它没有超过限制,所以这 400 行会原封不动进入上下文中,然后陪着你跑完整个 Session。。。
Claude 通常会自己使用 quiet flag 或 tail 来控制输出。如果你不想把控制权交给它们,官方文档里也有一个 Hook,可以在命令运行前自动重写,只留下真正重要的内容。
可以把每天最常用的两三个命令直接写进
CLAUDE.md,连 quiet flag 一起写清楚。比如:“使用npx vitest run <file> --reporter=dot运行单个测试文件。”看起来只是一个小改动,但它能在后面的每个 Session 里都少跑一轮,还能少塞几百行输出。
同样一份任务,塞进一个很长的 Session,通常会比拆成几个新的 Session 开销更大。因为第 40 轮不只处理你这一轮的对话,它还要重新读取前面的 39 轮的上下文缓存。
所以,尽量让当前 Session 里的上下文保持简短且具有主线相关性。新任务开始时使用 /clear;同一个复杂任务进入新阶段,前面的过程已经没那么重要时,再使用 /compact。
下面这幅图上面的是使用 /clear 之后每个任务的开销,下方这幅图是一个长 Session 的开销。
如果以后还想找回当前 Session,可以在
/clear前先运行/rename。使用/compact时,最好明确告诉 Claude 哪些内容必须保留;如果每次保留的内容都一样,也可以在CLAUDE.md里增加一个 “Compact instructions” 部分。使用 1M context model 时,如果想把自动压缩阈值恢复到过去的 200k,可以运行/autocompact 200k,但需要 Claude Code v2.1.221 或更高版本。
你还需要注意一下没有输入时的轮次。
比如 /loop。每次循环都会在当前 Session 中执行完整的一轮,并携带整个对话。如果距离上一轮已经超过一小时,还会碰上 cache miss,重新 prefill 整个上下文。
所以,需要长期运行的 loop,最好在另一个终端里新开一个干净 Session。
Subagent
还有一种减少上下文污染的办法:把任务放进 Subagent 中。
Subagent 有独立的 context window,也有自己的 system prompt、工具和 CLAUDE.md,但它拿不到主 Session 的完整对话。它会在自己的上下文里完成任务,最后只把答案交回主 Session。中间读取的文件、执行命令产生的日志,以及它自己的思考过程,任务结束后都会被丢掉。
这听起来很美好,对吧?问题是,Subagent 看不到主对话,有时不得不重新读取主 Session 已经读过的文件,而且它自己的每一轮同样要消耗 token。
所以,小任务扔给 Subagent,可能只是平白增加额外开销。
比较适合 Subagent 的场景是那些会产生大量的过程信息,但主 Session 只需要最终结论的任务,比如分析一份很长的日志。
你可以直接告诉 Claude:
在 Subagent 中分析这份日志,只返回错误原因和相关的行数即可。
这样几千行日志只会留在 Subagent 的上下文里,主 Session 最后拿到的可能只有几句话。
如果有一类高噪声任务需要反复交给 Subagent,可以给它单独创建一个 Subagent ,然后把模型指定为
haiku或sonnet。否则,它默认会使用主 Session 当前使用的模型。
所以上面讲了这么多,罗里吧嗦一大堆,真正值得优先检查的只有四件事情,按照成本从高到低排列:
- Session 太长。 每一轮都会重新发送此前的所有内容,这是大部分 token 最容易花掉的地方。
- 上下文里塞了太多东西。 无关文件、无效的命令输出、上一个任务留下来的内容,以及根本没用到的 MCP server,都是噪音。
- 模型或 effort 超过任务需要。 模型越贵、它的 thinking token 开销越大,主打一个杀鸡焉用宰牛刀?
- 中途破坏 Prompt cache。 长对话里切换模型、effort 或 Fast mode,或者等缓存过期后再回来,可能让整个会话重新 prefill,失去原本的 0.1 倍的缓存费用。
这篇文章我看完之后最大的感受是,Claude Code 的 token 优化,还真的藏在细节里面。
如果只记住一句话,我觉得应该是:一个 Session 只做一件事,而且只留下完成这件事真正需要的上下文。
更多推荐


所有评论(0)