SSD Nodes Learn 🎉 VPS $5.50/月起
指南 Matt Connor作者: Matt Connor

KV cache 与 prompt cache 有什么区别?

KV cache 是每个请求都要占用的 RAM 或 VRAM,可能导致模型加载失败;prompt cache 只影响延迟和费用,未命中时按全价计费。

KV cache 与 prompt cache:简短结论

KV cache 和服务提供商的 prompt cache 只共享一个词,几乎没有其他共同点。KV cache 是每个请求的工作内存。在单个请求的整个生命周期内,它都驻留在服务器的 RAM 或 VRAM 中,并且会随上下文长度和并发请求数增加而增长。服务提供商的 prompt cache 是一种计费和延迟优化功能。系统会将提示词中稳定不变的前缀存储在服务提供商的服务器上;再次发送该前缀时,可按折扣价格计费。

前者是您通过硬件购买的内存。后者是由其他方持有、并向您收取使用费用的内存。

实际差异比定义更重要。KV cache 可能耗尽;耗尽后,模型无法加载,或者请求会被拒绝。prompt cache 不会耗尽。您只能无法命中缓存,此时系统会静默地按全价计费。

KV cache 保存什么,以及它存在的原因

生成第 500 个 token 的 transformer 必须关注前面的 499 个 token。对于这些 token 中的每一个,每一层都需要一个 key 向量和一个 value 向量。如果每生成一个新 token 都重新计算全部向量,生成开销会随序列长度的平方增长,因此运行时会将它们保留下来。这个存储区就是 KV cache(key/value cache)。

它是每个请求独有的状态,因为它基于该请求的确切 token 序列构建。发送不同提示词的两个用户不能共享此缓存,除非运行时启用了 prefix caching;这是后文介绍的另一项功能。

服务过程分为两个阶段。Prefill 读取完整提示词并填充缓存,受计算能力限制。Decode 一次生成一个 token,并将其追加到缓存,受内存带宽限制。因此,当您在自己的设备上测量每秒生成的 token 数时,提示词处理和 token 生成会报告不同的速度。

KV 缓存占用多少内存?

不要去查厂商表格。对于任何模型,都可以通过以下算式重新计算缓存大小:

bytes per token = 2 * layers * kv_heads * head_dim * bytes per element

其中的 2 代表 key 和 value。其他数字都来自模型的 config.json,可在模型的 Hugging Face 页面上查看。

以 Llama 3.1 8B 为例。其配置列出 num_hidden_layers 为 32,num_key_value_heads 为 8。hidden_size 为 4096,分布到 32 个注意力头后,每个注意力头的维度为 128。在 f16 中,每个元素占用 2 字节:

2 * 32 * 8 * 128 * 2 = 131072 bytes = 128 KiB per token

将结果乘以请求使用的上下文长度,再乘以同时运行的请求数。

ChartLlama 3.1 8B KV cache at f16, in GiB
The data behind this chart
[
  {
    "label": "2k context",
    "one_request_gib": 0.25,
    "four_requests_gib": 1
  },
  {
    "label": "8k context",
    "one_request_gib": 1,
    "four_requests_gib": 4
  },
  {
    "label": "32k context",
    "one_request_gib": 4,
    "four_requests_gib": 16
  },
  {
    "label": "128k context",
    "one_request_gib": 16,
    "four_requests_gib": 64
  }
]

上下文长度为 8k 时,缓存占用 1 GiB。上下文长度为 32k 时,缓存占用 4 GiB,与 4-bit 权重本身处于同一数量级。使用模型完整的 128k 上下文时,单个请求占用 16 GiB;如果 4 个请求都填满上下文,则占用 64 GiB。权重没有变化,变化的只有缓存。

分组查询注意力(GQA)显著降低了这个数值。Llama 3.1 8B 有 8 个 key/value 头为 32 个 query 头提供服务,因此每 4 个 query 头共享一组已存储的 key/value。对于 num_key_value_heads 等于 num_attention_heads 的模型,在参数量相同的情况下,缓存占用量会达到 4 倍。在假设两个 8B 模型的服务成本相同之前,先检查这个字段。

为什么在 2k 下可以运行的模型拒绝在 32k 下加载

这是因为运行时会在模型加载时预留 KV cache,其大小取决于您配置的上下文长度,而不是实际发送的提示词长度。Ollama 的默认上下文窗口为 4096 tokens。将其提高到 32k,意味着在生成第一个 token 之前,系统就需要额外分配 4 GiB。

OLLAMA_CONTEXT_LENGTH=32768 ollama serve

在交互式提示符中,可以为当前会话设置相同的参数:

ollama run llama3.1:8b
/set parameter num_ctx 32768

不同技术栈的报错形式不同。vLLM 会在启动时检查计算结果,并拒绝运行:

ValueError: The model's max seq len (131072) is larger than the maximum number of tokens that can be stored in KV cache (78336). Try increasing `gpu_memory_utilization` or decreasing `max_model_len` when initializing the engine.

