官方更新 Claude Blog 2026-08-15T00:01 正文 ok 2222 words

最大化 Claude Code 会话的价值

英文标题:Maximizing the value of your Claude Code sessions

Claude Code 的一次请求分为输入预填充(prefill)和输出解码(decode)两个阶段,输出 token 的单价约为输入 token 的 5 倍,思考 token 往往是会话输出成本的重要组成部分。提示缓存会让重复上下文以输入价格的 0.1 倍读取,但切换模型、effort、Fast mode、执行 `/compact` 或缓存过期,都可能导致整段上下文重新预填充。开发者应尽量缩短并保持会话上下文的相关性:明确指定文件、控制命令输出、在任务切换时清理会话,并将高噪声或高输出任务交给 subagent。对于订阅用户而言,尽管不会直接看到 token 价格,同样的请求结构仍会消耗其使用额度。
打开原文相关 3.6 · 机会 1
media12.5 KB media62.9 KB media83.6 KB media403 B media119.6 KB media74.0 KB media96.3 KB media118.7 KB media207.1 KB media10.2 KB media29.0 KB media9.6 KB media9.5 KB media35.3 KB

中文

输入与输出 token

一次请求会经历 GPU 上的两个阶段,二者的成本不同。

首先是预填充(prefill)阶段,模型会读取你的请求和上下文:系统提示词、你的 CLAUDE.md、你的消息,以及此后添加到对话中的所有内容(Claude 已读取的文件和它执行过的命令输出)。这些就是你的输入 token。

接着是解码(decode)阶段,模型会生成输出 token:它的思考、发出的工具调用,以及你看到的文本。这一过程一次只生成一个 token;一条 200 token 的回复意味着模型连续运行 200 次。按单个 token 计算,解码会让 GPU 忙碌更长时间,因此输出的定价大约是输入的 5 倍。

一个会话中的大量输出 token 都是思考 token,而模型每轮进行多少思考由 effort 级别控制。和模型设置一样,你通过 /effort 选择的级别也会保留,并成为下一次会话的默认设置。

提示: 在一个新会话中执行一次 /model 和 /effort,确认自己实际正在使用什么设置。两者都会记住你上次的选择,因此你应当有意识地作出这个决定。

提示: 如果你已经知道某个会话会是重复性的体力活,可以使用 MAX_THINKING_TOKENS=0 claude 为该会话关闭思考功能(Fable 5 除外)。这比 /effort 的 low 级别还要低一级。

提示缓存

如果一个请求的开头 token 与服务器刚刚见过的某个请求完全一致,那么这个共享开头对应的状态也会相同。因此,服务器可以保留上次的状态,只预填充后续新增的内容。这称为提示缓存(prompt caching)。

从缓存中读取的成本是输入价格的 0.1 倍,因为服务器加载的是已有状态,而不是重新计算。将 token 写入缓存的成本则略高于普通输入,最高可达 2 倍,因为服务器还必须在之后保留该状态。但每个 token 的写入只发生一次,而 0.1 倍价格的读取会发生在此后的每一轮。

Claude Code 会在每个请求中自动管理提示缓存,无需手动开启。不过,你可能会破坏缓存,因此了解如何避免这类成本尖峰非常重要。

假设我们输入“修复 utils.test.ts 中失败的测试”。以下是 Claude Code 为此发送的内容:

  1. Claude Code 由系统提示词(包括工具定义)、你的 CLAUDE.md 和你的消息组装第一个请求并发送出去(输入 token)。此时缓存中还没有内容,因此所有内容都会被预填充并写入缓存。

  2. 模型无法修复自己尚未看到的测试,因此它会先思考片刻,并返回一个针对 utils.test.ts 的 Read 调用(输出 token)。Claude Code 读取该文件,将其追加到对话中,然后再次发送全部内容(输入 token)。这次,请求 1 中的所有内容都从缓存中读回,价格仅为原来的十分之一;只有新增内容,即 Read 调用和该文件,才会按全价预填充。

  3. 此时模型想读取被测试的文件(输出)。又一次 Read,又一次追加,随后再次发送全部内容:请求 1 和 2 从缓存读取,第二个文件按全价计入输入。

  4. 模型返回一个 Edit(输出)。Claude Code 应用该编辑,追加结果,然后再次发送全部内容。逻辑相同:Edit 及其结果是新增内容,位于它们之前的所有内容都是缓存读取(输入)。

  5. 模型运行 npm test(输出)。Claude Code 追加测试输出,再次发送全部内容;其中测试输出是唯一的新内容(输入)。

  6. 测试通过,模型返回简短摘要(输出)。没有工具调用,就没有内容需要追加,也就不会有第 6 个请求;至此结束。

