1. 先搞清楚 Claude Code 的 Cache Read Token 到底在缓存什么
如果你最近认真看过 Claude Code 的/usage输出,大概率会被一组数字吓到:普通 input 只有一两千 token,output 也就几千,可 cache read 那一栏直接飙到几十万甚至上百万。我第一次看到 940.0k cache read 的时候,第一反应是"这玩意儿是不是在偷偷烧我的钱"。
先说结论:Cache Read Token 高,在长会话里通常是省钱信号,不是浪费信号。
要理解这件事,得先把 Claude Code 从"聊天框"的认知里拽出来。你在终端敲一句"帮我修一下这个路由 bug",Claude Code 实际发给模型的上下文远不止这句话。它至少包含这几层:
- Claude Code 自己的系统提示词(System Prompt),描述它是个什么 Agent、该怎么用工具、输出格式是什么
- 工具定义(Tool Definitions),Read、Edit、Bash、Grep、Glob 这些工具的 JSON Schema
- 工作目录、Git 状态、平台、Shell、OS 版本等环境信息
- 项目规则文件,比如
CLAUDE.md - 已经读取过的源码、搜索结果、测试输出
- 历史对话轮次和工具调用结果
这些内容加起来,几万到几十万 token 很正常。而 Claude Code 是个 Agent Loop:读文件 → 跑测试 → 分析报错 → 改代码 → 再跑测试,每一步都是一次独立的模型请求,每次请求都带着前面累积的全部上下文。
Prompt Caching 缓存的就是这些"前缀"。更准确地说,它缓存的是从 Prompt 开头到某个 Cache Breakpoint 之间的完整前缀,在模型内部对应的 KV 表示(Key-Value 中间状态)。下一轮请求如果前缀完全一致,就不用从第一个 token 重新做一遍 Prefill 计算,直接复用。
所以那 940k cache read 的真实含义是:有 94 万个 token 本来要按普通 Input 价格重新算一遍,现在走了缓存通道,单价只有普通输入的十分之一。
这里有个特别容易搞混的公式,记住它基本就不会误判账单:
Total Input = Cache Read + Cache Creation + Regular Input三个指标各管一段:
| 指标 | 含义 | 单价(以 Sonnet 4.6 为例) |
|---|---|---|
input_tokens | 本轮新增、未缓存的输入 | $3 / 百万 token |
cache_creation_input_tokens | 正在建立缓存前缀的输入 | $3.75 / 百万(5 分钟 TTL) |
cache_read_input_tokens | 命中缓存的历史前缀 | $0.30 / 百万 |
output_tokens | 模型真正生成的新内容 | 按输出价计费 |
倍率关系很好记:5 分钟 Cache Write 是普通输入的 1.25 倍,1 小时 Cache Write 是 2 倍,Cache Read 是 0.1 倍。
拿一段稳定的 10 万 token 上下文连续用 10 次算笔账:完全不用缓存,10 次全按普通输入,约 3 美元;用 5 分钟缓存,第一次写入约 0.375 美元,后面 9 次读取 90 万 cache read token 约 0.27 美元,合计约 0.645 美元。同一批重复上下文,成本从 3 美元降到 0.645 美元,省了大约 78.5%。
还有个关键点:Prompt Caching 是前缀缓存,不是答案缓存。它不会把历史回答直接拿出来复用,当前这一轮的答案仍然是模型实时生成的。缓存命中要求缓存点之前的 Prompt 段 100% 一致,包括文本和图像。所以它更像"计算缓存",而不是"语义缓存"。
理解了这一层,再看 Claude Code 的/usage,思路就该从"总共出现了多少 token"切换成"这些 token 分别落在哪个计费通道"。Cache Read 大,说明命中率高;真正该警惕的是稳定上下文反复 Cache Miss,每一轮都按普通 Input 或频繁 Cache Write 重新算。
2. 把 endpoint 切到 TaoToken 统一 Key 通道的前置准备
搞清楚了缓存机制,接下来要解决一个实际问题:怎么稳定地观察 Cache Read Token 的变化,并且让多个工具、多个项目共用一套 Key 和 endpoint。
我试过在本地同时跑 Claude Code、Cline、Codex 这类工具,每个都单独配一遍 Anthropic Key,改起来很烦,而且不同工具对 Base URL 的写法还不一样。这时候把 endpoint 统一到一个通道会省事很多。TaoToken 提供的就是这样一个统一 Key 通道,官网在 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 入口是 https://taotoken.net/api 。
在动手之前,先把三件套准备好,这是后面所有配置的基础:
- Base URL:
https://taotoken.net/api - API Key:在控制台创建,形如
sk-开头的一串字符 - Model ID:比如
claude-sonnet-4-6、claude-opus-4-8这类,具体以你账号里可用的模型为准
这三样东西缺一不可。很多人配置失败,不是网络问题,而是只填了 Base URL 忘了 Model ID,或者 Key 复制时带了空格。
先创建 Key。打开控制台页面 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,登录后在 API Keys 区域新建一个 Key。建议按用途分开建,比如claude-code-dev、cline-test,这样后面看用量时能区分是哪个工具在消耗。
创建完 Key 之后,先别急着往 Claude Code 里塞,用一条 curl 验证通道本身是通的:
curl https://taotoken.net/api/v1/messages \ -H "x-api-key: $TAOTOKEN_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "content-type: application/json" \ -d '{ "model": "claude-sonnet-4-6", "max_tokens": 64, "messages": [ {"role": "user", "content": "只回复两个字:收到"} ] }'把$TAOTOKEN_API_KEY换成你自己的 Key。如果返回里能看到content字段和正常的usage结构,说明通道没问题。这一步很重要,因为如果直接跳到 Claude Code 配置,一旦报错你分不清是 Key 的问题、Base URL 的问题,还是 Claude Code 自身配置的问题。
关于 Key 的存放,别硬编码在脚本里。用环境变量:
export TAOTOKEN_API_KEY="sk-你的key"写进~/.zshrc或~/.bashrc,新开终端生效。这样 Claude Code、Cline、curl 都能复用同一个变量,改 Key 只改一处。
还有一点要提醒:不要把生产数据库的凭据、真实用户数据塞进测试 Prompt。缓存机制本身是内存态、按组织隔离的,但测试阶段养成好习惯没坏处。
前置准备做到这里就够了:一个可用的 Key、一个验证过的 Base URL、一个明确的 Model ID。接下来进入真正的配置环节。
3. 可复制的 settings 配置片段与缓存命中验证
这一节是重点,直接给可复制的配置。Claude Code 的配置主要落在~/.claude/settings.json,部分场景也会用到项目级的.claude/settings.json。
先看全局配置。打开或新建~/.claude/settings.json,写入下面这段:
{ "env": { "ANTHROPIC_BASE_URL": "https://taotoken.net/api", "ANTHROPIC_AUTH_TOKEN": "sk-你的key", "ANTHROPIC_MODEL": "claude-sonnet-4-6", "ANTHROPIC_SMALL_FAST_MODEL": "claude-sonnet-4-6" }, "cleanupPeriodDays": 30 }几个字段说明一下:
ANTHROPIC_BASE_URL:指向 TaoToken 的 API 入口,注意这里不带任何路径后缀,就是https://taotoken.net/apiANTHROPIC_AUTH_TOKEN:填你的 Key。有些版本读ANTHROPIC_API_KEY,如果前者不生效就换成后者试ANTHROPIC_MODEL:主模型 IDANTHROPIC_SMALL_FAST_MODEL:处理轻量任务(比如生成 commit message)用的模型cleanupPeriodDays:本地 session transcript 的保留天数,和 Prompt Cache 是两码事,别搞混
如果你更习惯用环境变量而不是 settings.json,等价写法是:
export ANTHROPIC_BASE_URL="https://taotoken.net/api" export ANTHROPIC_AUTH_TOKEN="sk-你的key" export ANTHROPIC_MODEL="claude-sonnet-4-6"两种方式选一种就行,同时配可能互相覆盖,反而排查困难。
配置写完后,重启 Claude Code,让它重新读取 settings。然后做一次缓存命中验证,这是本篇最核心的动作。
验证步骤:
第一步,在一个真实项目目录里启动 Claude Code,让它读几个文件。比如:
请阅读 package.json 和 src/router/index.ts,然后告诉我这个项目用的什么路由方案第二步,等它回答完,紧接着追问一个基于上文的问题:
基于你刚才读到的路由配置,如果我要加一个全局前置守卫,应该改哪个文件第三步,输入/usage查看统计。
预期结果:第一次请求里,cache_creation_input_tokens会比较高,因为它在建立缓存前缀;第二次请求里,cache_read_input_tokens会明显上升,而input_tokens保持很小。这就是缓存命中的直接证据。
如果第二次请求的 cache read 依然是 0,说明前缀没匹配上,常见原因在下一节展开。
再补一个多工具共用的场景。如果你同时用 Cline,它的 MCP 配置里也要写全三件套。以 Cline 的配置为例:
{ "mcpServers": { "taotoken": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-fetch"], "env": { "BASE_URL": "https://taotoken.net/api", "API_KEY": "sk-你的key", "MODEL_ID": "claude-sonnet-4-6" } } } }注意这里 Base URL、Key、Model ID 三件套一个都不能少。Cline 这类工具如果只填了 Base URL 没填 Model ID,请求会直接失败或者回落到默认模型,导致你观察到的 cache read 数据对不上。
如果你用的是 Codex 的auth.json体系,思路一样,把 endpoint 指向https://taotoken.net/api,Key 填进去,模型 ID 写清楚。不同工具的字段名不同,但三件套的逻辑不变。
配置完成后,建议固定一个测试项目,每次改配置后都跑一遍上面那三步验证。这样你能快速判断是配置生效了,还是缓存没命中。
4. 一次完整的缓存命中率验证与结果解读
上一节给了配置和验证步骤,这一节把一次完整验证的过程和结果讲透,让你看到数字时知道该怎么读。
假设我在一个 Angular 项目里跑了一次典型的多轮会话。第一轮让 Claude Code 读package.json、src/app/app-routing.module.ts、src/app/guards/auth.guard.ts和CLAUDE.md。第二轮让它基于这些内容分析路由守卫的加载顺序。第三轮让它改一个文件并跑测试。
跑完三轮后/usage大致会呈现这样的形态:
input_tokens: 1.2k output_tokens: 5.3k cache_creation_input_tokens: 50.0k cache_read_input_tokens: 940.0k先别被 940k 吓到。拆开看:
- 第一轮:读文件,建立缓存前缀,
cache_creation上升,cache_read接近 0 - 第二轮:前缀命中,
cache_read大幅上升,input_tokens只包含新增的那句追问 - 第三轮:前缀继续命中,
cache_read再涨一截,cache_creation只增加新写入的尾部
940k cache read 意味着大约 94 万个 token 走了 0.1 倍单价的通道。如果这些 token 全部按普通输入算,成本是它的 10 倍。
这里要理解 Claude Code 的 Automatic Caching 机制:它会随着对话推进自动把 Cache Breakpoint 往后移。第一轮缓存 System + User1 + Assistant1 + User2,第二轮前面部分从缓存读,只把新增的 Assistant2 + User3 写入新缓存,第三轮继续往后推。开发者不需要手工管理 Breakpoint。
怎么判断缓存是否健康?看两个信号:
一是cache_read在长会话中应该持续增长。如果它长期接近 0,而input_tokens或cache_creation反复很高,说明前缀一直在变,缓存没命中。
二是input_tokens应该保持相对小。它代表本轮真正新增、未缓存的内容。如果它突然变得很大,说明有大量内容没走缓存。
哪些操作会破坏缓存前缀?这是实战里最容易踩的坑:
- 工具定义变化。打开或关闭 Web Search 会改变 System Prompt,导致 System 和 Message 层缓存失效
- 在 System Prompt 里塞当前时间、随机 UUID。每次请求前缀都不同,缓存永远命中不了
- JSON 序列化顺序不稳定。内容语义相同但字节序列不同,前缀 Hash 就变了
- 图像的增加和删除会影响 Message 层缓存
- 不同工作目录可能导致 System Prompt 不同,因为里面包含工作目录、Git 状态、平台、Shell 等信息
Anthropic 把缓存层级描述为tools → system → messages,前面层级一变,后面全部失效。所以稳定内容要尽量靠前,动态内容靠后。
关于 TTL 的选择。默认 5 分钟,命中后生命周期会刷新,所以活跃会话能连续维持缓存。5 分钟缓存写入是 1.25 倍,只要后续命中一次,总成本 1.25x + 0.1x = 1.35x,而不用缓存两次是 2x,一次命中就开始省钱。1 小时缓存写入是 2 倍,需要命中两次才回本(2x + 0.1x + 0.1x = 2.2x,对比三次普通输入 3x)。Claude Code 这种高频多轮场景,默认 5 分钟就很合适。
关于最小缓存长度。不同模型要求不同,Sonnet 系列一般是 1024 token,Opus 部分型号是 4096 token。低于这个长度即使配了 cache_control 也不会真正建立缓存,而且 API 不一定报错。所以验证时要确保前缀足够长,读几个真实文件是必要的。
关于本地 Session Cache 和 Prompt Cache 的区别。Claude Code 会在~/.claude/projects/保存 session transcript,用于/resume,默认保留 30 天。这是本地缓存,和 Prompt Cache 完全不是一回事。删掉本地目录不会清理 Prompt Cache,Prompt Cache 也不会把你的代码仓库长期存在本地。
验证做到这里,你应该能明确回答:这次会话的 cache read 是多少、命中率如何、哪些内容走了缓存通道。接下来处理报错。
5. 本篇常见报错排查:401、local proxy failed 与 reading choices
配置和验证过程中,报错基本集中在几类。这一节按真实报错逐个拆。
报错一:401 Unauthorized
API Error: 401 {"type":"error","error":{"type":"authentication_error","message":"invalid x-api-key"}}原因通常是 Key 不对。排查顺序:
先确认ANTHROPIC_AUTH_TOKEN或ANTHROPIC_API_KEY里的 Key 没有多余空格、换行。从控制台复制时经常带上尾部空格。
再确认 Key 没有过期或被删除。去 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 检查 Key 状态。
最后确认你用的字段名对。有些 Claude Code 版本读ANTHROPIC_API_KEY,有些读ANTHROPIC_AUTH_TOKEN。两个都试一下,或者干脆在 settings.json 里两个都写。
报错二:local proxy failed / connection refused
Error: connect ECONNREFUSED 127.0.0.1:xxxx local proxy failed这类报错说明请求根本没发到目标 endpoint,而是被本地某个代理配置拦截了。检查:
- 环境变量里有没有残留的
HTTP_PROXY、HTTPS_PROXY、ALL_PROXY - settings.json 里有没有指向本地端口的配置
- 系统级代理设置是否影响终端
清掉这些之后,直接用第 2 节的 curl 命令验证通道,能通再回到 Claude Code。
报错三:reading 'choices' of undefined
TypeError: Cannot read properties of undefined (reading 'choices')这个报错通常出现在用 OpenAI 兼容格式调用、但返回结构不匹配的时候。Claude 的原生 Messages API 返回的是content数组,不是choices。如果你用的工具默认按 OpenAI 格式解析,就会读不到choices。
解决办法是确认工具用的是 Anthropic 原生协议,Base URL 指向https://taotoken.net/api,而不是 OpenAI 兼容路径。同时确认 Model ID 是 Claude 系列,不是 GPT 系列。
报错四:OAuth / 登录态相关报错
OAuth error: invalid_grant如果你之前用官方账号登录过 Claude Code,本地可能残留 OAuth 凭据,和新的 Key 配置冲突。清理~/.claude/下的凭据缓存,或者用独立的配置目录启动。
报错五:缓存不命中,cache_read 长期为 0
这个不算报错,但最影响成本。排查:
- 前缀是否 100% 一致,包括工具定义
- System Prompt 里有没有动态内容(时间、UUID、随机 ID)
- JSON 序列化顺序是否稳定
- 前缀长度是否达到模型的最小缓存要求
- 工作目录是否频繁变化
报错六:模型 ID 无效
model: claude-xxx not found确认 Model ID 拼写正确,并且在你账号的可用模型列表里。三件套里 Model ID 最容易写错,尤其是版本号后缀。
排查完这些,基本能覆盖 90% 的配置问题。核心原则是:先用 curl 验证通道,再验证 Claude Code 配置,最后看缓存命中。分层排查比一股脑改配置高效得多。
6. 把 endpoint 固定到统一通道后怎么持续观察 Cache Read
配置跑通、报错排完,最后一步是让它稳定运行并持续观察。
把 endpoint 固定到 TaoToken 统一 Key 通道之后,最大的好处是多个工具、多个项目共用一套 Key 和 Base URL。你不需要在每个工具里重复配置,改一处全局生效。观察 Cache Read 时,数据来源也统一了。
持续观察的三个动作:
第一,固定一个测试项目。每次改配置或升级 Claude Code 后,跑一遍第 3 节的三步验证,对比 cache read 的变化。这样能快速发现配置回退。
第二,定期看/usage的 Usage Breakdown。Claude Code 新版会显示更详细的分解,当 Long Context 或 Cache Miss 占比异常时会给出 Behavior Flag。看到 flag 就去查前缀稳定性。
第三,区分计费通道做成本分析。把 token 分成四类看:普通 Input 是新增未缓存输入,Cache Write 是建立前缀,Cache Read 是命中历史前缀,Output 是真正生成的新内容。健康的长会话里,Cache Read 应该占大头。
几个实用技巧:
把稳定内容前置。工具定义、System Instruction、项目规范放前面,动态内容放后面。这是提升命中率最直接的办法。
避免在 System Prompt 里注入时间戳和随机 ID。如果业务需要,放到 messages 尾部。
如果基于 Claude Agent SDK 做自动化 Agent,注意excludeDynamicSections这个选项,它能把工作目录、Git 状态等 session 相关信息移出 System Prompt,让静态部分跨 session 共享缓存。
多轮对话优先用 Automatic Caching,复杂系统再考虑 Explicit Breakpoint。Breakpoint 最多 4 个,有 20 个 Block 的 Lookback Window,历史缓存点被推太远可能找不到。
关于成本的心理模型。看到几十万 cache read 不要慌,先算它替代了多少普通输入。940k cache read 按 0.1 倍单价算,相当于 94k 普通输入的成本。如果这些内容本来要按普通输入重复算,成本是它的 10 倍。真正该警惕的是稳定上下文反复 Cache Miss。
关于隐私。Prompt Caching 用的是内存中的 KV 表示和内容 Hash,不是把原始 Prompt 持久化到磁盘。缓存条目在 TTL 结束后清理,不同组织之间不共享缓存。这一点在选通道时值得留意。
最后回到那个核心公式:Total Input = Cache Read + Cache Creation + Regular Input。看懂这四个数字的分布,就看懂了 Claude Code 背后的 token 经济学。模型能力决定它能不能完成任务,而 Context Management 和 Prompt Caching 决定同样的任务要花多少推理成本。把 endpoint 固定到统一通道,持续观察 Cache Read 的变化,你就能在长会话里既保住效果,又控住成本。