仅使用 CPU 的 VPS 不会执行此类检查,因为这只是普通系统 RAM 分配。此时,内核的 out-of-memory killer 会终止该进程,并将证据写入内核环形缓冲区:

dmesg -T | grep -i "killed process"

如果其中一行包含您的服务进程名称,说明该主机承诺分配的内存超过了实际可用内存。解决方法是缩短上下文,而不是增大 swap 文件:每生成一个 token,系统都必须从磁盘读取分页到磁盘的 KV cache,因此生成速度会慢到无法使用。如何选择合理的数值,请参阅我们的 Ollama num_ctx 和上下文长度指南

并发如何影响数字

每个正在处理的请求都需要自己的 KV 缓存。这是许多容量规划遗漏的关键点。4 个用户各自持有 32k 上下文时,彼此合计需要 16 GiB,此外还要为模型权重预留内存。

不同运行时对此的处理方式并不相同。Ollama 和 llama.cpp 在加载模型时会预留您指定的上下文容量,因此无论是否实际使用,这部分内存都会被提交。vLLM 会将内存池拆分为固定大小的块,并随着请求增长逐块分配,因此一个 500-token 的请求只会占用相当于 500 个 token 的空间。无论采用哪种方式,内存池的容量都是有限的。内存池满后,新请求会排队,而不是立即执行。排队对响应时间的影响见自托管 LLM 可服务多少个并发用户

缩小 KV 缓存的 4 种方法

  1. 降低上下文长度。这是影响最大、通常成本最低的调整项。大多数聊天工作负载都不会接近 32k。
  2. 对缓存本身进行量化。Ollama 的 OLLAMA_KV_CACHE_TYPE 默认为 f16,也接受 q8_0(占用的内存约为一半)和 q4_0(占用的内存约为四分之一)。llama.cpp 对应的选项是 -ctk q8_0-ctv q8_0
  3. 选择键/值头数量更少或层数更少的模型。下载 40 GB 的权重前,先阅读 config.json
  4. 同时处理更少的请求,将其余请求排队。

q4_0 下,Llama 3.1 8B 每个 token 的占用量会从 128 KiB 降至约 32 KiB,因此 32k 上下文的成本约为 1 GiB,而不是 4 GiB。这种节省并非没有代价。键和值会以更低的精度存储,因此在保留该设置前,请使用您自己的提示词比较输出结果。

Provider 提示词缓存实际带来的收益

Provider 提示词缓存是另一种产品,其计费单位也不同。您标记一个稳定的前缀,由 Provider 存储该前缀。之后,重复使用完全相同前缀的请求将按折扣价格计费,而不是按完整输入价格计费。

截至 2026 年 8 月,Anthropic 公布的倍率如下:5 分钟缓存写入的费用是基础输入 token 价格的 1.25 倍;1 小时写入的费用是 2 倍;缓存读取的费用是 0.1 倍。将一个 20,000 token 的系统提示词代入这些数值,成本结构就很清楚了。

ChartCost of a 20,000 token prefix, in base input token equivalents
The data behind this chart
[
  {
    "label": "No caching, every call",
    "billed_token_equivalents": "20,000"
  },
  {
    "label": "First call, 5 minute cache write",
    "billed_token_equivalents": "25,000"
  },
  {
    "label": "First call, 1 hour cache write",
    "billed_token_equivalents": "40,000"
  },
  {
    "label": "Every later call, cache hit",
    "billed_token_equivalents": "2,000"
  }
]

按算术来理解。首次调用时,5 分钟写入的额外费用相当于 5,000 个 token:25,000,而未使用缓存直接发送时的费用是 20,000。在缓存窗口内的每次后续调用都按 2,000 计费,而不是按 20,000 计费,节省 18,000。因此,从第二次调用开始,5 分钟缓存就能节省成本。

1 小时缓存的取舍不同。写入时按 40,000 计费,相当于额外支付 20,000 个 token,因此必须在 1 小时内命中两次后才开始节省成本。这取决于您的流量模式,而不是模型本身。完整计算方法以及如何选择缓存窗口,请参阅 Claude 提示词缓存的盈亏平衡计算

有两个细节决定请求是否能命中缓存。首先,低于模型最小长度的前缀不会被缓存,而且不会返回错误。截至 2026 年 8 月,文档规定 Claude Opus 5 的最小长度为 512 个 token,Claude Sonnet 5 的最小长度为 1,024 个 token。较短的请求会正常处理。其次,缓存生命周期从写入或读取该条目的请求开始时计算,每次读取都会在不增加费用的情况下刷新生命周期。因此,繁忙的端点可以让 5 分钟缓存无限期保持有效。每 10 分钟调用一次的端点则每次都要支付写入溢价,始终无法获得缓存收益。

请检查响应,不要直接假定缓存已命中。usage 对象会报告 cache_creation_input_tokenscache_read_input_tokens。如果每次调用的读取次数都为 0,说明您一直在购买写入,却没有获得缓存读取。

两个缓存的交汇点

长系统提示词是两者的交汇点,并且会同时在两端产生费用。