这只是一个小修复,却产生了五个请求,而且每一个请求都包含此前的完整对话。典型的一轮请求极不对称:输入可能有数万 token,输出却只有几百 token。但每轮中只有新增内容才会按全价进行预填充。

这就是每轮的完整账单:历史记录的缓存读取、所有新增内容的全价输入,以及回复的输出价格。

这同样适用于订阅方案。你不会直接看到这些价格,但同样的请求会消耗你的额度。

缓存必须从请求最开始的位置向后完全匹配,而请求始终按相同顺序发送:先是工具定义,然后是系统提示词,再是对话内容(CLAUDE.md 位于对话内容的开头)。

如果这个前缀中的任何内容发生变化,后面的所有内容都会再次被预填充。追加到对话末尾的工具结果是最理想的情况,因为它后面没有其他内容。会使缓存失效的是:更靠近请求前部的内容发生变化,或者缓存键所依据的内容发生变化:

  • /model:每个模型都有独立的缓存,因此下一轮会以全价重新预填充完整对话。(这也包括 opusplan:每当你进入或退出计划模式时,它都会切换模型。)
  • /effort:effort 级别也是缓存键的一部分,因此情况相同。这就是为什么在对话中途切换时,/model 和 /effort 都会要求你确认。
  • Fast mode:它同样属于缓存键的一部分,重新预填充会按 Fast mode 的价格计费。因此,如果要开启它,最好在会话开始时开启。(再次关闭它在缓存层面是免费的。)
  • /compact:对话会被替换为一段更短的内容,因此其中不再有任何内容能够匹配(它前面的系统提示词仍会保留)。只要旧对话仍在缓存中,生成摘要本身成本很低;因此,在长时间离开前执行它,要比离开很久后再执行便宜得多。
  • 时间: 每一轮都会重置计时,但订阅方案的缓存会在一小时后过期,API key 的缓存会在五分钟后过期(ENABLE_PROMPT_CACHING_1H=1 可将其设为一小时)。如果你回来时已超过该时间,下一轮就会重新预填充整个对话。恢复旧会话时几乎也总是如此:缓存通常已经消失,而且系统提示词会在启动时重新构建。

这并不意味着你永远不该切换模型或 effort。它意味着,切换有成本低的时机,例如会话开始时或刚执行 /clear 后;也有成本高的时机,例如一段长对话的中途。

提示: 如果最近几轮的内容已经偏离了你不想保留的方向,请使用 /rewind 回退到这些轮次之前,而不是执行 /compact。回退只会从末尾切掉这些轮次,因此此前的所有内容仍在缓存中,且不会产生额外成本。压缩会重写整个对话,因此总会产生一定成本。

什么决定了一个会话发送多少 token

这里最重要的一点是:没有内容只会被发送一次。最终进入对话的所有内容——Claude 读取的文件,或者它运行的命令输出——都会在此后会话的每一轮中再次发送,直到会话结束。

它们会被缓存,因此每次重新发送的成本较低,但低成本并不等于没有成本;同时,它们还会占用模型每轮思考时必须处理的上下文空间。

这基本就是一个会话的完整成本模型:最终进入上下文的 token 数量、它们在其中停留的轮数,以及你同时运行了多少个上下文。

哪些内容会进入上下文

有一部分上下文在你输入任何内容之前就已经存在:工具定义、系统提示词、CLAUDE.md,以及其他在启动时加载的内容。

提示: 在新会话中执行 /context,查看你输入任何内容前上下文中已有些什么。让 CLAUDE.md 保持为具体指令,并将特定工作流的指令移入 skills;skills 只会在被使用时加载。如果某个 MCP server 在当前会话中并不需要,可通过 /mcp 将其关闭。

会话期间新增的几乎所有其他内容,都是工具结果:Claude 读取的文件,以及它执行命令时产生的输出。

Claude 要读取多少内容,主要取决于它必须自行弄清楚多少事情。如果你说“测试失败了”,它首先得找出是哪项测试失败:执行一两次 grep,打开几个文件以确定哪个相关,而所有这些结果即使早已不再有用,仍会留在上下文中。

“修复 utils.test.ts 中失败的测试”跳过了搜索过程,只需对该文件进行一次 Read 调用;而“修复 @utils.test.ts 中失败的测试”则连这次 Read 调用都不需要。

