摘要

1) 一句话总结 Claude Code 的底层架构深度依赖提示词缓存,通过严格的前缀匹配顺序、延迟加载工具、消息传递状态以及复用父级前缀等工程设计,最大化缓存命中率,从而显著降低智能体的运行成本和延迟。

2) 关键要点

  • 监控与定级:提示词缓存命中率是核心指标,命中率过低在 Claude Code 中会被直接定级为严重故障(SEVs)。
  • 前缀匹配架构:缓存基于前缀匹配,必须遵循“静态内容在前,动态内容在后”的顺序(全局静态提示词和工具 项目上下文 会话上下文 对话消息)。
  • 通过消息传递更新:当上下文信息(如时间、文件修改)发生变化时,不修改系统提示词,而是在下一轮消息中通过 <system-reminder> 标签传递更新,以保持缓存有效。
  • 使用子智能体切换模型:由于缓存与特定模型绑定,跨模型切换应通过子智能体(subagents)完成(例如让 Opus 生成交接消息给 Haiku),而不是直接切换主对话模型。
  • 状态转换代替工具增删:对话中途增删工具会破坏缓存。在“计划模式”中,系统保留所有工具,将 EnterPlanModeExitPlanMode 作为工具提供,并通过系统消息限制模型编辑文件。
  • 工具延迟加载:为避免加载大量 MCP 工具产生高昂成本或移除工具破坏缓存,系统发送带有 defer_loading: true 的轻量级存根,仅在模型通过 ToolSearch 选中时才加载完整 Schema。
  • 缓存安全的压缩:在上下文耗尽进行总结压缩时,使用与父对话完全相同的系统提示词、工具和历史记录,仅在末尾追加压缩提示词,以复用父级前缀缓存。
  • 预留压缩缓冲区:系统设计中需预留压缩缓冲区(compaction buffer),以确保上下文窗口有足够空间容纳压缩指令和总结输出的 Token。

3) 风险与隐患

  • 破坏前缀顺序的风险:在静态系统提示词中加入详细时间戳、以非确定性方式打乱工具顺序,或更新工具参数,都会破坏缓存前缀导致缓存失效。
  • 直接更新提示词的成本风险:直接在提示词中更新过时信息会导致缓存未命中(cache miss),进而增加用户的 API 成本。
  • 模型切换的成本倒挂风险:在积累了大量 Token 的对话中(如 10 万 Token 的 Opus 对话),为了简单问题切换到小模型(如 Haiku),由于需要重新构建提示词缓存,实际成本可能比继续使用大模型更昂贵。
  • 独立压缩调用的成本风险:如果将对话压缩作为独立的 API 调用(使用不同的系统提示词且不带工具),将无法匹配主对话的缓存前缀,导致需要为所有输入 Token 支付全额费用。

正文

工程界常说“缓存主宰一切”,这条法则同样适用于智能体产品。像 Claude Code 这类长时运行的智能体系统之所以可行,核心就在于提示词缓存:它能复用前序轮次的计算,显著降低时延与成本。

Claude Code 团队把提示词缓存当作底层约束来设计整个执行框架。他们不仅持续监控缓存命中率,还将低命中率视为严重故障处理,因为这会直接影响订阅计划的成本结构与可提供的速率额度。

提示词缓存的工作方式是前缀匹配:API 会缓存从请求起点到各个 cache_control 断点之前的内容。因此,请求结构顺序极其关键,目标是让尽可能多的请求共享同一前缀。实践上应遵循“静态在前、动态在后”的排列:

  1. 静态系统提示词与工具定义(全局缓存)
  2. 项目级上下文(项目内共享)
  3. 会话级上下文(会话内共享)
  4. 当前对话消息

这套顺序一旦被破坏,缓存命中会迅速下滑,而且往往是由看似微小的改动触发,例如在静态提示词里加入细粒度时间戳、以非确定性顺序输出工具定义、或临时调整工具参数。

当上下文信息发生变化时,直觉上会想直接改系统提示词,但这通常会触发缓存失效。更稳妥的做法是把变化放进后续消息中。在 Claude Code 的实践里,会通过 <system-reminder> 在下一轮消息或工具结果中注入更新信息,从而保留可复用前缀。

缓存还与模型绑定,这会带来反直觉的成本现象。例如当 Opus 对话已积累到 10 万 Token,即便问题很简单,切换到 Haiku 也可能更贵,因为需要重建新模型的缓存前缀。若确需切换模型,建议通过子智能体交接任务,让主链路保持缓存稳定。

在对话中途增删工具也是常见的缓存破坏点。工具定义本身属于缓存前缀的一部分,因此临时移除工具会让整个会话缓存失效。

计划模式:围绕缓存设计

计划模式是“围绕缓存做产品设计”的典型案例。直觉方案是进入计划模式时只保留只读工具,但这会破坏缓存。Claude Code 的做法是始终保留全量工具,并把 EnterPlanModeExitPlanMode 也设计成工具,通过系统消息约束行为边界。这样既不破坏缓存,也允许模型在检测到复杂问题时自主切入计划模式。

工具搜索:延迟加载而不是移除

当 MCP 工具很多时,每次都发送完整定义成本很高,但中途移除又会破坏缓存。对应方案是延迟加载:请求里先放稳定的轻量工具存根(defer_loading: true),模型需要时再通过 ToolSearch 拉取完整 Schema。这样前缀稳定,成本也可控。

压缩与缓存安全分叉

上下文窗口耗尽时需要压缩对话并续接新会话。若把压缩作为一个独立调用执行,且系统提示词和工具集与主对话不同,就无法复用原有缓存,会显著抬高输入成本。

Claude Code 的做法是进行缓存安全分叉:压缩请求与父对话保持同一系统提示词、同一上下文结构、同一工具定义,并复用父会话历史,仅在末尾追加压缩指令。这样新增 Token 主要来自压缩指令本身。与此同时,系统还要预留压缩缓冲区,以确保上下文窗口能够容纳压缩输入与摘要输出。

五条实践原则

  1. 将提示词缓存视作前缀约束来设计系统。
  2. 状态更新尽量通过消息注入,不改系统提示词。
  3. 不在对话中途切模型、切工具集;用状态工具表达流程切换。
  4. 把缓存命中率当作可观测核心指标持续监控。
  5. 分支计算任务尽量复用父级前缀,降低旁路操作成本。

如果你在构建智能体产品,提示词缓存不该是补丁层面的优化,而应是从第一天起就进入架构决策的底层约束。

关联主题