我们分享了在 Claude Code 中优化提示词缓存的最佳实践,包括如何最有效地构建提示词、使用工具以及分层进行压缩。
- 分类Claude Code
- 产品Claude Code
- 日期2026 年 4 月 30 日
- 阅读时间5分钟
- https://claude.com/blog/lessons-from-building-claude-code-prompt-caching-is-everything
工程领域常说“缓存主宰一切”,这条规则同样适用于 AI 智能体。
像 Claude Code 这样长时间运行的智能体产品,之所以能够实现,靠的是提示词缓存。它允许我们复用之前往返请求的计算结果,从而显著降低延迟和成本。
在 Claude Code 中,我们整个运行框架都建立在提示词缓存之上。高提示词缓存命中率能降低成本,并帮助我们为订阅计划制定更慷慨的速率限制。因此,我们会监控提示词缓存命中率,如果命中率过低,就会触发告警。
以下是我们从大规模优化提示词缓存中学到的(往往反直觉的)经验。
为缓存设计你的提示词结构

提示词缓存通过前缀匹配机制工作——API 会缓存从请求开始到每个 `cache_control` 断点之间的所有内容。这意味着,你放置内容的顺序至关重要,你需要让尽可能多的请求共享同一个前缀。
提示词的结构方式不仅影响缓存命中率,也影响输出质量——提示词工程的基本功是这门学问的另一半。
最佳做法是:静态内容在前,动态内容在后。对于 Claude Code 来说,具体结构如下:
- 静态系统提示词和工具(全局缓存)
- CLAUDE.md(项目内缓存)
- 会话上下文(会话内缓存)
- 对话消息
通过这种方式,我们最大限度地提高了多个会话之间共享缓存命中的可能性。
但这种方法可能出奇地脆弱。我们曾因多种原因打破过这种排序,包括:在静态系统提示词中加入了详细的时间戳、以非确定性的方式打乱了工具定义的顺序,以及更新了工具的参数(例如,Agent 工具可以调用哪些智能体)。
使用消息进行更新
有时你放入提示词中的信息可能会过时,例如你设置了时间,或者用户修改了某个文件。你可能会想更新提示词,但这会导致缓存未命中,最终可能让用户付出相当高的代价。
不妨考虑能否在智能体下一轮交互中通过消息传递这些信息。在 Claude Code 中,我们会在下一条用户消息或工具结果中添加一个 `<system-reminder>` 标签,将更新后的信息提供给模型,这有助于保持缓存。
不要在会话中途切换模型
提示词缓存是模型独有的,这使得提示词缓存的运算逻辑相当反直觉。
例如,如果你与 Opus 的对话已经进行了 10 万 token,现在想问一个相当简单的问题,那么切换到 Haiku 实际上比让 Opus 回答更昂贵,因为我们需要为 Haiku 重建提示词缓存。
如果需要切换模型,最佳方式是使用子智能体;延续上述例子,你可以部署一个子智能体,让 Opus 准备一条“交接”消息给另一个模型,说明需要完成的任务。我们在 Claude Code 的 Explore 智能体中经常这样做,这些智能体使用的是 Haiku。
不要在会话中途添加或移除工具
在对话中途更改工具集是人们破坏提示词缓存最常见的方式之一。这看起来很直观——你应该只给模型你认为它当前需要的工具。但由于工具是缓存前缀的一部分,添加或移除工具会使整个对话的缓存失效。
使用计划模式围绕缓存进行设计
计划模式是一个围绕缓存约束设计功能的绝佳例子。直观的做法是:当用户进入计划模式时,将工具集替换为仅包含只读工具,但这会破坏缓存。
相反,我们始终将所有工具保留在请求中,并将 EnterPlanMode 和 ExitPlanMode 本身作为工具使用。当用户开启计划模式时,智能体会收到一条系统消息,说明当前处于计划模式以及相关指令:探索代码库、不编辑文件,并在计划完成后调用 ExitPlanMode。工具定义始终保持不变。
这还有一个额外的好处:由于 EnterPlanMode 是模型可以自行调用的工具,因此当模型检测到难题时,它可以自主进入计划模式,而无需破坏缓存。
使用工具搜索来延迟加载,而非移除
同样的原则也适用于我们的工具搜索工具。Claude Code 可能加载了数十个 MCP 工具,在每次请求中都包含所有这些工具成本高昂,但在对话中途移除它们则会破坏缓存。
我们的解决方案是:defer_loading。我们不移除工具,而是发送轻量级的存根(仅包含工具名称,并设置 defer_loading: true),模型可以在需要时通过工具搜索来“发现”这些工具。完整的工具架构仅在模型选中时才会加载。由于相同的存根始终以相同的顺序存在,这保持了缓存前缀的稳定性。
你也可以通过我们的 API 使用工具搜索工具来简化这一过程。
在不破坏缓存的情况下进行压缩

