DeepSeek V4 KV-Cache 深度解析

概述

DeepSeek V4 之所以能实现极高的 KV-Cache 命中率,核心在于它改变了传统 KV-Cache 的存储策略:将计算最昂贵的长共享前缀压缩后持久化到 SSD,新请求到来时直接复用而非重新计算。

原始图片: 来源:小红书(基于 DeepSeek-V4 技术报告整理)


一、为什么 KV-Cache 是个问题?

Transformer 的推理过程

每次 LLM 推理分为两个阶段:

  1. Prefill(预填充):将输入 prompt 的所有 token 一次性送入模型,计算每个 token 的 K(Key)和 V(Value)状态
  2. Decode(解码):基于已有的 KV 状态,逐个生成新 token

问题在于:如果输入 prompt 很长(比如 64K tokens),prefill 阶段需要:

  • 计算 64K × 64K 的注意力矩阵(O(n²) 复杂度)
  • 为每一层、每个 head 存储完整的 K 和 V 矩阵
  • 对于 64K 长度的 prompt,全量 KV-Cache 可能占用几十到几百 GB 内存

传统方案的致命弱点

每个请求独立存储全量 KV → 内存很快耗尽 → 被迫淘汰旧缓存 → 命中率极低

传统 KV-Cache 存在内存中,无法持久化,服务器一重启就全没了。而且 GPU/显存容量有限,只能存很少量的缓存。


二、DeepSeek V4 的四步流程详解

第一步:第一个请求——预填充生成长共享前缀

输入序列的组成

[System Prompt] [Tool Schema] [Repo Context/Docs] [Conversation History] [New User Tokens]
     ←────────────────────── 长共享前缀 ────────────────────────→     ← 独特后缀 →

为什么要这样划分?

在 Agent 场景中,每次对话的输入有大量重复部分:

  • System Prompt:几乎不变的角色设定和指令
  • Tool Schema:工具的 API 描述,多个对话共享
  • Repo Context/Docs:代码仓库或文档上下文,同项目下共享
  • Conversation History:同一对话的历史记录
  • New User Tokens:只有用户新输入的这句是不同的

“长共享前缀”(Long Shared Prefix) = 前面 4 个部分,它们在不同请求之间高度重复。 “独特后缀”(Unique Suffix) = 最后用户新输入的部分,每次不同。

Prefill 阶段的计算

Model Prefill: "Compute attention over the long prefix and generate KV states"

Prefill 就是把整个输入序列送入模型,计算每一层的注意力(Attention),并为每个 token 生成对应的 K 和 V 状态。

为什么标注”这是最昂贵的部分”(The expensive part)?

  • 64K tokens 的 prefill 计算量远大于生成几个新 token 的 decode 阶段
  • 注意力计算是 O(n²),序列越长越贵
  • 如果有 100 个并发请求都有相同的 64K 前缀,传统方案就要重复计算 100 次

产物:三种压缩缓存

Prefill 完成后,DeepSeek V4 不是保存完整的 KV 状态,而是生成三种压缩格式的缓存产物:

产物全称压缩比用途
CSACompressed Sparse Attention4x中等压缩,保留较多细节
HCAHeavily Compressed Attention128x极高压缩,存最老的历史
SWASliding Window Attention1x(不压缩)精确状态,存最近窗口

第二步:压缩后的 KV 存储到 SSD

三种压缩技术详解

1. CSA(Compressed Sparse Attention)—— 4 倍压缩

4 tokens → 1 compressed token state
  • 原理:将每 4 个连续 token 的 KV 状态压缩成 1 个
  • 稀疏选择(Sparse Selection):不是简单平均,而是有选择地保留最重要的信息
  • 类比:就像把 4 帧视频压缩成 1 帧关键帧
  • 适用场景:中间时间跨度的上下文,需要一定精度但可以接受少量信息损失

2. HCA(Heavily Compressed Attention)—— 128 倍压缩

128 tokens → 1 compressed token state
  • 原理:将每 128 个 token 压缩成 1 个,压缩率极高
  • 为什么可以压缩这么多? 最老的上下文(比如 system prompt、几轮前的对话)对当前生成的影响较小,即使丢失部分细节也不太影响结果
  • 类比:就像把整章书的内容压缩成一段摘要
  • 适用场景:非常久远的历史信息,主要作用是保持整体语境

3. SWA(Sliding Window Attention)—— 不压缩的精确状态

Exact states for the most recent tokens
  • 原理:保留最近若干个 token 的完整、未压缩的 KV 状态
  • 为什么不能压缩? 最近的 token 对下一个生成的 token 影响最大,压缩会显著降低生成质量
  • 窗口大小:技术报告中提到的滑动窗口,通常覆盖最近几千个 token
  • 类比:短期记忆,需要精确回忆

为什么存到 SSD 而不是内存?

On-disk cache (SSD): 高容量、持久化、可跨请求复用
对比维度内存(GPU/DRAM)SSD
容量有限(几十 GB)很大(TB 级)
成本贵便宜
持久化断电丢失持久保存
速度极快较快(NVMe SSD 可达 7GB/s)

关键洞察:prefill 计算的时间远大于从 SSD 读取的时间。所以即使 SSD 比内存慢,从 SSD 加载压缩 KV 也比重新计算快得多。

压缩后的存储大小

对于 64K tokens 的长前缀:

  • 全量 KV:几十到几百 GB
  • 压缩后(CSA + HCA + SWA):远小于 1 GB(<< 1 GB)

缩小了 100~1000 倍,这就是为什么能在 SSD 上存储数千个前缀。


第三步:第二个请求到来——前缀匹配检测

匹配检测机制

"Prefix match detected → KV cache hit"

当新请求到来时:

  1. 提取输入序列的前缀(System Prompt + Tool Schema + Context + History)
  2. 计算前缀的哈希或指纹
  3. 在 SSD 缓存中查找匹配的指纹
  4. 如果命中,加载对应的压缩 KV 状态

匹配覆盖率:92% 的含义

图中示例展示了92% 的匹配覆盖率,分为三部分:

[■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■] 100%
 ←──────── 绿色: Matched prefix ────────→ ← 蓝色 → ←橙色→
    (可从缓存复用的部分)             (最后完整 (必须重算
                                      压缩块边界) 的不匹配尾部)
  • 绿色部分(Matched prefix):92% 的前缀完全匹配,可以直接从缓存加载
  • 蓝色部分(Last complete compression block):对齐到压缩块的边界。因为 HCA 是 128 tokens 一组、CSA 是 4 tokens 一组,所以最后一个完整压缩块之后的部分需要特殊处理
  • 橙色部分(Unmatched tail):不匹配的尾部,必须重新计算。可能是因为新请求比缓存的前缀稍微长了一点

为什么不是 100%?

  • 对话历史可能多了一轮
  • 压缩块的对齐问题(128 的整数倍、4 的整数倍)
  • 但即使只有 92% 命中,也比 0%(全重算)好得多

第四步:复用而非完全重计算

四步复用流程

On-disk cache (SSD) ──→ V4 Serving System ──→ Attention Engine
   [CSA] [HCA] [SWA]

Step 1:加载压缩的 CSA/HCA 前缀缓存

"Read the compressed prefix KV from disk"
  • 从 SSD 读取 CSA 和 HCA 的压缩 KV 状态
  • 因为这些状态已经被大幅压缩,读取时间很短
  • 加载后送入 Attention Engine

Step 2:恢复 SWA 或重新计算最近的几个 token

"Load the recent-window state or recompute the few latest tokens"
  • SWA 的精确状态可能已经过期(比如对话又多了几轮)
  • 可以从检查点恢复,或者直接重新计算最近这几个 token
  • 因为只涉及少量 token,计算成本很低

Step 3:重新计算不完整的压缩块

"Compute the small unmatched tail to reach the current point"
  • 就是第三步中橙色部分的 unmatched tail
  • 因为只涉及少量 token(不到一个压缩块),计算很快
  • 这一步将缓存状态”补齐”到当前请求的精确位置

Step 4:继续生成新 token

"Proceed to generate new tokens"
  • 现在 Attention Engine 拥有了完整的、对齐到当前请求的 KV 状态
  • 可以开始正常处理新后缀并生成回复
  • 跳过了一次完整的 64K token prefill!

三、传统 vs DeepSeek V4 对比

传统全量 KV-Cache 的问题

长前缀(64K tokens)
    ↓
全量 KV-Cache 存储(每个请求独立)
    ↓ 巨大(几十到几百 GB)
难以存储大量对话
    ↓ 内存压力 + 淘汰策略
实际命中率低

核心问题:

  1. 每个请求独立存全量 KV,内存浪费严重
  2. GPU 显存有限,存不了几个缓存
  3. 一旦缓存被淘汰,下次来又要全重算
  4. 没有持久化机制

DeepSeek V4 的压缩前缀缓存

同样的长前缀(64K tokens)
    ↓
压缩缓存(CSA + HCA + SWA)
    ↓ 非常小(<< 1 GB)
SSD 上可存数千个前缀
    ↓ 持久化 + 高容量
实际工作负载中命中率极高

核心优势:

  1. 100~1000 倍压缩比:存得更多
  2. SSD 持久化:重启不丢失
  3. 跨请求复用:不同对话共享同一前缀缓存
  4. 三级分层:精度与空间的智能平衡

四、为什么”命中率高得离谱”?

图中的总结:

“Why the hit rate looks ridiculous: repeated long prefixes + very compact compressed KV + disk-backed prefix reuse”

三个因素叠加:

  1. 重复的长前缀:Agent 场景下,System Prompt + Tool Schema + Context 几乎不变
  2. 极紧凑的压缩 KV:64K tokens → << 1 GB,存得下、读得快
  3. 磁盘持久化复用:跨会话、跨重启都能用

这三个因素单独拿出任何一个都有用,叠加在一起就产生了”高得离谱”的命中率。


五、关键技术术语表

缩写全称说明
KV-CacheKey-Value CacheTransformer 推理中存储的注意力 K 和 V 矩阵
CSACompressed Sparse Attention4 倍压缩,带稀疏选择
HCAHeavily Compressed Attention128 倍压缩,用于最老上下文
SWASliding Window Attention滑动窗口,不压缩的精确状态
SSDSolid State Drive固态硬盘,大容量持久化存储
PrefillPre-filling输入序列一次性送入模型的计算阶段
DecodeDecoding逐个生成新 token 的阶段
Hit Rate命中率缓存命中/总请求数的比例

六、对 Agent 系统的实际意义

在 AI Agent 场景中,这个设计的价值尤其明显:

  • System Prompt 通常占输入的 30-50%,且几乎不变
  • Tool Schema 可能包含数十个工具描述,所有对话共享
  • 代码仓库上下文可能有数万 token,同项目下完全重复
  • 多轮对话的历史记录随对话增长而变长

如果没有前缀缓存复用,每个请求都要重新计算这些重复部分,浪费大量计算资源。DeepSeek V4 的方案让这些只算一次、多次复用。


七、进阶讨论:Multi-Agent 场景与 KV-Cache 机制辨析

在实际部署 Multi-Agent 系统时,关于 KV-Cache 有几个关键的技术细节和架构决策点。

1. Multi-Agent 是否会降低命中率?

答案取决于系统设计,而非 Agent 数量。

  • Agent 类别有限(推荐):如果系统只有 5-10 个固定角色的 Agent(如“代码专家”、“产品经理”、“测试员”),它们的前缀(System Prompt + Tool Schema)是稳定的。KV-Cache 池只会缓存这几十个固定前缀,复用率极高,命中率不受影响。
  • 动态千人千面(不推荐):如果每个用户或每个任务都生成完全不同的 System Prompt,缓存池会被大量一次性前缀塞满,导致“缓存污染”,命中率下降。

2. 是否需要隔离 API 来保护缓存?

  • 场景 A:共享 API(单缓存池)

    • 优势:跨 Agent 共享公共部分(如相同的项目文档、基础指令)。
    • 劣势:如果 Agent 前缀差异大,会互相挤占缓存空间(Pollution)。
    • 结论:适合角色少、上下文共享度高的系统。
  • 场景 B:隔离 API(独立缓存池)

    • 优势:Agent A 的缓存不会被 Agent B 污染,命中率稳定在高位(80-90%+)。
    • 劣势:无法复用不同 Agent 间的公共前缀,管理多个 API Key。
    • 结论:适合角色差异大、各自独立的系统。

3. KV-Cache vs 传统 Memory

KV-Cache 与传统意义上的 Memory(如 Redis、向量数据库)完全不同:

维度KV-Cache传统 Memory / Vector DB
匹配方式精确前缀匹配(Token 级)Key 查找 / 语义相似度
顺序敏感性极高(差一个 Token 即中断)低 / 无
存储内容注意力计算的中间状态(KV 矩阵)数据本身或特征向量
智能程度无(不懂语义,只看序列)可能具备语义理解能力

4. “只存上一次请求”是误区

KV-Cache 不是短期记忆,而是一个**“前缀图书馆”**:

  • 容量巨大:利用 SSD 可存储数千个不同的前缀。
  • 长期有效:只要空间足够且频繁使用,前缀会长期驻留,不会被轻易淘汰。
  • 淘汰机制:通常采用 LRU(最近最少使用),淘汰的是“很久没来且不热门”的前缀,而不是“上一次”请求。


● main|UTF-8|Markdown金乌意 v1.0