最大化 Claude Code 会话价值:让每个 Token 都物有所值
核心要点
- 在任务之间运行
/clear:防止将先前无关的上下文信息发送回模型,减少 Token 使用。 - 开始之前先设定好模型和工作量级别:对话过程中更改其中任何一项都可能导致提示缓存失效,增加 Token 成本。
- 使用
@提及文件而不是手动命名文件:文件会直接附加到消息中,节省一次读取调用,也避免 Claude 搜索文件。 - 为会产生大量噪音的命令添加静默标志,或在子代理中运行它们:命令输出会像文件一样追加到对话中,并在会话剩余时间内一直保留。
- 在新会话中先运行一次
/context:查看已加载的内容(MCP 工具定义、CLAUDE.md等),删除任何不必要的内容。 - 离开键盘前运行
/compact:提示缓存约一小时后过期,趁缓存有效时压缩对话能节省大量资源。
为什么要关注 Token 的高效使用
过去,你用来编写代码的工具按固定费用(甚至免费)计价。无论你当天下午修改一个测试用例还是五十个,编辑器的费用都一样,单个任务本身并没有独立的价格。
但使用 Claude Code 这样的智能编程工具后,情况发生了变化:同一项任务的完成费用,会因使用方式的不同而有所差异。
在一个会话中,Claude 读取测试及其对应的文件,进行编辑,几个循环就完成了。在另一个会话中,它先在代码仓库中搜索,读取十几个文件才找到相同的两个文件,而且每次循环都会把从早上到现在读取的所有其他内容都带进来。

虽然修复方法相同,但你花费的 Token 数量不同;而且在修复过程中,模型还不得不考虑它并不需要的十几个文件。
高效使用 Token 并不意味着减少 Token 总数,而是确保你使用的 Token 都用于你实际请求的事项。
接下来,我们来了解决定 Token 价格的因素、决定会话发送 Token 数量的因素,以及它们如何影响你运行会话的方式。
Token 的价格由什么决定?
费用按 Token 计算,但你实际支付的是推理成本:即 GPU(或 TPU,或模型运行的任何设备)在你的 Token 上运行模型所需的时间。
决定 Token 处理时间长短的因素有三点:你运行的是哪个模型、它是输入 Token(进入)还是输出 Token(输出),以及它是否被缓存。
1. 模型
更大的模型需要处理更多的输入和输出 Token。哪种模型适合哪种类型的工作,这本身就是一个值得深入探讨的话题。
对于本文而言,你只需要知道:接下来我们要讨论的所有内容都会乘以模型的价格。当问题确实困难或模糊时,使用更大的模型;当工作比较常规时,使用更小的模型。

2. 输入输出 Token
请求通过 GPU 分为两个阶段,两个阶段的成本不同。
首先,在预填充阶段,模型会读取你的请求和上下文:系统提示符、CLAUDE.md、你的消息,以及此后添加到对话中的所有内容(Claude 读取的文件和运行的命令输出)。这些就是输入 Token。
然后,在解码阶段,模型会写入输出 Token:包括思考过程、工具调用以及你看到的文本。这个过程一次处理一个 Token;一个包含 200 个 Token 的响应,相当于模型连续运行 200 次。每个 Token 的解码都会让 GPU 占用更长时间,这就是输出价格约为输入价格 5 倍的原因。

