Claude Code 为什么这么顺?Anthropic 最新复盘:真正撑住它的不是模型,而是缓存

0 阅读10分钟

Claude Code 为什么这么顺?Anthropic 最新复盘:真正撑住它的不是模型,而是缓存

大家好,我是晓刘,欢迎来到我的公众号。 本文由人工整理、撰写,并做了必要校准。

最近 Anthropic 发了一篇技术复盘,主题很直白:他们是怎么构建 Claude Code 的。

但看完之后你会发现,文章真正想讲的不是“Claude Code 多会写代码”,而是另一件更底层的事:

Agent 产品能不能跑得稳,关键不只在模型,而在 Prompt Caching。

Claude Code 这类工具和普通聊天机器人不一样。

普通聊天可能问几句就结束。
但编程 Agent 往往会在一个项目里连续工作很久:读文件、查依赖、改代码、跑测试、修错误、再回头理解上下文。

如果每一轮请求都把前面所有内容重新算一遍,成本会很高,延迟也会越来越明显。

所以 Anthropic 的结论很直接:

没有缓存,就很难有今天这种长时间运行的 Claude Code。

缓存不是优化项,而是 Agent 的地基

很多人听到“缓存”,第一反应是性能优化。

就像页面加载慢了,加个缓存;数据库压力大了,加个缓存;接口重复请求多了,加个缓存。

但在 Claude Code 里,Prompt Caching 的位置要高得多。

Anthropic 在官方博客里提到,他们会监控 prompt cache hit rate,也就是提示词缓存命中率。一旦命中率异常下降,甚至会像处理线上事故一样触发告警。

这说明缓存对 Claude Code 来说不是锦上添花,而是基础设施。

原因也很简单。

Claude Code 是长上下文产品。一个 session 里可能有几十轮交互,每一轮都需要带上前面的项目背景、工具定义、会话状态和历史消息。

如果没有缓存,模型每次都要重新处理一大段重复上下文。

结果就是:

延迟变长;
成本变高;
额度消耗更快;
长任务体验变差。

Prompt Caching 的价值,就是让模型复用已经处理过的前缀内容。

说白了,前面那些没变的内容,不要每次都重新算。

最核心的原则:前缀匹配

Prompt Caching 的底层逻辑,其实可以用一句话概括:

它靠前缀匹配。

Anthropic 官方解释是,API 会从请求开头开始,把内容缓存到指定的 cache_control 断点。下一次请求时,如果前缀和之前一致,就可以直接复用缓存。

这听上去简单,但对 Agent 架构影响非常大。

因为只要前缀中间某个地方变了,后面的缓存就可能失效。

所以 Claude Code 的 prompt 组织方式非常讲究顺序。

大致可以理解成:

最稳定的内容放最前面;
项目级内容放中间;
会话状态继续往后;
每轮新增的对话消息放最后。

Anthropic 在官方博客中给出的结构是:

  1. 静态 system prompt 和工具定义
  2. 项目里的 CLAUDE.md
  3. 当前 session 的上下文
  4. 持续增长的对话消息

这套顺序背后的逻辑很简单:

越不容易变化的东西,越应该靠前。

因为它越靠前,被多个请求复用的概率就越高。

一个小改动,就可能让缓存全断

Prompt Caching 最麻烦的地方,也在这里。

它很强,但也很脆。

比如你在静态 system prompt 里塞了一个精确到秒的时间戳。
看起来只是一个小字段,但每次请求时间都不同,前缀就对不上了。

再比如工具定义顺序不稳定。
这次是 A、B、C,下次变成 B、A、C。
内容一样,但排列不同,缓存也可能失效。

再比如你在会话中途改了工具参数。
哪怕只是一个字段变化,前缀也不再相同。

这就是 Agent 产品和普通应用很不一样的地方。

普通应用里,动态更新配置可能是灵活;
但在 Prompt Caching 里,频繁变动前缀就是灾难。

所以 Claude Code 的设计思路是:能不动 prompt,就不要动 prompt。

信息变了怎么办?放进消息里,不要改 prompt

那问题来了。

如果时间变了、文件状态变了、用户刚刚改了代码,难道不告诉模型吗?

当然要告诉。

但 Anthropic 的做法不是去修改 system prompt,而是把这些变化放到下一轮消息里。

比如 Claude Code 会用类似 <system-reminder> 的方式,把新的状态塞进 user message 或 tool result。

这样做的好处是:

系统 prompt 不变;
工具定义不变;
缓存前缀不被破坏;
模型仍然能拿到最新信息。

这里其实有一个很重要的设计分层:

prompt 是稳定地基,message 是流动信息。

地基不要频繁动。
变化都走消息层。

这点对做 Agent 产品的人非常有参考价值。

中途换模型,可能反而更贵

还有一个很反直觉的点:不要轻易在 session 中途换模型。

很多人会觉得,这不是很合理吗?

复杂任务用 Opus;
简单问题切 Haiku;
这样不是更省钱吗?

但在 Prompt Caching 里,事情没那么简单。

Anthropic 官方博客提到,prompt cache 是和模型绑定的。

也就是说,你在 Opus 上积累下来的缓存,切到 Haiku 后不能直接复用。新模型需要重新建立缓存。

