claude.dev 解读
EngineeringThariq Shihipar2026-04-30约 6 分钟阅读原文 ↗

Lessons from building Claude Code: Prompt caching is everything

缓存按前缀逐字节匹配,Claude Code 的一切设计都围绕保住共享前缀

主张
6
审视
5
遗漏
2
  1. 01Prompt caching 按前缀逐字节匹配,所以 Claude Code 的整个 harness 都按「静态内容在前、动态内容在后」来排布,缓存命中率也被当成线上事故指标来盯。
  2. 02能保住缓存的做法有这几种:状态变化通过 messages 里的 system-reminder 注入,不去改 system prompt;会话中途不换模型、不增删工具;Plan Mode 本身做成工具;MCP 工具用 defer_loading 存根延迟加载。
  3. 03压缩(compaction)要用 cache-safe forking,也就是沿用父会话完全相同的 system prompt 和工具定义,只在末尾追加摘要指令。否则越长的对话,摘要那一次调用越贵。
怎么读这篇
作者主张 · Thariq Shihipar我们的审视
01

顺序即成本:静态在前

缓存按前缀匹配,所以内容的排列顺序决定了成本。

机制拆解

API 会缓存从请求开头到每个 cache_control 断点为止的全部内容。前缀里任何位置一变,后面的缓存就全部作废。所以按变化频率从低到高排:全局的静态 system prompt 和工具,然后是项目级的 CLAUDE.md 和 memory,再是会话级上下文,最后是对话消息。这样尽可能多的会话能共用同一段前缀。

“the order you put things in matters enormously” ¶5

这一点暂无对应的审视。

02

状态更新走消息通道

状态更新走消息通道,不改 system prompt。

机制拆解

时间、文件改动这类会过期的信息,如果写回 system prompt 就会让整段前缀失效。Claude Code 的做法是在下一条 user message 或 tool result 里附一个 system-reminder 标签,前缀保持不动。作者也提到排序很容易被无意破坏:静态 prompt 里放了精确时间戳、工具定义顺序不固定、改了工具参数,都出过事故。

“we add a <system-reminder> tag in the next user message or tool result” ¶15

遗漏

代价一侧几乎没展开:system-reminder 注入对输出质量的影响只用一句话带过(¶6);defer_loading 会多一次 tool search 往返,模型也可能选错或漏选工具;压缩摘要会丢信息。文中都没给数据。

自己验证在 harness-eval 的用例里对比两种配置下的工具选择正确率和端到端时延:MCP 工具全量加载,以及用存根延迟加载。
03

中途换小模型反而更贵

缓存按模型隔离,会话中途换成便宜的模型反而可能更贵。

机制拆解

每个模型的缓存是各自独立的。例如 Opus 对话已经积累到 100k token,这时切到 Haiku,就得在 Haiku 上把整段前缀按未缓存价重新写一遍。真要换模型,正确做法是开一个 subagent,由主模型写一份交接消息再交给另一个模型;Claude Code 的 Explore agent 就是这样用 Haiku 的。

“more expensive to switch to Haiku than to have Opus answer” ¶17

厂商视角

命中率高直接关系到厂商自己的算力成本和订阅限额,所以「别中途切到便宜模型」这条建议也同时增加了大模型的用量。文中只举了一个对 Opus 有利的例子,没有给出对话长度和问题难度在什么范围内切换反而更省的临界点。

自己验证用自己定时任务的实际 usage,按当前价目分别算两种情况:留在大模型上继续用缓存,以及切到小模型重新写前缀。在不同上下文长度下各算一遍,找出盈亏平衡点。
04

工具集合永不增删

工具集合始终固定,状态切换和按需加载都做成工具或存根。

机制拆解

工具定义属于缓存前缀,增删一个工具就会让整段对话的缓存失效。所以 Plan Mode 不靠替换成只读工具集来实现,而是把 EnterPlanMode 和 ExitPlanMode 做成工具,再用 system message 说明当前约束。顺带的好处是模型遇到难题时能自己进入计划模式。MCP 工具多的时候只发名称存根(defer_loading: true),模型通过 tool search 选中后才加载完整 schema,存根本身的内容和顺序始终不变。

“we keep all tools in the request at all times” ¶21

这一点暂无对应的审视。

05

压缩要共用父会话前缀