压缩发生在上下文窗口耗尽时。我们会总结到目前为止的对话,并利用该摘要继续新的会话。
压缩与提示词缓存的交互方式很容易出错。要压缩一段对话,你必须将完整的对话内容发送给模型,以便它能够撰写摘要。最简单的做法是发起一次独立的 API 调用,使用独立的系统提示词(例如“总结这段内容”),并且不附加任何工具,但这恰恰是成本陷阱所在。提示词缓存仅在请求的前缀与已缓存内容(从开头起逐字节匹配)完全一致时才生效。你的主对话在某个系统提示词和工具集下被缓存;而摘要调用则使用了不同的系统提示词且没有工具,因此前缀从第一个 token 开始就出现差异,缓存完全不生效。最终,你需要为发送的整个对话内容支付完整的、未缓存的输入费用——而且对话越长(即你越需要压缩),这一次调用的成本就越高。
解决方案:缓存安全的分支
当我们执行压缩时,我们使用与父对话完全相同的系统提示词、用户上下文、系统上下文和工具定义。我们将父对话的消息前置,然后在末尾将压缩提示词作为一条新的用户消息附加进去。
从 API 的角度来看,这个请求看起来与父对话的最后一次请求几乎相同——相同的前缀、相同的工具、相同的历史记录——因此缓存的前缀被重复使用。唯一的新 token 就是压缩提示词本身。
然而,这确实意味着我们需要保存一个“压缩缓冲区”,以便在上下文窗口中有足够的空间来容纳压缩后的消息和摘要输出的 token。
压缩虽然棘手,但幸运的是,你无需亲自吸取这些教训——基于我们从 Claude Code 中获得的经验,我们已将压缩功能直接构建到 API 中,因此你可以在自己的应用程序中应用这些模式。
经验教训
以下是我们发现的一些在构建智能体时优化提示词缓存的有用模式:
- 提示词缓存是一种前缀匹配。前缀中任何位置的任何更改都会使其之后的所有内容失效。围绕这一约束来设计你的整个系统。只要顺序正确,大部分缓存工作就能自动完成。
- 请使用消息而非修改系统提示词。你可能会想通过编辑系统提示词来进入计划模式、更改日期等操作,但实际上,更好的做法是在对话过程中将这些内容插入到消息中。
- 不要在对话中途更换工具或模型。应使用工具来建模状态转换(如计划模式),而不是更换工具集。采用延迟加载工具的方式,而非移除工具。
- 像监控服务正常运行时间一样监控你的缓存命中率。我们会对缓存失效发出告警,并将其视为事故处理。几个百分点的缓存未命中率就可能显著影响成本和延迟。
- 分支操作需要共享父级的前缀。如果你需要运行一个侧边计算(压缩、摘要、技能执行),请使用相同的缓存安全参数,这样就能在父级前缀上获得缓存命中。
Claude Code 从第一天起就围绕提示词缓存构建;为了在构建智能体时获得最佳效果,我们建议你也这样做。
立即开始使用 Claude Code。
本文由 Claude Code 团队的技术成员 Thariq Shihipar 撰写。