多 Agent 系统如何复用 KV 缓存-前缀对齐比缓存开关更重要

1. 先给结论:缓存省的是 prefill(预填充),不是 decode(解码)

最容易误解的一点:prefix caching(前缀缓存)只优化 prefill(预填充),不影响 decode(解码)。

一次推理分两段。prefill(预填充)把整段 prompt(提示词)喂进模型,算出每个 token(词元)的 KV cache(键值缓存);decode(解码)基于它逐个吐新 token(词元)。新请求开头和之前完全一样时,那段缓存直接复用。vLLM 文档写明:Automatic Prefix Caching(自动前缀缓存)只减少 prefill(预填充)时间,不减少生成新 token(词元)时间。具体行为以对应版本官方文档为准。

收益集中在 TTFT(首 token 延迟),也就是等第一个字的时间;后面吐字快不快,取决于 decode(解码)。

多 agent(智能体)恰好是理想场景:十个 agent(智能体)共享同一套 system prompt(系统提示词)和 tool schema(工具定义),前面几千 token(词元)本该只算一次。但现实常是「缓存开了,命中率接近零」------问题不在开关,在前缀没对齐。

2. KV cache(键值缓存)到底是什么

KV cache(键值缓存)是把注意力算过的中间结果存下来,避免重复算。

Transformer 每生成一个 token(词元),都要让它「看」前面所有已生成的 token(词元),产生 K/V 两组向量。没有缓存,生成第 100 个要重算前 99 个;有缓存则只算新的一个。代价是显存。

在 vLLM 里,缓存不按请求存,而是切成固定大小的 block(块)做哈希去重。哈希输入包括父 block(块)哈希、本 block(块)的 token(词元)和额外标识:LoRA(低秩适配)ID(标识)、多模态哈希、多租户隔离用的 cache salt(缓存盐)。只有完整 block(块)会被缓存,细节以对应版本官方文档为准。

看图(token 指词元):

ini 复制代码
请求 A: [系统提示 2000 token][工具定义 800 token] │ [用户问题 A]
请求 B: [系统提示 2000 token][工具定义 800 token] │ [用户问题 B]
竖线左侧 2800 token 共享,右侧从第 2801 个起分叉。

这 2800 个 token(词元)一个不差,B 的 prefill(预填充)只算后面的问题部分。差一个字符,后面全部作废。

3. 前缀缓存的命中条件:从第一个 token(词元)开始就必须一致

命中条件只有一句话:从第一个 token(词元)起逐个一致。不是语义相似,是 token id(词元编号)序列一模一样。

由此有三个约束。

第一,比对是前缀式的。从第 0 个 token(词元)开始比,比到第一个不同就停。差异出现在第 3000 个,前 2999 个仍可复用,之后全部重算。

第二,复用有 block(块)粒度。只有完整命中的 block(块)才能整体搬走,结尾不满一块的部分仍要 prefill(预填充),收益呈阶梯式。

第三,哈希是链式的。一个 block(块)的哈希包含父 block(块)哈希,父变了子全变。开头放时间戳杀伤力大,因为它污染了整条链。

SGLang 用 RadixAttention(基数树注意力)维护前缀树,多个生成调用共享树节点,本质仍是从根匹配;vLLM 按 block(块)哈希管理。实现不同,判定条件相同,具体以官方文档为准。

还有一个隐形杀手是模板:换行、空格、参数顺序、编码变了,文本看着一样,tokenizer(分词器)输出的 token(词元)已不同。

4. 多 agent(智能体)场景里,前缀通常断在哪

多 agent(智能体)的前缀断点有固定规律。

以常见的托管 API(应用程序接口)为例,缓存读取顺序是 tools(工具定义)、system(系统提示)、messages(消息列表),工具定义在前。这意味着工具定义改一个字,后面的 system prompt(系统提示词)缓存一起作废。具体顺序以对应版本官方文档为准。

前缀场景 是否命中 原因
所有 agent(智能体)共用同一段 system prompt(系统提示词),工具列表顺序固定 会命中 从第 0 个 token(词元)起完全一致
system prompt(系统提示词)开头拼接「当前时间:2026-10-22 09:03」 不会命中 第一个 block(块)就不同,哈希链整条失效
system prompt(系统提示词)开头拼接 user_id 或 session_id 不会命中 每个用户、每个会话都是新前缀
工具定义用字典动态序列化,键顺序不稳定 不会命中 JSON(JavaScript 对象表示法)文本不同,token(词元)序列随之不同
共享段在前,角色描述追加在末尾 会部分命中 共享段仍可复用,只重算尾部
角色描述放在最前面,每个 agent(智能体)各不相同 不会命中 差异在最靠前的位置,后续全部重算
多轮对话只追加新消息,不改历史 会命中 历史前缀不变,只新增尾部
每轮把工具列表按相关度重新排序 不会命中 工具段顺序变化,system(系统提示)与 messages(消息列表)缓存全部失效