压缩之类的旁路计算必须共用父会话的前缀(cache-safe forking)。

机制拆解

直观的写法是单独发一次「总结一下」的调用,换一个 system prompt、不带工具。但这样从第一个 token 起前缀就不一样了,整段对话都按未缓存价计费,而且对话越长、越需要压缩,这一次就越贵。正确做法是沿用父会话完全相同的 system prompt、上下文和工具定义,接上原有消息后在末尾追加压缩指令,这样新增的 token 只有指令本身。代价是要预留一块 compaction buffer,给指令和摘要输出(图中约 20k token)留出空间。

“use the exact same system prompt, user context, system context, and tool definitions” ¶28

可复现性

「会话中途增删工具或换模型会让缓存失效」「独立摘要调用拿不到缓存」这两条都可以低成本复现。

自己验证用 claude -p 加 JSON 输出跑同一段长上下文:第一组前后两次完全一致,第二组第二次多加一个 MCP server,第三组第二次换模型。比较三组的缓存读取和缓存写入 token。具体字段名先以 CLI 的实际输出为准。
06

命中率当可用性来盯

缓存命中率要像可用性(uptime)一样监控,命中率掉下来按事故处理。

机制拆解

命中率高,成本就低,订阅方案的限额也能给得更宽,所以 Claude Code 对缓存失效设了告警,命中率过低会宣布 SEV。作者说缺失率差几个百分点就会明显影响成本和延迟,但文中没有给出具体基线。

“Monitor your cache hit rate like you monitor uptime” ¶36

数字口径

「缺失率差几个百分点就会明显影响成本」没有给基线命中率,也没有给分母口径(按请求算还是按 token 算)。图中缓存读取是原价十分之一,但文中完全没提缓存写入的溢价和缓存过期时间,这两项会改变长间隔场景(比如每周跑一次的任务)的计算。

自己验证查官方价目页里缓存写入、读取和过期时间的条款,对照 inference-scheduling 第 05 节已经整理的价目数据;在自己的日志里同时统计按 token 和按请求两种命中率。

作者没有直接谈到这一点。

遗漏

文章没有讨论 effort 或 thinking 档位的改变会不会打断缓存,而 L-001 方案是否可行正取决于这一点。

自己验证同一会话前后两轮只改 effort 参数,观察第二轮的缓存读取 token 是否接近前一轮的上下文长度。
01

机制聚焦

压缩:独立调用 vs 安全分叉

同样是给长对话做摘要,前缀是否与父会话一致,决定了这次调用能否命中缓存。

独立摘要调用
cache-safe forking
system prompt
另写一个「summarize this」
与父会话完全相同
工具定义
不带任何工具
沿用父会话的工具定义
摘要指令位置
放在独立的 system prompt 里
作为新 user 消息追加在末尾
缓存效果
首个 token 就分叉,全部未命中
复用缓存前缀,只新增压缩提示
代价
对话越长,这次调用越贵
需预留 compaction buffer
依据原文 ¶28 ↗
02

核心观点

01

顺序即成本:静态在前

缓存按前缀匹配,所以内容的排列顺序决定了成本。

机制拆解

API 会缓存从请求开头到每个 cache_control 断点为止的全部内容。前缀里任何位置一变,后面的缓存就全部作废。所以按变化频率从低到高排:全局的静态 system prompt 和工具,然后是项目级的 CLAUDE.md 和 memory,再是会话级上下文,最后是对话消息。这样尽可能多的会话能共用同一段前缀。

the order you put things in matters enormously¶5 ↗

02

状态更新走消息通道

状态更新走消息通道,不改 system prompt。

机制拆解

时间、文件改动这类会过期的信息,如果写回 system prompt 就会让整段前缀失效。Claude Code 的做法是在下一条 user message 或 tool result 里附一个 system-reminder 标签,前缀保持不动。作者也提到排序很容易被无意破坏:静态 prompt 里放了精确时间戳、工具定义顺序不固定、改了工具参数,都出过事故。

we add a <system-reminder> tag in the next user message or tool result¶15 ↗

03

中途换小模型反而更贵

缓存按模型隔离,会话中途换成便宜的模型反而可能更贵。

机制拆解