会话中大量的输出 Token 都是思考 Token,模型每回合的思考量取决于努力程度。与模型一样,你选择的努力程度也会通过 /effort 成为后续会话的默认值。
提示:在新会话中运行一次/model和/effort,看看你实际使用的是哪个选项。两者都会记住你上次的选择,所以需要慎重考虑后再做决定。
提示:如果你已经知道某个环节会很枯燥乏味,可以设置MAX_THINKING_TOKENS=0,让 Claude 在该环节关闭思考功能(Opus 5 除外),这比/effort的低努力程度还要低一档。
3. 提示缓存
如果一个请求的开头与服务器刚刚收到的请求完全相同,那么这两个请求共享相同的开头状态,服务器可以保留上次的状态,只预填充其后的内容。这就是提示缓存。
从缓存读取数据的成本是输入价格的 0.1 倍,因为服务器加载状态而不是重新计算。将 Token 写入缓存的成本略高于正常输入,最高可达 2 倍,因为服务器之后还需要维护状态。但每个 Token 只需写入一次,之后每回合都会进行一次 0.1 倍的读取操作。
Claude Code 会在每次请求时自动管理提示缓存,无需手动启用任何设置。但缓存也可能被破坏,因此了解如何避免这些开销峰值非常重要。
假设我们输入「修复失败的测试 utils.test.ts」,以下是 Claude Code 的处理过程:
- Claude Code 根据系统提示符(包括工具定义)、
CLAUDE.md和消息组装第一个请求并发送(输入 Token)。由于缓存中尚无任何内容,所有内容都会预先填充并写入缓存。 - 模型无法修复它未见过的测试,因此它会思考片刻,然后响应一个读取
utils.test.ts的请求(输出 Token)。Claude Code 读取文件,将其追加到对话中,然后再次发送整个对话(输入 Token)。这次,请求 1 中的所有内容都以十分之一的价格从缓存读取,唯一以全价预填充的内容是新增的读取请求和文件。 - 现在模型掌握了待测文件(输出)。再次读取、再次追加,一切重新开始:第 1 和第 2 步的内容从缓存读取,第二个文件以全价获取(输入)。
- 模型返回一个 Edit 对象(输出)。Claude Code 应用该 Edit 对象,追加结果,然后再次发送所有内容。情况依旧:Edit 对象及其结果是新的,之前的所有内容都从缓存读取(输入)。
- 模型运行
npm test(输出)。Claude Code 将测试输出追加到对话中,然后再次发送所有内容,其中测试输出是唯一新增的部分(输入)。 - 测试通过,模型返回简短的摘要(输出)。由于没有工具调用,无需追加任何内容,也没有第 7 次请求,任务完成。
为了解决一个小问题,我们发起了六次请求,而且每次请求都包含了之前的所有对话内容。典型的回合是不对等的:投入数万个 Token,而产出只有几百个。但只有该回合新增的 Token 才会以全价预填充。
这就是每回合的全部费用:历史记录的缓存读取费用、所有新内容的完整输入价格,以及响应的输出价格。
订阅用户同样适用此规则。你不会直接看到这些价格,但正是这些请求在消耗你的额度。
缓存必须从请求开始就保持匹配,而且请求总是按相同的顺序发出:工具定义,然后是系统提示,然后是对话(CLAUDE.md 在对话开头)。
如果前缀中的任何内容发生变化,其后的所有内容都会重新预填充。将工具结果追加到会话末尾是理想情况,因为其后没有任何内容。任何更改更靠前的请求或更改缓存键的内容都会导致缓存失效:
/model:每个模型都有自己的缓存,因此在下一回合,整个对话都会以全价重新预填充(这包括 Opus Plan,它会在你每次进入或退出计划模式时切换模型)。/effort:努力程度也是缓存的依据之一,情况相同。这就是为什么在对话过程中切换/model或/effort都会要求你确认。- 快速模式:这也是缓存键之一,重新预填充需要支付快速模式的费用。如果要启用它,请在启动时就启用(从缓存角度看,再次关闭它是免费的)。
/compact:对话会被替换成一个更短的版本,其中没有任何内容与原对话匹配(系统提示信息会保留)。只要旧对话还在缓存中,编写摘要本身的成本就很低,所以在长时间休息之前压缩,要比休息之后压缩便宜得多。- 时间:每回合都会重置计时,但订阅用户的缓存会在一小时后过期,API 密钥用户的缓存会在五分钟后过期(
ENABLE_PROMPT_CACHING_1H=1可设置为一小时)。如果超过这个时间再回来,下一回合会重新预填充整个对话。恢复之前的会话几乎总是如此:缓存通常已经过期,而且系统提示会在启动时重新构建。
这并不意味着你永远不应切换模式或努力程度,而是说有些时机可以轻松把握,比如在一段对话开始或结束前 /clear;而有些时机则需要付出更高的代价,比如在长时间的谈话过程中切换。
提示:如果最后几回合的对话走向不是你想要的方向,与其直接回溯,不如使用/rewind回到/compact之前。回溯只会删除这些回合,之前的对话仍然被缓存,不会消耗任何资源。而压缩会重写整个对话,总是会消耗资源。
决定会话发送 Token 数量的因素
这里需要了解的关键一点是:任何信息都不会只发送一次。对话中出现的所有内容,无论是 Claude 读取的文件还是运行的命令输出,都会在之后的每一次对话中再次发送,直到会话结束。
由于数据已被缓存,每次重新发送的成本很低,但低成本不等于零,而且在模型每次都必须考虑的上下文中,它也会占用空间。
这就是会话的完整成本模型:有多少 Token 最终进入上下文、它们在那里停留多少回合,以及你同时运行多少个上下文。
1. 最终进入上下文的是什么?
上下文的一部分在你输入任何内容之前就已经存在:工具定义、系统提示符、CLAUDE.md,以及启动时加载的其他任何内容。
提示:在输入任何内容之前,先在新会话中运行/context,查看其中的内容。CLAUDE.md应严格限于具体指令,并将工作流程相关的指令移至技能中,技能仅在需要时加载。如果此会话中不需要某个 MCP 服务器,请使用/mcp将其关闭。
会话期间添加的几乎所有其他内容都是工具结果:Claude 读取的文件以及它运行的命令输出。
Claude 读取的信息量主要取决于它需要自行推断的信息量。如果你说「测试失败了」,它首先需要找出是哪些测试失败了:运行一两个 grep 命令,打开几个文件查看哪个测试相关,而所有这些结果即使在不再有用之后也会长期保留在上下文中。
「修复失败的测试 utils.test.ts」会跳过搜索,并且只需要一次读取调用;而「修复失败的测试 @utils.test.ts」则连读取调用都不需要。

