Prompt Caching 作为一等约束
来源:grapeot,2026-04-04
核心判断
Prompt caching 在成熟 harness 中是 viability constraint(可行性条件),不是成本优化。
它同时决定:
- 系统的成本基线(cache hit vs miss 有 10 倍成本差距)
- 交互延迟(128K token prompt,cache hit 500ms vs miss 13秒)
- 哪些系统架构能够存在(sub-agent 并行、speculation 模式)
为什么 Cache Discipline 反向塑造 Harness Design
缓存匹配基于严格的前缀比对,精确到 token 级别。哪怕改了一个空格,从改动位置往后的所有内容都无法命中。
这个约束渗透到多个子系统:
- Compaction 顺序:从尾部删而非从头部删(OpenClaw PR #58036)
- Tool definitions 排列:必须确定性排序,动态加载的抖动会导致 cache break
- 图片裁剪时机:延迟裁剪优于激进裁剪
- Sub-agent 参数传递:必须共享与父进程一致的 cache key
实践原则(来自 Manus 团队)
- Keep prefix stable
- Make context append-only
- Mask tools don't remove them(通过 logit masking 控制工具可用性,而非列表增删)
Sub-agent 的隐性成本
每个 sub-agent 启动时建立独立的 API 会话,主 agent 精心维护的缓存对 sub-agent 完全无效。短生命周期的 sub-agent 意味着一次缓存冷启动。
Auto-Caching API 机制
Claude Messages API 支持两种缓存模式:
Block-Level Caching(显式断点)
在任意消息块上放置 cache_control: {type: "ephemeral"},告诉 Claude 缓存该断点之前的所有内容块。缓存命中通过向后搜索最多 20 个块实现,内容必须完全匹配(一个字符差异即 miss)。
{
"role": "user",
"content": "C",
"cache_control": {"type": "ephemeral"}
}
Request-Level Auto-Caching(推荐)
在请求顶层放置单个 cache_control 参数,自动将断点移动到最新可缓存块。随着对话增长,断点自动前移,无需手动管理:
{
"cache_control": {"type": "ephemeral"},
"messages": [
{"role": "user", "content": "A"},
{"role": "assistant", "content": "B"},
{"role": "user", "content": "C"}
]
}
Cache-Friendly Prompt 设计
来自 Claude Code 团队(@trq212)的实践建议:
- 避免编辑历史消息:修改历史会打破从该点往后的所有缓存
- 追加优先于替换:新内容放在末尾,而非修改已有块
- 保持系统提示词和工具定义稳定:这些是缓存前缀的核心部分
- 结构化分隔:使用明确的角色标签和格式,让模型更容易识别重复模式
Claude Code 团队的规模化实践 (2026-04-30)
来源:Claude Blog — Prompt Caching is Everything
Claude Code 团队(Thariq Shihipar)公开了他们围绕 prompt caching 构建整个 harness 的经验。核心原则:cache hit rate 是 SEV 级别指标 —— 他们对此运行告警,过低时直接宣布严重事故。
四层缓存结构
Claude Code 的 system prompt 按稳定性分层排列:
- Static system prompt & Tools — 全局缓存(所有会话共享)
- CLAUDE.md — 项目级缓存(同一项目内所有会话共享)
- Session context — 会话级缓存
- Conversation messages — 动态增长,不缓存
这种排序最大化跨会话的缓存共享。但极其脆弱 —— 团队曾多次打破该顺序,原因包括:
- 在静态 system prompt 中加入详细时间戳
- 非确定性打乱 tool definition 顺序
- 更新 tool 参数(如 Agent tool 可调用的子 agent 列表)
用 messages 传递更新,不要改 prompt
当 prompt 中的信息过时(如时间变化、用户修改文件),直觉是更新 system prompt,但这会导致 cache miss。
Claude Code 的做法:在下一轮的用户消息或 tool result 中加入 <system-reminder> 标签传递更新信息。这样既让模型获得新信息,又保持了缓存前缀的稳定性。
不要中途换模型
Prompt cache 是模型级别的 —— 不同模型不共享缓存。一个反直觉的例子:对话已到 100k tokens,想切换到 Haiku 回答一个简单问题,结果比继续用 Opus 更贵,因为需要为 Haiku 重建整个缓存。
正确做法:用 subagent 做模型切换。让 Opus 准备一份 "hand-off" 消息给另一个模型,由 subagent 在新模型上继续。Claude Code 的 Explore agents(使用 Haiku)就是这样工作的。
不要中途增删工具
这是最常见的缓存断裂原因。工具定义是缓存前缀的一部分,增删任何一个工具都会使整个对话的缓存失效。
Plan Mode 的缓存友好设计:直觉上,进入 plan mode 时应该只保留只读工具,但这会破坏缓存。Claude Code 的做法是始终保持全部工具在请求中,把 EnterPlanMode 和 ExitPlanMode 本身设计成工具。用户切换 Plan Mode 时,agent 收到 system message 说明当前处于 Plan Mode 及相应指令。工具定义从不改变。
附带好处:因为 EnterPlanMode 是一个工具,模型检测到难题时可以自主进入 plan mode,完全不需要缓存断裂。
defer_loading 替代移除工具:Claude Code 可能加载数十个 MCP 工具,全部包含在每个请求中很贵,但中途移除又会破坏缓存。解决方案是发送轻量级 stub(仅工具名 + defer_loading: true),模型通过 tool search 在需要时"发现"它们。完整 schema 只在模型选中后才加载。这样缓存前缀始终保持稳定。
缓存安全的 Compaction(Cache-safe Forking)
上下文窗口满时的 compaction(摘要压缩)极易出错。简单做法是另起一个 API call,用不同的 system prompt(如 "summarize this")且不带工具,但这就是成本陷阱:summarization call 的前缀与主对话不同(system prompt 和工具集都不同),从第一个 token 就分叉,没有任何缓存命中。对话越长、越需要 compaction,这个调用就越贵。
Claude Code 的解决方案:compaction 时使用与父对话完全相同的 system prompt、user context、system context 和 tool definitions。将父对话的消息历史 prepend 进去,然后把 compaction prompt 作为新的 user message append 到末尾。
从 API 视角看,这个请求与父对话的最后一次请求几乎完全相同 —— 相同前缀、相同工具、相同历史 —— 因此缓存前缀被复用。唯一新增的 token 是 compaction prompt 本身。
代价:需要预留 "compaction buffer",确保上下文窗口有足够空间容纳 compact message 和 summary output tokens。
Anthropic 已将这些经验直接 built into API,开发者无需自己实现。
度量先于优化
Claude Code 源码中的 promptCacheBreakDetection.ts 追踪缓存断裂的来源:system prompt 变了?工具列表顺序变了?历史消息被修改?
Manus 团队将 cache hit rate 称为生产 AI agent 最重要的单一指标。
无法度量的东西无法改进。 Claude Code 团队进一步将其提升到 SEV 级别 —— cache hit rate 告警不足时直接宣布严重事故。
五条核心原则
- Prompt caching 是前缀匹配。 前缀中任何位置的改动都会使之后所有内容失效。围绕这个约束设计整个系统。
- 用 messages 替代 system prompt 改动。 进入 plan mode、更新日期等操作应插入 conversation messages,而非修改 system prompt。
- 不要在对话中途换工具或换模型。 用工具建模状态转换(如 plan mode),用 defer_loading 替代移除工具。
- 像监控 uptime 一样监控 cache hit rate。 几个百分点的 miss rate 会极大影响成本和延迟。
- Fork 操作必须共享父前缀。 side computation(compaction、summarization、skill execution)应使用与父对话一致的 cache-safe 参数。
Claude Code 从 day one 就围绕 prompt caching 构建;构建 agent 时建议你也这样做。
2026-04-27 价格信号
DeepSeek 宣布全系列 API 的 input cache hit 价格降到原来的 1/10。该价格变化强化了本页核心判断:prompt caching 不是附属优化,而是决定 agent 架构是否经济可行的约束。
对长上下文 agent 来说,稳定前缀、append-only context、确定性工具定义和 cache hit 度量会直接影响单位任务成本。缓存命中价格下降后,能够维持高 hit rate 的 harness 会获得更大经济优势。
关联
- harness-engineering/overview — Harness Engineering 总览
- Agent Memory Engineering(Nic) — Prefix cache 作为记忆系统设计的硬经济约束
- 上下文管理 — 上下文管理
- claude-code/overview — Claude Code 的 prompt caching 实现