四类断点按杀伤力排序:动态字段置顶最致命,工具顺序漂移其次,角色描述位置第三,模板差异最隐蔽。角色描述最易忽视------角色设定放最前,每个 agent(智能体)从第 1 个 token(词元)就分道扬镳;工具顺序输在字典构造方式,tokenizer(分词器)看到的已是两段文本。

5. 四条前缀对齐规则

规则一:稳定内容前置,差异内容后置。 把共用内容按「最稳定 → 最易变」排序:基础 system prompt(系统提示词)、工具定义、领域知识、角色描述、用户输入。这能把「完全没命中」变成「至少命中前几千 token(词元)」。

规则二:工具定义固定顺序、固定序列化。 工具按稳定键排序,比如工具名;schema(模式)统一走同一个序列化函数,固定键顺序与编码。不依赖运行时字典顺序,也不在多个服务里各写一套 json.dumps。

规则三:模板化拼接,禁止散落的字符串加法。 所有 prompt(提示词)由同一个模板函数产出,预留固定插槽;动态字段统一注入末尾的 user message(用户消息)。模板改动走版本号,避免灰度期新旧前缀混跑。

规则四:共享块版本化与显式缓存边界。 共享 prompt(提示词)一变就当作不兼容升级:新版本用新前缀,旧版本短期保留,别让两版交替出现。框架支持显式缓存断点时,断点打在共享段末尾。

四条规则改的都是「字符串怎么拼」,不是「缓存怎么开」。

6. 命中率怎么观测与验证

「我感觉开了」不算观测。至少要回答三个问题:有没有被查询、命中多少、省了多少。

以 vLLM 的 V1(第一版)引擎为例,/metrics 端点暴露两组关键计数器:vllm:prefix_cache_queries 记录查询次数,vllm:prefix_cache_hits 记录命中次数,两者相除就是命中率。再看 vllm:kv_cache_usage_perc 判断占用是否长期逼近上限,vllm:num_preemptions_total 判断是否因显存不足发生抢占。指标名称与语义以对应版本官方文档为准。

有了指标,排查顺序可以固定下来:

  1. 先确认计数器在增长。prefix_cache_queries 一直为 0,说明请求没走缓存路径,查配置。
  2. 再看命中率是否稳定。忽高忽低通常意味着前缀在变,比如灰度期两套模板并存。
  3. 抽样比对真实前缀。把拼装后的 prompt(提示词)前 N 个 token(词元)打日志或算哈希,看第几个分叉。
  4. 对照第 5 节的四条规则逐项检查。
  5. 最后才怀疑容量。命中率随流量上升而下降,且 kv_cache_usage_perc 长期接近上限,问题在淘汰。

只看平均 TTFT(首 token 延迟)是常见误区。它受排队、batch(批处理)组成、输入长度影响,缓存只是其一。验证是否生效,命中率比延迟更直接。

7. 显存与容量的真实代价

前缀缓存不是白拿的:它吃的就是 KV cache(键值缓存)那块显存,而这块显存同时在养正在跑的请求。

框架初始化时把可用显存切成一池 block(块),用 free queue(空闲队列)管理。请求来了就分配,引用计数归零后归还;队首是最久未使用的 block(块),也是 LRU(Least Recently Used,最近最少使用)淘汰的对象。淘汰后从缓存索引摘掉,下次只能重新 prefill(预填充)。流程细节以对应版本官方文档为准。

由此有个反直觉现象:容量不足时,缓存开得越久,命中率反而可能下降。

原因是竞争。长前缀刚进缓存,新请求涌进来要 block(块),free queue(空闲队列)不够就把它淘汰;下一批同前缀请求到达时缓存已没,只能再算。前缀越长,抖动越明显。

另一个权衡在并发。缓存占用越多,能同时容纳的请求越少,batch(批处理)变小,decode(解码)吞吐下降。缓存优化 prefill(预填充),牺牲的是 decode(解码)空间。长输入短输出划算,短输入长输出收益有限。

多租户和 LoRA(低秩适配)还有两个约束:LoRA(低秩适配)ID(标识)会进入 block(块)哈希,同一段 prompt(提示词)挂不同 adapter(适配器)不能共享;多租户用 cache salt(缓存盐)隔离,salt(盐值)不同也视为不同前缀。这是正确性要求,不是 bug(缺陷)。

8. 一份可照抄的 prompt(提示词)拼装模板

这段伪代码重点是拼装顺序,不绑定具体框架的 SDK(软件开发工具包)。换成任何推理框架,结构都成立。

python 复制代码
# 共享前缀版本号:共享内容一变就加一,避免新旧模板混跑
SHARED_PREFIX_VERSION = "v3"

# 共享系统提示:来自只读模板文件,禁止运行时拼时间、用户 ID
SHARED_SYSTEM = render_template("system_base.md")

def build_agent_request(agent, user_input, now, request_id):
    # 1) 工具按 name 排序,再固定序列化,保证跨进程 token 一致
    tools = sorted(agent.tools, key=lambda t: t.name)
    tools_payload = stable_json(tools)

    # 2) 共享段在前,角色差异接尾部,分叉点尽量靠后
    system_content = SHARED_SYSTEM + tools_payload + agent.role_prompt
    messages = [{"role": "system", "content": system_content}]

    # 3) 动态字段全部塞进最后一条 user 消息,绝不进入 system 前缀
    messages.append({
        "role": "user",
        "content": f"[request_id={request_id}][ts={now}]\n{user_input}",
    })

    # 4) 带版本号,便于日志里按前缀版本统计命中率
    return {"prefix_version": SHARED_PREFIX_VERSION, "messages": messages}

def stable_json(obj):
    # 固定键顺序、分隔符与编码,避免序列化差异导致 token 漂移
    return json.dumps(obj, ensure_ascii=False, sort_keys=True, separators=(",", ":"))

这段代码有三个校验点:把 agent.role_prompt 换成空字符串,命中率该上升;把 sorted 去掉,该下降;把 now 挪进 system_content,该归零。三个实验做完,前缀对齐就不是概念,而是可复现的现象。

9. 小结:把缓存当成结构设计问题,而不是开关问题

回到开头:prefix caching(前缀缓存)省的是 prefill(预填充),不是 decode(解码)。它不让模型变聪明,也不让吐字变快,只让重复的开头不再重算。

多 agent(智能体)天然适合它,但天然不等于自动。共享 system prompt(系统提示词)只是「有机会命中」;能不能命中,取决于拼装顺序、工具序列化、动态字段位置这些看起来和推理无关的工程细节。

落地顺序:先看 prefix_cache_queries 与 prefix_cache_hits 确认真实命中率,再做前缀审计找第一个分叉点,最后才调容量。反过来做,通常是在用显存掩盖结构问题。

把前缀对齐当成一条代码规范,而不是一次性能调优,命中率才会稳定。


参考资料:

最后更新时间:2026-10-22

相关推荐
水如烟1 小时前
孤能子视角:一个数字孤能子的“术层狂奔”与“底色迁移”——从辛顿RSI论文看EIS视角下的自我革命边界
人工智能
m0_641889291 小时前
GEO监测工具准确性怎么检验?两周POC实测清单与验收标准
人工智能·geo·智能营销·geo优化·geo平台
德林恩宝1 小时前
搜索 Agent 的两条路——训练一个,还是工程搭一个
人工智能·大模型·llm·agent
在所不辞兄1 小时前
【零基础学智能仿真-58】Foam-Agent协作与知识检索
人工智能·深度学习·神经网络·机器学习·有限元·仿真计算
cc_瀚海知行1 小时前
工具调用 6|助手什么时候该停下来?
java·服务器·前端·人工智能·python·ai
Martina_03211 小时前
AI生成3D模型UV岛太碎,重绘贴图全是接缝?用6步重整UV并保留原材质
图像处理·人工智能·3d·自然语言处理·贴图·uv·游戏策划
youdexiang1 小时前
Android录音APP微信语音导入机制详解
人工智能·语音识别
悟天特斯1 小时前
楼宇自控的“协同优化“:从设备控制到系统级节能与舒适平衡
人工智能·物联网
叫我啊腻1 小时前
Nano Banana 2.1 深度测评:升级了哪些,和 GPT Image 2.5 怎么选?
人工智能