如果一个会话已经跑到 100k tokens,再为了一个简单问题切小模型,可能并不省钱,反而更贵。

这也是 Claude Code 的一个关键策略:

主会话尽量保持同一个模型。

如果真的要用小模型处理子任务,更好的方式是开子 Agent。

子 Agent 有自己的上下文和缓存,做完之后只把结果交回主会话。这样不会破坏主链路上的缓存。

这也是为什么现在很多 Agent 系统都在强调“主 Agent + 子 Agent”架构。

它不只是任务拆分,也是在保护缓存结构。

工具集也不要中途乱动

另一个容易踩坑的地方是工具。

直觉上,我们可能会觉得:

当前任务只需要 3 个工具,那就只给模型 3 个工具。
等它需要别的工具时,再把新工具加进来。

听上去很节省 token。

但 Claude Code 的经验恰恰相反:

不要在 session 中途增删工具。

因为工具定义也是缓存前缀的一部分。

你加一个工具、删一个工具、调整一个工具定义,都会改变前缀,导致缓存失效。

这就出现了一个很有意思的取舍:

多放一些工具定义,会多花一点 token;
但中途改工具集,可能让整个缓存链重建。

后者代价通常更大。

所以 Claude Code 更倾向于保持工具集稳定,而不是为了眼前一点 token 开销频繁调整。

Plan Mode 的设计,本质也是为了缓存

Claude Code 的 Plan Mode 很典型。

直觉上,进入规划模式后,应该把写文件、执行命令这类工具拿掉,只保留阅读和搜索工具。

但 Anthropic 没这么做。

因为一旦工具集变化,缓存就断了。

他们的做法是:工具集保持不变,然后把 EnterPlanModeExitPlanMode 本身设计成工具。

进入 Plan Mode 后,通过 system message 告诉模型当前只能规划、不能修改文件。退出时再调用对应工具。

这样既能实现规划模式,又不破坏缓存前缀。

更有意思的是,因为进入 Plan Mode 本身是一个工具调用,模型可以在发现问题复杂时主动进入规划状态。

这就是围绕缓存约束做产品设计。

不是先做功能,再想办法补缓存;
而是先知道缓存边界,再设计功能形态。

MCP 工具太多怎么办?延迟加载

现在很多 Agent 都会接 MCP。

问题是,MCP 工具一多,schema 也会变得很大。

如果把几十个工具的完整定义全塞进 prompt,token 开销会很重;
但如果中途按需添加工具,又会破坏缓存。

Anthropic 给出的方案是:延迟加载。

一开始只放轻量级工具说明,也就是 stub。模型知道有这个工具,大概知道它能做什么,但不会一开始就拿到完整 schema。

等模型真的需要用某个工具,再通过 tool search 拉完整定义。

这个设计很像图书馆目录。

你不用把所有书搬到桌上。
先看目录,确定需要哪本,再去书架拿。

对 Agent 来说,这样既控制了 token,又尽量保持缓存前缀稳定。

上下文满了,压缩也不能乱来

长会话跑久了,迟早会遇到 context window 不够的问题。

这时候需要 compaction,也就是把前面的长对话压缩成摘要,然后继续往下跑。

但这里也有一个缓存陷阱。

最简单的做法是另起一个请求,让模型“总结一下前面的对话”。

问题是,如果这个压缩请求用了新的 system prompt,或者没带原来的工具定义,那它从第一个 token 开始就和主会话不一样。

缓存完全复用不了。

Anthropic 的做法叫 cache-safe forking。

简单理解就是:压缩请求也尽量复用主会话的同一套 system prompt、用户上下文、工具定义和历史消息,只是在最后追加一条压缩指令。

这样从 API 视角看,压缩请求和主会话前缀基本一致,缓存还能继续命中。

这也是为什么 compaction 看似只是“总结上下文”,实际是一个很考验架构设计的动作。

真正的启发:缓存是一种架构约束

看完 Anthropic 这篇复盘,最值得记住的其实不是某个技巧,而是一种思维方式:

Prompt Caching 不是最后加上的优化,而是 Agent 产品从第一天就要考虑的架构约束。

它会影响 prompt 怎么排;
影响 system message 和 user message 怎么分层;
影响模型能不能中途切换;
影响工具集能不能动态变化;
影响 Plan Mode 怎么设计;
影响 MCP 怎么加载;
影响长上下文怎么压缩。

这也是 Claude Code 能长时间运行、保持流畅体验的关键。

很多人做 Agent,第一反应是堆模型、堆工具、堆上下文。

但 Anthropic 这篇文章提醒我们:真正能跑起来的 Agent,往往不是上下文越多越好,而是上下文结构要稳定、可复用、可缓存。

尤其是做编程 Agent。

项目说明、工具定义、代码结构、会话状态、历史消息,这些东西如果乱放,模型可能还能回答,但成本和延迟会越来越难控制。

所以对于正在做 Agent 产品、MCP 工具、AI 编程助手的人来说,这篇文章的价值很直接:

不要把缓存当优化项。
把它当系统边界。
从第一天就围绕它设计。

这可能才是 Claude Code 真正跑顺的原因。


参考来源:
Anthropic 官方博客:Lessons from building Claude Code: Prompt caching is everything
Claude Docs:Prompt caching