你有没有在 Claude Code 里敲过 /cost 命令?如果敲过,你大概率会被一个数字吓一跳——cache read tokens,也就是缓存读取的 token 数量,往往占了你整个会话用量的绝大部分,有人实测甚至能到 99% 以上。
这到底是怎么回事?为什么你只是打了几行字,账单上的 token 却动辄几十万?要搞清楚这个问题,我们得先从 LLM 的工作原理讲起。
先聊聊 Token 到底是什么
Token 是大语言模型处理文本的最小单位。你可以粗略地理解为”词块”。英文里大约每个单词对应 1 到 1.5 个 token,中文大约每个汉字对应 1 到 2 个 token。代码会更密集一些,因为括号、缩进、各种符号都会被切分成单独的 token。
Claude 的上下文窗口有容量限制,当前主流模型支持 200K 到 1M 个 token。所有的输入和输出加在一起,不能超过这个窗口大小。一旦超了,旧的内容就会被丢弃或者压缩。
三种 Token 类型:各有各的角色
在 Claude Code 的 API 响应里,每一条消息都附带一个 usage 对象,里面记录了四个字段:
input_tokens:本次请求中新的、未被缓存的输入 tokencache_creation_input_tokens:本次请求中首次写入缓存的 tokencache_read_input_tokens:本次请求中从缓存读取的 tokenoutput_tokens:模型生成的输出 token
我们逐个来看。
Input Tokens:你”真正新说的话”
Input tokens 代表的是每次请求中,模型需要从头处理的那部分内容。听起来这应该是大头,但实际上在一个持续运行的 Claude Code 会话里,这个数字往往小得可怜——可能就几百个 token,甚至个位数。
为什么?因为你每次发消息的时候,真正”新”的内容就是你刚刚输入的那句话。系统提示词、工具定义、CLAUDE.md 配置文件、之前的对话历史——这些内容在上一轮已经处理过了,它们会走缓存通道,不再算作 input tokens。
以 Sonnet 4.6 为例,input tokens 的价格是每百万个 $3。
Output Tokens:模型”说的话”
Output tokens 是模型生成的所有内容,包括它给你写的代码、解释、分析结果,还有 extended thinking(扩展思考)时的思维过程。
Output tokens 的价格远高于 input tokens。还是拿 Sonnet 4.6 来说,output tokens 每百万个 $15,是输入的 5 倍。这个价格差异背后有深刻的技术原因,我们一会儿再说。
值得注意的是,prompt caching 对输出没有任何影响。不管你的输入是从缓存读的还是新处理的,模型生成的回答都是一模一样的。缓存只是加速了”理解你问题”的过程,不影响”回答你问题”的过程。
Cache Tokens:占大头的”老朋友”
Cache tokens 分两种:cache write(缓存写入)和 cache read(缓存读取)。
Cache Write:当一段内容第一次出现、还没有被缓存过的时候,系统会把它写入缓存。写入缓存的价格是普通 input 的 1.25 倍(5 分钟有效期的情况下)。Max 订阅用户可以享受 1 小时有效期,但写入价格是 2 倍。
Cache Read:当后续请求中出现了和缓存内容完全一致的前缀时,系统直接从缓存读取,不需要重新计算。读取的价格只有普通 input 的十分之一。
用 Sonnet 4.6 的价格来算:普通 input 是每百万 $3,cache read 只要 $0.3,而 cache write 是 $3.75。也就是说,只要你写入一次缓存、后续读取两次,成本就已经低于不用缓存的方案了。
为什么 Cache Token 这么多?
好了,终于到了核心问题。如果你用 ccusage 或者 Claude Code Usage Monitor 之类的工具分析过自己的会话数据,你会发现一个震撼的事实:cache read tokens 占了你总用量的绝大多数,有些用户的数据里这个比例高达 77%,甚至更夸张的达到 99.93%。
这不是 bug,这是 LLM API 的工作方式决定的。
关键事实:API 是无状态的
这是理解整个问题的钥匙。Claude API 没有”记忆”。它不会记得你上一轮说了什么。每一次你在 Claude Code 里按下回车,发出去的不是你刚刚打的那一行字——发出去的是整个对话历史。
具体来说,每次 API 调用都包含:
- 系统提示词(约 14K token)——Claude Code 的行为指令、工具定义、你的 CLAUDE.md 配置
- 完整的对话历史——从会话开始到现在的每一条消息、每一次工具调用、每一个工具返回结果
- 你刚刚输入的新消息——可能就几十个 token
第 1 轮对话,总共可能就发了 15K token。但到了第 50 轮,你发的内容可能已经积累到了 100K 甚至更多。而这些内容里,绝大部分在上一轮已经发过了,只是这次又原封不动地发了一遍。
幸运的是,prompt caching 会帮你识别出这些重复的前缀,从缓存里直接读取,省去了重新计算的成本。但即便如此,这些 cache read tokens 还是会出现在你的用量统计里。
滚雪球效应
想象一下这个场景:
- 第 1 轮:发送 15K token(系统提示词 + 你的第一句话),几乎没有缓存
- 第 2 轮:发送 18K token(上一轮的 15K 走缓存 + 新的 3K),cache read = 15K
- 第 3 轮:发送 22K token(前两轮的 18K 走缓存 + 新的 4K),cache read = 18K
- ……
- 第 50 轮:发送 150K token,其中 147K 走缓存,cache read = 147K
看出来了吗?对话越长,每一轮的 cache read 就越大。这是一个线性累加的过程。有人做过统计,一个 100 条消息的会话,仅仅是 CLAUDE.md 文件(假设 15K token)就会被缓存读取 100 次,产生 150 万个 cache read tokens。
CLAUDE.md 和 MCP 工具的放大效应
你的 CLAUDE.md 文件、项目级配置、全局配置——这些内容在每一次 API 调用里都会被包含进去。如果你的 CLAUDE.md 写了 10K token,那每发一条消息就多 10K 的 cache read。50 条消息下来,光 CLAUDE.md 就产生了 50 万个 cache read tokens。
MCP 工具也是同理。每个 MCP 服务器注册的工具定义都会加到上下文里,不管你这一轮有没有用到它们。一个 Playwright 服务器可能带 22 个工具、接近 3500 个 token;一个 Gmail 服务器 7 个工具、2640 个 token。如果你连了 4、5 个 MCP 服务器,还没开始干活,上下文里就可能已经塞了 7000 到 55000 个 token 的工具定义了。这些定义每轮都会被作为缓存读取。
深层原因:为什么 LLM 非得这么做?
要理解为什么每一轮都要重发完整历史,需要了解 Transformer 模型的注意力机制。
LLM 生成文本是一个”自回归”过程:每次只生成一个 token,而每生成一个新 token,模型都需要”注意”到前面所有的 token。这个注意力计算涉及三个向量——Query(查询)、Key(键)和 Value(值),简称 QKV。
对于输入阶段(prefill),模型把你的整个提示词一次性灌进去,计算出所有 token 的 K 和 V 向量,存到 KV Cache 里。这一步可以高度并行化,效率很高。
对于输出阶段(decode),模型每次只处理一个新 token,但需要读取之前所有 token 的 KV Cache 来计算注意力。每生成一个 token 就要跑一次模型的完整前向传播(forward pass)。
这就解释了两件事:
第一,为什么 output token 比 input token 贵 5 倍。 处理 1000 个 input token 只需要一次前向传播,因为它们可以并行处理。但生成 1000 个 output token 需要 1000 次前向传播,每次都要加载一遍模型权重。在当前 GPU 的硬件架构下,这个过程严重受限于内存带宽而不是计算能力,所以成本远高于输入。
第二,为什么 cache read 只要十分之一的价格。 当 API 收到一个请求,发现前缀和缓存里的内容完全匹配时,它可以直接复用之前计算好的 KV Cache,不需要重新跑 prefill。省掉的就是那个昂贵的注意力计算过程。服务商只需要做一次哈希匹配和内存读取,成本自然大幅降低。
顺便提一句,这也是为什么你跟 Claude 对话时,第一个 token 出来得比较慢(模型在做 prefill),但后面的 token 流式输出就快多了(只是 decode,而且有 KV Cache 加持)。
实际成本影响有多大?
我们来算一笔账。假设你用 Sonnet 4.6($3/$15 per MTok),进行一个 20 轮的对话,有 100K token 的稳定上下文(系统提示词 + CLAUDE.md + 工具定义 + 前几轮对话):
不使用缓存的情况:每轮都要重新处理 100K input token,20 轮 = 200 万 input tokens × $3/MTok = $6.00
使用缓存的情况:第 1 轮写入缓存花费 100K × $3.75/MTok = $0.375,后续 19 轮缓存读取 = 190 万 × $0.3/MTok = $0.57,合计 $0.945
缓存帮你省了 84% 的输入成本。所以,虽然你在 /cost 里看到的 cache read tokens 数量吓人,但它们每一个的实际”单价”只有普通 input 的十分之一。没有缓存的话,你要为同样多的 token 付 10 倍的钱。
换个角度看:cache read tokens 多,恰恰说明缓存在正常工作。如果你看到大量的 input_tokens 和 cache_creation_input_tokens,反而要担心了——那意味着缓存频繁失效,你在反复为相同的内容付高价。
怎么优化?
既然理解了原理,优化策略就很清晰了:
控制 CLAUDE.md 的大小。 这个文件每轮都会被加载。把只在特定场景下需要的指令放到 skills 或 custom commands 里,让它们按需加载,而不是每轮都带着。
及时用 /clear 清理上下文。 切换到不相关的任务时,旧的对话历史只会白白增加 cache read 的量。用 /clear 开始一个干净的会话。
用 /compact 压缩对话。 当对话变长时,手动用 /compact 可以把历史压缩成摘要,大幅减少后续每轮要读取的缓存量。虽然压缩是有损的,但比让上下文无限膨胀要好得多。
精简 MCP 工具。 不用的 MCP 服务器就断开。用 /context 命令看看哪些工具在吃你的上下文。能用 CLI 工具(比如 gh、aws)代替的,就别挂 MCP 服务器。
写精确的 prompt。 “帮我改改这个代码”会让 Claude 大范围扫描文件,消耗大量上下文。”给 auth.ts 里的 login 函数加上输入验证”则可以精准定位,一轮搞定,减少来回交互产生的上下文累积。
保持缓存热度。 Pro 用户的缓存有效期只有 5 分钟,Max 用户是 1 小时。如果你中间思考太久,缓存过期了,下一轮就要重新写入(cache write),比缓存读取贵 12.5 倍。偶尔发一条短消息”保温”,可以避免缓存过期。
总结
回到最初的问题。Input、Cache、Output 这三种 token 的核心区别是:
Input tokens 是每次请求中全新的、未被缓存的内容,数量通常很少,按标准价格计费。
Cache tokens 是上下文中与之前请求重复的部分,数量庞大但单价极低(只有 input 的十分之一),是整个系统成本优化的核心机制。
Output tokens 是模型生成的内容,数量取决于任务复杂度,单价最高(input 的 5 倍),因为每个 token 都需要一次独立的模型前向传播。
Cache token 之所以数量惊人,根本原因在于 LLM API 的无状态设计——每一轮对话都必须重发完整的上下文历史。而 prompt caching 的存在,让这个本来很昂贵的”重复劳动”变成了一个廉价的缓存查找操作。
所以下次看到 /cost 里那个巨大的 cache read 数字时,不用慌。它恰恰说明系统在帮你省钱。真正需要关注的,是 output tokens 的数量和你的对话轮次——这两者才是账单变大的主要推手。

发表回复