提示:引用文件时,请使用@提及,而不是输入文件路径。Claude Code 会在发送任何内容之前将文件附加到消息中,因此它会出现在第一个请求中,无需调用读取函数。无论哪种方式,文件在上下文中的位置都相同,因此每个对话只需提及一次:它会一直保留在那里,后续再次使用@提及通常会附加第二个副本。
构成上下文的另一个重要因素是 Claude 运行的命令输出。每次运行测试、构建或生成 Git 日志时,输出的内容都会像读取的文件一样追加到对话中,并保留相同的回合数。
实际上,输出量非常大也没关系:超过 30,000 个字符后,Claude Code 会将输出写入文件,并且只在对话中添加一个简短的预览和路径(可通过 BASH_MAX_OUTPUT_LENGTH 调整)。
问题在于低于这个限制的所有内容。例如,一个测试运行器每个通过的测试都打印一行,共打印 400 行,虽然没有超过限制,但这 400 行测试结果现在会占用之后每一轮的运行资源。
Claude 通常会使用标志和 tail 命令来处理这些操作。如果你不想让 Claude 来做,文档中有一个小钩子,可以在运行冗长的命令之前对其进行重写,这样就只会返回重要的行。
提示:把你每天运行的两三个命令(包括静默标志)按你平时输入的习惯写进CLAUDE.md(例如「运行单个测试文件:npx vitest run <file> --reporter=dot」)。这只是一个小小的改进,但可以节省一次操作,并在之后的每个会话中减少几百行输出。
2. 它在上下文里停留多久?
一次长时间的会话比将同样的工作分散到几次短会话中花费更多,而且多出的成本超乎想象,因为第 40 回合也需要重新阅读之前的 39 回合。你希望会话中的上下文简洁明了且切题,因此不要把上一个任务的上下文带入下一个任务:开始新任务时使用 /clear,完成同一任务的前序部分后使用 /compact。

提示:如果之后还想恢复会话,请在操作前先/rename。执行/clear或/compact时,要指定需要保留的内容;如果每次都保留相同内容,可以在CLAUDE.md中添加「压缩说明」部分。如果你使用的是 1M 模型,并希望恢复自动压缩安全网,可以将其设置为/autocompact 200k(需要 Claude Code v2.1.221 或更高版本)。
还要注意在你没有输入文字时发生的对话回合。/loop 每次执行操作都会算作一个完整回合,并把该会话中的所有对话都带入其中。如果距离上次回合已经超过一个小时,就会发生缓存未命中。建议在另一个终端启动一个新会话,并从那里运行循环。
3. 子代理:把噪音隔离在主上下文之外
另一种将某些内容排除在主上下文之外的方法是让它在另一个上下文中发生,这正是子代理的作用。子代理拥有自己的上下文窗口,包括系统提示符、工具以及 CLAUDE.md,但不包括主会话的对话。它运行自己的回合,唯一返回给主会话的是它的答案,其他所有内容在完成后都会被丢弃。
不进行对话的缺点是,子代理有时不得不重新阅读主代理已经读过的内容,而这同时也会消耗它自己的回合费用。对于小任务来说,这反而是额外开销。
当一项任务会产生大量你不需要保留的输出时(比如查看日志),使用子代理就很有意义。Claude 通常会主动这样做;如果它没有主动建议,你可以直接要求它(「在子代理中查看此日志」)。请记住,主会话只会收到子代理选择报告的内容。

提示:如果你要反复处理某个会产生大量噪音的任务,请为其创建一个单独的子代理定义,模型设置为 haiku(或 sonnet)。否则,它会在主会话运行的任何子代理上执行。
应该从哪里开始优化?
以上所有事项中,有四项值得重点关注,按成本高低大致排序如下:

更多推荐


所有评论(0)