提示: 当你要引用某个文件时,请使用 @ 提及,而不是手动输入路径。Claude Code 会在发送任何内容前将文件附加到你的消息中,因此该文件会出现在第一个请求里,不需要额外的 Read 调用。无论采用哪种方式,文件本身在上下文中占用的空间相同,因此每个对话中只需提及它一次:它会保留在那里,而在后续轮次再次 @ 提及通常会附加第二份副本。

另一个填满上下文的因素是 Claude 运行命令产生的输出。每次它运行测试、构建或 git log 时,命令打印的内容都会像读取的文件一样被追加到对话中,并在同样多的轮次内保留。

真正特别大的输出实际上没有问题:超过 30,000 个字符后,Claude Code 会将输出写入文件,并且只把简短预览和路径放进对话中(如果需要更改,可设置 BASH_MAX_OUTPUT_LENGTH)。

问题在于所有低于这个阈值的输出。一个测试运行器若逐行打印 400 个通过的测试,可能仍低于该限制,而这 400 行现在就成了后续每一轮的一部分。

Claude 通常会通过 flags 和 tail 为你处理这一问题;如果你不想完全交给 Claude,文档中还有一个小型 hook,可以在嘈杂命令运行前重写它们,使得只返回真正重要的行。

提示: 将你每天反复运行的两三个命令写入 CLAUDE.md,包括安静输出的 flags,并按照你自己会输入的方式编写(例如:“使用 npx vitest run <file> --reporter=dot 运行单个测试文件”)。这只是一个小补充,但能在之后的每个会话中节省一轮交互和几百行输出。

内容会停留多少轮

一个长会话的成本高于将同样工作拆分为几个短会话,而且高出的程度可能超出你的想象,因为第 40 轮也在重新读取之前的 39 轮。你应让会话中的上下文保持简短且相关,因此不要把一个任务的上下文带入下一个任务:开始新任务时使用 /clear,同一任务的早期部分完成后使用 /compact。

提示: 如果之后还想找回会话,请在执行 /clear 前使用 /rename。执行 /compact 时,告诉它需要保留什么;如果要求总是相同,也可以在 CLAUDE.md 中加入“Compact instructions”部分。如果你使用的是 1M 模型,并且希望自动压缩的安全阈值恢复到原来的位置,/autocompact 200k 可以将其恢复(需要 Claude Code v2.1.221+)。

也要留意那些并非由你输入触发的轮次。/loop 会在你设置它的那个会话中作为完整的一轮执行,每次都携带整个对话;如果距离上一轮已超过一小时,它还会额外出现一次缓存未命中。请在另一个终端启动一个新会话,并从那里运行循环。

Subagents

另一种避免让内容进入你当前上下文的方式,是让它在另一个上下文中完成,这正是 subagents 的用途。一个 subagent 拥有自己的上下文窗口、自己的系统提示词、工具和你的 CLAUDE.md,但不拥有你的对话。它独立运行自己的轮次,主会话中唯一会收到的内容是它的回答。其他所有内容都会在它完成后被丢弃。

没有你的对话这一缺点在于,subagent 有时必须重新读取主会话已经拥有的内容,而且在此过程中还要为自己的轮次付费。对于小型任务而言,这只是额外开销。

当任务会产生大量你不需要保留的输出时,subagent 就很划算,例如逐条分析日志。Claude 通常会主动对这类任务使用 subagent;如果它没有这样做,你也可以直接提出要求(“在一个 subagent 中分析这份日志”)。只需记住,主会话最终只会收到 subagent 选择汇报的内容。

提示: 如果有某项高噪声任务需要你反复委派,请为它单独创建一个 subagent 定义,并设置 model: haiku(或 sonnet)。否则,它会使用你的主会话当前所运行的模型。

首先该看哪里

在以上所有因素中,有四项尤其值得关注,按成本高低大致排序如下:

English / 原文

Input and output tokens

A request goes through the GPU in two phases, and they cost different amounts.

First, during prefill, the model reads your request and context: the system prompt, your CLAUDE.md, your message, and everything that's been added to the conversation since (the files Claude has read and the output of the commands it ran). Those are your input tokens.

Then, during decode, it writes output tokens: its thinking, the tool calls it makes, and the text you see. This happens one token at a time; a 200-token response is 200 runs of the model, one after the other. Per token, decode keeps the GPU busy for a lot longer, which is why output is priced at roughly 5x input.