在本地,20,000-token 的系统提示词在使用 f16 的 Llama 3.1 8B 服务器上约占用 2.4 GiB 的 KV cache;每个包含该提示词的并发请求都会单独占用这些缓存。在远程端,同一前缀只需写入一次缓存,之后每次调用的输入费用为原来的 0.1 倍。本地成本随用户数量增长。远程成本随流量增长,并会在空闲期间重置。

本地有一项功能看起来像提供商的提示词缓存,因此经常与其混淆:前缀缓存。vLLM 文档将自动前缀缓存描述为缓存“现有查询的 KV cache,这样新查询如果与某个现有查询共享相同前缀,就可以直接复用该 KV cache”。llama.cpp 服务器默认会为每个 slot 保留提示词缓存,--cache-reuse N 设置它尝试复用的最小块大小。

前缀缓存节省的是 prefill 计算。20,000-token 的系统提示词只需处理一次,而不是每次请求都重新处理,因此首 token 延迟会明显降低。在 vLLM 中,共享块会被复用,而不是重复存储,因此内存占用也会改善。但它不会减少当前仍处于活动状态的 token 所需的缓存容量。让权重在请求之间常驻内存是相关但独立的优化手段,详见让 Ollama 模型在请求之间保持加载状态

自行服务器上需要测量的指标

在目标上下文长度下加载模型,然后读取实际数值,不要只相信估算结果。

ollama ps
nvidia-smi --query-gpu=memory.used,memory.total --format=csv
free -g

ollama ps会列出已加载的模型、模型大小,以及模型是在 GPU 还是 CPU 上运行。原本预计完全放在 GPU 上的模型如果显示存在 CPU 分配,说明 KV cache 将模型的一部分挤到了 CPU,生成速度也会相应下降。nvidia-smi会给出实际 VRAM 数值,free -g则会在仅使用 CPU 的 VPS 上提供相同信息。逐步提高上下文长度,每次重新加载,然后观察数值变化。自行计算的结果应与报告的数值大致接近。如果两者不一致,差值通常来自运行时自身的计算缓冲区,而不是公式错误。

如果这些数值让您倾向于选择不太愿意租用的硬件,可以在GPU VPS 与 API token 的对比中查看按 token 付费的比较结果。

FAQ

KV 缓存与提示词缓存是一回事吗?

不是。KV 缓存是服务进程内按请求分配的内存,用于保存当前上下文中每个 token 的 key 和 value 向量。它位于 RAM 或 VRAM 中,并会在请求结束时释放。提供商的提示词缓存是一项计费功能,会将稳定的提示词前缀存储在提供商的基础设施中;再次发送该前缀时,按较低费率计费。KV 缓存耗尽会导致模型无法加载。没有命中提示词缓存只会增加账单和首 token 时间。

为什么我的模型能以 2k 上下文加载,却在 32k 时失败?

因为运行时会在加载时分配完整的 KV 缓存,其大小取决于配置的上下文长度,而不是你实际发送的提示词长度。对于 f16 的 Llama 3.1 8B,缓存大小为每个 token 128 KiB,因此 2k 上下文需要 0.25 GiB,32k 需要 4 GiB。两种情况下权重都能装入内存。失败的是缓存预留。vLLM 会将此报告为 ValueError,其中包含它能够存储的最大 token 数,并建议增大 gpu_memory_utilization 或减小 max_model_len。在仅使用 CPU 的服务器上,内核的 out-of-memory killer 会直接终止该进程;你可以使用 dmesg -T | grep -i "killed process" 确认这一点。

如何计算我的模型需要多大的 KV 缓存?

将 2、层数、key/value 头数量、头维度和每个元素的字节数相乘。结果就是每个 token 所需的字节数。然后将其乘以上下文长度和并发请求数。从模型的 config.json 中读取层数和头数量。f16 或 bf16 使用每个元素 2 字节。q8_0 缓存的大小约为其一半,q4_0 约为其四分之一。

提示词缓存会减少我自己的服务器所需的内存吗?

提供商的提示词缓存不会减少你的硬件需求,因为缓存存储在提供商一侧。本地对应功能是前缀缓存,vLLM 和 llama.cpp server 都支持该功能。它会复用共享前缀已经计算出的 key 和 value 向量,从而减少 prefill 计算并缩短首 token 时间。在 vLLM 中,共享块会被复用,而不是重复创建,因此内存占用也会降低。但这两项功能都不会减少当前正在处理的 token 所需的缓存,因此上下文长度和并发数的计算仍决定最低内存需求。

只发送一次的提示词值得缓存吗?

不值得。写入缓存的费用高于普通输入;截至 August 2026,5-minute 选项的费用是基础费率的 1.25 倍。因此,如果前缀在该时间窗口内不会再次发送,缓存只会造成直接损失。相同前缀会重复使用时,缓存才有价值,例如较长的系统提示词,或你准备就其提出多个问题的文档。检查 API 响应中的 cache_read_input_tokens,确认请求命中了缓存,而不是为写入缓存付费。