W
AI-Wiki
CONCEPT

KV缓存友好型上下文

定义

KV缓存友好型上下文,是指把 Agent 的提示组织、工具描述、动作—观察轨迹和上下文更新方式,专门设计成更容易命中前缀 KV 缓存的一类上下文工程实践。

其核心目标不是单纯“少用 token”,而是让多轮迭代中尽可能多的前缀保持完全一致,从而复用模型已经完成的预填充结果,减少重复计算,降低首 token 延迟(TTFT)与推理成本。

在长链路 Agent 中,这种设计尤其重要,因为每一步决策都要把此前的大量上下文重新送入模型,而新生成的输出通常很短。

在本文档中的语境

Manus 联合创始人 Peak 总结的 Agent 上下文工程经验里,KV 缓存命中率被明确视为“生产级 AI 智能体最重要的单一指标”。原因不是抽象的性能洁癖,而是 Agent 的输入输出比例极端失衡。

Manus 的典型执行循环为例:用户发出任务后,Agent 会反复进行“基于当前上下文选择动作—执行动作—获取观察结果—把结果追加回上下文”的迭代。随着循环推进,上下文不断变长,但每次输出通常只是一个较短的结构化函数调用。

文中给出的经验数字是:Manus 的平均输入 token 与输出 token 比例约为 100:1。也就是说,成本和时延的主要压力不在解码,而在反复处理那段越来越长的输入前缀。

这时,前缀缓存能带来非常直接的收益。文中以 Claude Sonnet 为例说明:缓存输入 token 的价格约为 0.30 美元/百万 token,未缓存输入 token 则约为 3 美元/百万 token,差了 10 倍。因此,是否命中缓存,不只是“快一点”,而是会显著改变生产成本结构。

关键机制

保持系统提示前缀稳定

KV 缓存依赖“相同前缀可复用”的前提。由于大模型是自回归的,只要前缀中某个 token 发生变化,从那个位置开始,后续缓存通常都会失效。

因此,KV缓存友好型上下文 的第一原则是:让系统提示及其前缀尽可能稳定,尤其不要把高频变化的信息塞到开头。

文中点名的典型反例,是在系统提示开头加入当前时间戳,特别是精确到秒的时间戳。这样虽然模型知道“现在几点”,但几乎会让每次请求的前缀都不同,缓存命中率接近归零。

这条规则的实质是:凡是会频繁变化、却又不是每轮都必须置于最前面的信息,都不应放在缓存敏感前缀中。

上下文尽量只追加,不改写历史

在 Agent 循环里,上下文天然适合做成追加式日志:新动作、新观察结果不断附加到末尾。

KV缓存友好型上下文 强调应尽量保持这种“只追加、不回写”的结构,避免为了美观、压缩或重排而修改之前已经出现过的动作或观察。

原因很直接:一旦历史内容被改写,即使只改了很早位置的一个 token,也可能让后续大段缓存全部作废。

因此,这里的“只追加”不是编码风格偏好,而是与 KV 缓存机制直接耦合的工程约束。

序列化顺序必须确定且一致

即使语义上“还是同一个对象”,只要序列化结果不稳定,缓存一样会被静默破坏。

文中特别提醒:很多编程语言或库在序列化 JSON 对象时,并不保证键顺序恒定。如果同一份结构化数据这次输出为 a,b,c,下次输出为 b,a,c,模型看到的 token 序列就已经不同,缓存无法复用。

因此,KV缓存友好型上下文 不只要求内容一致,还要求字面结果一致。常见做法包括:固定字段顺序、固定数组顺序、固定空白和模板格式、避免不必要的随机措辞。

这里要注意一个边界:为了打破 Few-Shot 模仿惯性,某些局部动作—观察表述可以引入受控变化;但凡是希望参与前缀缓存复用的稳定前缀部分,序列化必须严格确定。换言之,“需要缓存的部分求稳定,不需要缓存的部分才谈适度扰动”。

按框架要求设置缓存断点

并非所有模型提供方或推理框架都会自动做增量前缀缓存。某些系统要求开发者显式插入缓存断点,告诉框架“从这里开始是一段值得缓存和复用的前缀”。

因此,KV缓存友好型上下文 的第四条原则是:按所用框架的机制显式设置缓存断点,而不是假设平台会自动处理。

文中强调,设置断点时至少要确保断点覆盖系统提示的结尾;同时还要考虑缓存过期问题,不能只在理想条件下设计。

这意味着缓存断点的位置既是性能问题,也是结构问题:如果断点切得太晚,前面本可复用的大前缀被浪费;如果切得不合理,则可能因为高变动内容夹在断点前而频繁失效。

自托管场景下的额外要求

如果使用 vLLM 一类框架自托管模型,还需要额外满足基础设施层面的前提。文中提到两点:

  • 必须确认前缀缓存或提示缓存功能已经开启。
  • 在分布式工作节点之间,要通过会话 ID 等方式把同一会话稳定路由到可复用其缓存的节点。

否则,即使应用层上下文设计得很稳定,请求如果不断落到不同节点,缓存收益仍会被大幅稀释。

为什么它对 Agent 比对话机器人更重要

普通聊天应用也能从 KV 缓存中获益,但长链路 Agent 的收益更大,因为它反复在“长输入、短输出”的不平衡模式下工作。

在这种模式中:

  • 每一轮都要重新处理越来越长的历史。
  • 输出常常只是短小的工具调用或结构化动作。
  • 一次任务可能包含几十轮迭代。

这使得前缀是否稳定,几乎直接决定系统是“可规模化”还是“成本失控”。在 Manus 的描述中,典型复杂任务平均需要约 50 次工具调用,若每轮都打碎缓存,累计时延和费用会非常可观。

设计细节与常见破坏因素

破坏缓存的高频元数据

最常见的问题不是大改动,而是一些开发者觉得“无伤大雅”的小变化,例如:

  • 每轮插入新的时间戳。
  • 给系统提示附加动态环境描述。
  • 在历史记录中回填状态字段。
  • 因日志清理而重写旧消息。
  • 同一对象在不同轮次采用不同字段顺序或不同格式化方式。

这些变化往往不会改变语义,却会改变 token 序列,从而让缓存命中率明显下降。

动态增删工具也会破坏前缀稳定

Manus 的经验里,工具定义通常位于上下文前部,常常出现在系统提示之前或之后。因此,如果在迭代过程中频繁动态加载、删除或替换工具定义,前缀就会发生变化,后续动作与观察的 KV 缓存也会一起失效。

这说明 KV缓存友好型上下文 不只约束“对话文本”,也约束工具清单、动作空间说明等结构化前缀。

也正因此,Manus 更倾向于保留稳定的工具集合,再通过解码时的 logits 掩码去限制当前可选动作,而不是直接从上下文里移除工具。这样可以在不破坏前缀的前提下控制行为。

压缩和摘要要考虑可还原性

当上下文过长时,压缩本身并不与 KV 缓存友好相冲突,但压缩方式会影响后续结构稳定性。