A lot of the output tokens in a session are thinking tokens, and how much thinking the model does per turn is what the effort level controls. Like the model, the level you pick with /effort sticks around as your default for the next session too.

Tip: run /model and /effort once in a fresh session to see what you're actually on. Both remember whatever you picked last time, and you want that decision to be deliberate.

Tip: if you already know a session is going to be grunt work, MAX_THINKING_TOKENS=0 claude turns thinking off for that one session (except on Fable 5), which is the step below /effort low.

Prompt caching

If a request starts with exactly the same tokens as a request the server just saw, the state for that shared beginning comes out the same, so the server can keep it around from last time and only prefill whatever comes after it. This is called prompt caching.

Reading from the cache costs 0.1x the input price, because the server loads the state instead of computing it. Writing tokens into the cache costs a bit more than normal input, up to 2x, since the server also has to hold on to the state afterwards. But the write happens once per token, and the 0.1x reads happen on every turn after it.

Claude Code manages the prompt cache on every request, there's nothing to turn on. However you can break it, so it's important to know how to avoid these cost spikes.

Say we type "fix the failing test in utils.test.ts". Here's what Claude Code sends for it:

  1. Claude Code assembles the first request out of the system prompt (tool definitions included), your CLAUDE.md, and your message, and sends it off (input tokens). Nothing is in the cache yet, so all of it gets prefilled and written into the cache.

  2. The model can't fix a test it hasn't seen, so it thinks for a moment and responds with a Read call for utils.test.ts (output tokens). Claude Code reads the file, appends it to the conversation, and sends the whole thing again (input tokens). This time everything from request 1 is read back out of the cache at a tenth of the price, and the only thing prefilled at full price is what's new: the Read call and the file.

  3. Now the model wants the file under test (output). Another Read, another append, and everything goes out again: requests 1 and 2 from the cache, the second file at full price (input).

  4. The model responds with an Edit (output). Claude Code applies it, appends the result, and sends everything again. Same story: the Edit and its result are new, everything in front of them is a cache read (input).

  5. The model runs npm test (output). Claude Code appends the test output and sends everything again, with the test output as the only new part (input).

  6. The tests pass, and the model responds with a short summary (output). No tool call means nothing to append and no request 6, so we're done.

That's five requests for one small fix, and every one of them contained the entire conversation up to that point. A typical turn is lopsided: tens of thousands of tokens going in, a few hundred coming out. But only what's new in that turn gets prefilled at full price.

That's the whole per-turn bill: cache reads on the history, full input price on whatever's new, and the output price on the response.

This applies on a subscription too. You don't see these prices directly, but the same requests are what draw down your limits.

The cache has to match from the very start of the request forward, and requests always go out in the same order: tool definitions, then the system prompt, then the conversation (with CLAUDE.md at the front of it).

If anything in that prefix changes, everything behind it gets prefilled again. A tool result appended to the end of the conversation is the ideal case, since nothing is behind it. What throws the cache away is anything that changes the request further towards the front, or changes what the cache is keyed on:

  • /model : every model has its own cache, so on the next turn the entire conversation gets prefilled again at full price. (This includes opusplan, which switches models every time you go in or out of plan mode.)
  • /effort: the effort level is part of what the cache is keyed on too, so it's the same story. It's why both /model and /effort ask you to confirm when you switch in the middle of a conversation.
  • Fast mode : also part of the key, and the re-prefill happens at fast mode prices, so if you're going to turn it on, turn it on at the start. (Turning it off again is free, cache-wise.)
  • /compact : the conversation gets replaced with a shorter one, so nothing in it matches anymore (the system prompt in front of it survives). Writing the summary itself is cheap as long as the old conversation is still in the cache, so it's a lot cheaper before a long break than after one.
  • Time: every turn resets the clock, but the cache expires after an hour on a subscription or five minutes on an API key (ENABLE_PROMPT_CACHING_1H=1 makes it an hour). Come back later than that, and the next turn prefills the whole conversation again. Resuming an old session almost always does too: the cache is usually gone by then, and the system prompt gets rebuilt at launch anyway.

None of this means you should never switch models or effort. It means there are cheap moments to do it, the start of a session or right after a /clear, and expensive ones, the middle of a long conversation.

Tip: if the last few turns went somewhere you don't want to keep, /rewind to just before them instead of running /compact. Rewinding only cuts those turns off the end, so everything before them is still cached and it costs nothing. Compacting rewrites the whole conversation, so it always costs something.