每个模型的缓存是各自独立的。例如 Opus 对话已经积累到 100k token,这时切到 Haiku,就得在 Haiku 上把整段前缀按未缓存价重新写一遍。真要换模型,正确做法是开一个 subagent,由主模型写一份交接消息再交给另一个模型;Claude Code 的 Explore agent 就是这样用 Haiku 的。

more expensive to switch to Haiku than to have Opus answer¶17 ↗

04

工具集合永不增删

工具集合始终固定,状态切换和按需加载都做成工具或存根。

机制拆解

工具定义属于缓存前缀,增删一个工具就会让整段对话的缓存失效。所以 Plan Mode 不靠替换成只读工具集来实现,而是把 EnterPlanMode 和 ExitPlanMode 做成工具,再用 system message 说明当前约束。顺带的好处是模型遇到难题时能自己进入计划模式。MCP 工具多的时候只发名称存根(defer_loading: true),模型通过 tool search 选中后才加载完整 schema,存根本身的内容和顺序始终不变。

we keep all tools in the request at all times¶21 ↗

05

压缩要共用父会话前缀

压缩之类的旁路计算必须共用父会话的前缀(cache-safe forking)。

机制拆解

直观的写法是单独发一次「总结一下」的调用,换一个 system prompt、不带工具。但这样从第一个 token 起前缀就不一样了,整段对话都按未缓存价计费,而且对话越长、越需要压缩,这一次就越贵。正确做法是沿用父会话完全相同的 system prompt、上下文和工具定义,接上原有消息后在末尾追加压缩指令,这样新增的 token 只有指令本身。代价是要预留一块 compaction buffer,给指令和摘要输出(图中约 20k token)留出空间。

use the exact same system prompt, user context, system context, and tool definitions¶28 ↗

06

命中率当可用性来盯

缓存命中率要像可用性(uptime)一样监控,命中率掉下来按事故处理。

机制拆解

命中率高,成本就低,订阅方案的限额也能给得更宽,所以 Claude Code 对缓存失效设了告警,命中率过低会宣布 SEV。作者说缺失率差几个百分点就会明显影响成本和延迟,但文中没有给出具体基线。

Monitor your cache hit rate like you monitor uptime¶36 ↗

03

批判性审视

厂商视角

命中率高直接关系到厂商自己的算力成本和订阅限额,所以「别中途切到便宜模型」这条建议也同时增加了大模型的用量。文中只举了一个对 Opus 有利的例子,没有给出对话长度和问题难度在什么范围内切换反而更省的临界点。

自己验证用自己定时任务的实际 usage,按当前价目分别算两种情况:留在大模型上继续用缓存,以及切到小模型重新写前缀。在不同上下文长度下各算一遍,找出盈亏平衡点。
数字口径

「缺失率差几个百分点就会明显影响成本」没有给基线命中率,也没有给分母口径(按请求算还是按 token 算)。图中缓存读取是原价十分之一,但文中完全没提缓存写入的溢价和缓存过期时间,这两项会改变长间隔场景(比如每周跑一次的任务)的计算。

自己验证查官方价目页里缓存写入、读取和过期时间的条款,对照 inference-scheduling 第 05 节已经整理的价目数据;在自己的日志里同时统计按 token 和按请求两种命中率。
可复现性

「会话中途增删工具或换模型会让缓存失效」「独立摘要调用拿不到缓存」这两条都可以低成本复现。

自己验证用 claude -p 加 JSON 输出跑同一段长上下文:第一组前后两次完全一致,第二组第二次多加一个 MCP server,第三组第二次换模型。比较三组的缓存读取和缓存写入 token。具体字段名先以 CLI 的实际输出为准。
遗漏

文章没有讨论 effort 或 thinking 档位的改变会不会打断缓存,而 L-001 方案是否可行正取决于这一点。

自己验证同一会话前后两轮只改 effort 参数,观察第二轮的缓存读取 token 是否接近前一轮的上下文长度。
遗漏

代价一侧几乎没展开:system-reminder 注入对输出质量的影响只用一句话带过(¶6);defer_loading 会多一次 tool search 往返,模型也可能选错或漏选工具;压缩摘要会丢信息。文中都没给数据。

自己验证在 harness-eval 的用例里对比两种配置下的工具选择正确率和端到端时延:MCP 工具全量加载,以及用存根延迟加载。
本篇未覆盖(2)
风格