← 返回题目列表

多个请求如何复用相同 Prompt 前缀?

中等 第 22 / 25 题 更新于 2026/09/18
大模型推理优化AI技术大模型面试题

简化版

多个请求共享完全相同的 Token 前缀时,可以只对该前缀做一次 Prefill,把生成的 KV Cache 块缓存起来;后续请求引用这些只读块,只计算各自不同的后缀。这通常称为 Prefix Caching 或 KV Cache Sharing。

关键不是“文本看起来一样”,而是 Token 序列及影响 KV 的执行条件一致。缓存键要绑定模型权重、Tokenizer、聊天模板、位置编码和适配器版本,并做租户权限隔离。它主要降低重复 Prefill 和 TTFT,不会减少每个请求后续 Decode 的成本。

详细版

典型场景包括统一系统提示、公共 Few-shot、相同长文档上的多次问答和 Beam Search 分支。实现上通常将前缀切成固定 Token 块,对块内容做哈希,命中后通过逻辑到物理块表共享;分叉位置之后使用 Copy-on-Write,避免一个请求修改另一个请求的缓存。

shared: [system][policy][document]
                          /--> [question A][decode A]
physical KV blocks -----+
                          \--> [question B][decode B]

收益取决于公共前缀长度、复用次数和命中率,同时受缓存占用与哈希查找成本影响。动态时间戳、请求 ID 或不同模板会使前缀过早分叉,应将动态字段后移。上线至少监控命中 Token 比例、节省 Prefill Token、TTFT、占用显存、淘汰率和跨租户访问审计。

完整版教学

1. 为什么前缀可以复用

因果注意力保证某个位置的隐藏状态只依赖它之前的 Token。若两个请求的前 k 个 Token 完全相同,且模型执行条件一致,那么这 k 个位置在每层产生的 K/V 也相同。

因此可以缓存一次计算结果:

KV(prefix + suffix) = KV(prefix) + compute_KV(suffix | prefix)

这里的加号表示逻辑拼接,不是数值相加。后缀仍需基于共享前缀继续计算。

2. 哪些场景收益最大

系统提示只有几十 Token、每分钟请求很少时,缓存管理可能得不偿失。长且稳定、重复频繁的前缀更有价值,例如数千 Token 的政策文档、多轮会话历史或固定 Few-shot 示例。

收益粗略与 公共前缀 Token × 重用次数 成正比。还应看流量是否集中在缓存存活期内,否则条目刚写入就过期,只有占用没有复用。

3. “相同”为什么必须到 Token 级

空格、换行、Unicode 规范化和聊天模板差异都可能改变 Token 序列。两个肉眼相似的 Prompt 若 Token ID 不同,不能直接共享对应 KV。

缓存键应基于模板展开和 Tokenize 之后的序列,而不是原始字符串。预处理版本也要纳入键,避免模板升级后命中旧缓存。

4. 哪些执行条件必须一致

同一 Token 前缀在不同模型权重下会生成不同 KV。LoRA Adapter、RoPE 配置、位置偏移和部分模型并行实现也可能改变结果。

缓存键维度不一致的风险
模型权重版本KV 数值完全不同
Tokenizer/模板Token 序列不同
Adapter 版本层输出不同
位置编码配置相同 Token 在不同位置含义不同
KV 数据类型布局或精度不兼容

采样温度不影响已给定前缀的 KV,通常无需进入键;但实现相关配置必须根据引擎验证。

5. 块级哈希如何工作

将 Token 前缀切成固定大小块,每个块的哈希包含父块哈希和当前 Token。只有从根开始连续命中的块才可复用,不能跳过中间差异。

h0 = hash(model_version)
h1 = hash(h0, tokens[0:16])
h2 = hash(h1, tokens[16:32])
h3 = hash(h2, tokens[32:48])

父哈希使相同局部 Token 出现在不同前缀路径时不会错误共享。哈希碰撞仍需用足够强的摘要或二次校验控制。

6. Copy-on-Write 为什么必要

共享前缀块应视为只读。两个请求在前缀末尾分叉后,各自分配新块;若最后一个共享块尚未填满,需要复制后再追加,不能原地写入。

引用计数记录物理块被多少序列使用。请求结束时只减少引用,计数归零才可回收到空闲池,从而避免提前释放其他请求仍使用的缓存。

7. Radix Tree 与块哈希的区别

Radix Tree 按 Token 前缀组织请求,天然支持最长前缀匹配;块哈希更容易与分页 KV 结合并做分布式索引。二者都需要处理插入、引用和淘汰。

选择取决于引擎数据结构与流量模式,不应只背名称。关键能力是找到最长安全公共前缀,并让物理 KV 块只存一份。

8. 如何提高实际命中率

把稳定内容放在 Prompt 前部,把用户 ID、时间戳、随机数等动态字段后移;固定 System Prompt 和工具 Schema 的序列化顺序;避免每次请求插入无意义的空白变化。

但不能为了命中率把权限上下文删除。若租户或用户权限会改变模型可见信息,应保留在缓存边界或使用独立命名空间,安全优先于复用率。

9. 会话缓存与公共前缀缓存

同一会话多轮请求可复用上一轮历史 KV,命中高但生命周期与会话绑定。公共模板或文档缓存跨请求复用范围更大,同时需要更严格的权限、版本和淘汰策略。

类型复用范围主要风险
会话 KV单会话后续轮次会话过期、状态一致性
公共模板多用户模板版本失效
私有文档同权限主体跨租户数据泄漏

不同类型最好使用独立命名空间和容量配额。

10. 多租户隔离如何保证

即使共享内容相同,缓存元数据、命中时序或错误配置也可能形成侧信道。对私有前缀应按租户、权限域或加密上下文隔离,不能仅凭内容哈希跨租户复用。

权限撤销、文档删除或租户下线时要主动失效相应条目,并保留审计记录。敏感场景可只共享公开系统模板,不共享用户数据。

11. 缓存淘汰怎样设计

缓存空间有限,可综合最近访问、访问频率、占用块数和节省的 Prefill 时间。大而极少复用的前缀可能不如多个短而高频的条目有价值。

benefit_per_byte = reuse_probability × saved_prefill_ms / kv_bytes

模型或模板发布新版本时应批量失效旧命名空间,而不是等待 LRU 自然淘汰,以避免错误命中并及时回收容量。

12. 如何估算收益

设公共前缀为 P Token,每秒命中 R 次,完整 Prefill 吞吐为 T Token/s,理想节省计算时间约为 P × R / T 个 GPU 秒每秒。实际还要扣除查找、块映射和缓存写入成本。

更直接的线上指标是“避免计算的 Prefill Token 数”和命中请求的 TTFT 降幅。请求命中率可能很高,但若每次只命中很短前缀,价值仍有限。

13. 如何测试与上线

测试应覆盖完全命中、部分块命中、首 Token 即不同、模型升级、Adapter 切换、权限变化、并发分叉和哈希碰撞模拟。命中与未命中的输出在确定性配置下应一致。

灰度记录缓存命中 Token 比例、TTFT P95/P99、额外显存、淘汰率、错误命中和租户审计。发现版本或隔离问题时必须能一键关闭缓存并清空对应命名空间。

前缀复用的红线不是命中率,而是只有在计算语义和权限域都一致时才允许共享。

14. 常见误区与追问

  • 误区:原始 Prompt 字符串相同就能复用。 应比较模板展开后的 Token 和执行配置。
  • 误区:前缀缓存能降低 Decode 成本。 它主要跳过重复 Prefill,后续生成仍需 Decode。
  • 误区:哈希相同即可跨租户共享。 权限命名空间必须先满足隔离要求。
  • 误区:动态字段放哪里都一样。 放在前部会让后续所有块无法命中。
  • 误区:缓存越大越好。 它会挤占活跃请求 KV,需要按收益淘汰。
  • 追问:为什么只能复用连续前缀? 后续位置的 K/V 已依赖之前全部 Token,中间差异会传播。
  • 追问:如何保证分叉后互不影响? 共享块只读,尾块使用 Copy-on-Write,并维护引用计数。

15. 加强记忆

  1. 先记前提:相同 Token 前缀产生相同 KV。
  2. 再记键:模型、Tokenizer、模板、Adapter、位置配置。
  3. 再记结构:块哈希或前缀树寻找最长连续命中。
  4. 再记写入:共享块只读,分叉使用 Copy-on-Write。
  5. 再记布局:稳定内容前置,动态字段后移。
  6. 再记红线:租户与权限域隔离,版本变化主动失效。
  7. 最后记指标:命中 Token、节省 Prefill、TTFT、显存和淘汰率。