What decides how many tokens a session sends

The main thing to know here is that nothing gets sent just once. Everything that ends up in the conversation, a file Claude read or the output of a command it ran, gets sent again on every turn after it, for the rest of the session.

It's cached, so each of those re-sends is cheap, but cheap isn't nothing, and it's taking up room in the context the model has to think around on every turn too.

That's really the whole cost model of a session: how many tokens end up in the context, how many turns they stay there, and how many contexts you're running at the same time.

What ends up in the context

Part of what's in the context is there before you type anything: the tool definitions, the system prompt, CLAUDE.md, and whatever else gets loaded at startup.

Tip : run /context in a fresh session to see what's in there before you've typed anything. Keep CLAUDE.md to specific instructions and move workflow-specific ones into skills, which only get loaded when they're used. If there's an MCP server you don't need in this session, turn it off with /mcp.

Nearly everything else that gets added during the session is tool results: the files Claude reads, and the output of the commands it runs.

How much Claude reads mostly comes down to how much it has to figure out on its own. If you say "the tests are failing", it first has to find out which tests: a grep or two, a few files opened to see which one is relevant, and all of those results stay in the context long after they've stopped being useful.

"Fix the failing test in utils.test.ts" skips the searching and costs one Read call for the file, and "Fix the failing test in @utils.test.ts" doesn't cost the Read call either.

Tip: when you're referring to a file, @-mention it instead of typing the path. Claude Code attaches the file to your message before anything gets sent, so it's in the very first request and there's no Read call for it. The file itself takes up the same room in the context either way, so you only need to mention it once per conversation: it stays there, and @-mentioning it again on a later turn generally attaches a second copy.

The other thing that fills up the context is the output of the commands Claude runs. Every time it runs your tests, a build, or a git log, whatever that prints gets appended to the conversation just like a file it read, and stays there for the same number of turns.

Really big outputs are actually fine: after 30,000 characters Claude Code writes the output to a file and only puts a short preview and the path in the conversation (BASH_MAX_OUTPUT_LENGTH if you want to change it).

The problem is everything under that. A test runner that prints 400 passing tests one line at a time comes in under the limit, and those 400 lines are now part of every remaining turn.

Claude will often take care of this for you with flags and tail, and if you'd rather not leave it up to Claude, there's a small hook in the docs that rewrites noisy commands before they run so only the lines that matter come back.

Tip : put the two or three commands you run all day in CLAUDE.md, quiet flags included, the way you'd type them yourself ("run a single test file with npx vitest run <file> --reporter=dot"). It's a small addition, but it saves a turn and a few hundred lines of output in every session after it.

How many turns it stays there

One long session costs more than the same work spread over a few short ones, and by more than you'd think, because turn 40 is also re-reading the 39 turns before it. You want the context in your session to be short and relevant, so don't carry one task's context into the next: /clear when you start something new, and /compact when the earlier part of the same task is done.

Tip : /rename before you /clear if you'll want the session back later. When you /compact, tell it what to keep, or put a "Compact instructions" section in CLAUDE.md if it's always the same thing. And if you're on a 1M model and would rather have the auto-compact safety net where it used to be, /autocompact 200k puts it back (needs Claude Code v2.1.221+).

Keep an eye on turns that happen when you're not typing, too. A /loop fires as a full turn in the session you set it up in, carrying that whole conversation with it every time, and if it's been more than an hour since the last turn, it's a cache miss on top. Start a fresh session in another terminal and run the loop from there.

Subagents

The other way to keep something out of your context is to have it happen in a different one, which is what subagents are for. A subagent gets its own context window, with its own system prompt, the tools, and your CLAUDE.md, but not your conversation. It runs its own turns, and the only thing that comes back to the main session is its answer. Everything else is thrown away once it's done.

The downside of not having your conversation is that a subagent sometimes has to re-read things the main session already had, and it's paying for its own turns while it does. For a small job it's just overhead.

It pays off when a job produces a lot of output you don't need to keep, like going through a log. Claude will often reach for one on its own for that kind of thing, and you can ask for one directly when it doesn't ("go through this log in a subagent"). Just keep in mind that the main session only gets back what the subagent chose to report.

Tip : if there's a noisy job you hand off over and over, give it a subagent definition of its own with model: haiku (or sonnet). Otherwise it runs on whatever your main session is running on.

Where to look first

Of everything above, four things are worth keeping an eye on, roughly in order of how much they cost: