LLM 提示词缓存深度实战:把 API 成本砍到 1/10 的完整方案
每次请求都重复发送几千 token 的静态指令,是 LLM 应用最大的隐性成本黑洞。本文讲透 Prompt Caching 提示词缓存的底层原理与生产级实战,帮你把 API 账单实实在在降下来。
开篇:从一个真实业务场景说起
你在负责一个企业级 RAG 客服系统:用户问「退款流程是什么」,系统先检索知识库,拼出 4000 token 的上下文,连同 800 token 的系统提示一起发给大模型。一次请求 4800 token 的输入,其中约 4600 token 是重复的静态内容——每个用户、每个问题几乎都一样。
假设日活请求 10 万次,输入 token 按 DeepSeek 官方非峰值价 $0.44/M 计算,光输入成本一天就是 4800 × 10万 / 100万 × $0.44 ≈ $2112,一个月超过 6 万美元。而如果你让重复部分命中缓存,输入成本直接打 1/31——同样的流量,一个月省下近 6 万美元。
这不是天方夜谭,这就是 Prompt Caching(提示词缓存) 的威力。2024 年 Anthropic 率先推出显式缓存,随后 OpenAI、Google、DeepSeek 全部跟进,2026 年缓存已成为各家 API 的默认能力。但绝大多数开发者只把缓存当作「平台自动做的事情」,既不理解它的命中规则,也不知道如何主动设计来提升命中率——于是钱白白流走。
这篇文章要解决的就是这个问题:从 KV Cache 的底层原理讲起,拆解四大厂商的缓存机制差异,给你 6 个可运行的生产级代码示例,最后用 DeepSeek 官方 2026 年 8 月的最新定价做一次量化收益实测。
技术背景与核心概念扫盲
要理解提示词缓存,先要理解大模型生成文本的机制。
自回归生成(Autoregressive Generation)
今天的主流大模型都是 Decoder-only Transformer,采用自回归方式生成:模型一次只预测下一个 token(token 是文本的最小单元,可以是单词、子词或标点),然后把这个 token 拼回输入,再预测下一个,循环往复直到输出结束标记。生成一句 100 个 token 的回答,就要做 100 次前向推理。
预填充与解码(Prefill / Decode)
一次请求在服务端被分为两个阶段:
- 预填充(Prefill):把用户输入的全部 token 一次性并行计算,得到每个位置的注意力中间结果,这个阶段计算密集、耗时与输入长度成正比。
- 解码(Decode):逐 token 生成输出,每步只处理最新一个 token,但需要与之前所有 token 做注意力计算,这个阶段访存密集。
关键问题来了:解码阶段每一步都要和之前所有 token 算注意力。如果每次请求都从头计算,那多轮对话的每一次追问、RAG 的每一次检索,都要把同样的静态上下文重新算一遍——这就是成本与延迟的最大来源。
KV Cache:解决重复计算的关键
Transformer 的注意力机制中,每个 token 会生成三个向量:Query(查询)、Key(键)、Value(值)。当前 token 的 Query 要与序列中所有 token 的 Key 做点积(决定"该关注谁"),再对 Value 加权求和。
聪明的优化出现了:Key 和 Value 只由 token 自身决定,与后续 token 无关。所以一旦某个 token 的 K、V 算过,就可以存起来复用——这就是 KV Cache(键值缓存)。有了它,生成下一个 token 时只需计算新 token 的 Q、K、V,用新 Q 去和缓存的全部 K、V 做注意力即可,彻底避免重复计算。
从 KV Cache 到 Prompt Caching
KV Cache 原本是服务端推理引擎(如 vLLM)内部的性能优化。但当它被商业 API 化之后,就产生了计费层面的巨大价值:
如果两个请求的前缀 token 完全相同,那么前缀部分的 KV Cache 可以直接复用——服务端省了算力,于是 API 厂商给「缓存命中的输入 token」大幅降价。
这就是 Prompt Caching(提示词缓存),Google 生态叫 Context Caching,DeepSeek 叫 Context Caching on Disk,本质是同一件事:跨请求复用相同前缀的 KV Cache,并把这个省下来的钱以折扣形式返还给调用方。
它的演进脉络也很清晰:2024 年 8 月 Anthropic 首发(显式 cache_control 断点)→ 2024 年 9 月 OpenAI 上线自动缓存 → Google Gemini 推出显式 Context Caching → DeepSeek 全自动磁盘缓存 → 2026 年各厂开始支持细粒度控制(OpenAI 的 prompt_cache_key、GPT-5.6+ 显式断点等)。到 2026 年,缓存已是所有主流 API 的标配能力,只是命中规则各不相同。
底层原理深度拆解
前缀匹配:缓存命中的唯一法则
提示词缓存有一个几乎通用的核心规则:只有完全一致的 token 前缀才能命中。
请求 A: [系统提示] [知识库内容] [问题1] → 生成 回答1
请求 B: [系统提示] [知识库内容] [问题2] → 前缀命中!
请求 C: [系统提示] [知识库内容] [问题2] [追问] → 更长前缀,仍命中
请求 D: [系统提示] [问题2] ← 没有知识库 → 前缀不一致,全部 miss
原因很直接:KV Cache 是按 token 位置存储的。第 n 个 token 的 K、V 只对「第 n 个位置」有效。请求 B 的第 14800 个 token 与请求 A 完全一致,才能复用请求 A 缓存下来的第 14800 个位置的 KV。只要前缀里任何一个 token 不同(哪怕多一个空格、换一个标点),从那个位置开始全部失效。这就是后面要讲的「前缀漂移」陷阱的根源。
graph TD
A[用户请求 tokens] --> B[Tokenizer 分词]
B --> C{与已有 KV Cache 前缀匹配?}
C -- 是 --> D[直接复用缓存 K/V<br/>缓存命中 cached_tokens]
C -- 否 --> E[从头计算 Prefill<br/>缓存未命中]
D --> F[仅计算新 token 的注意力<br/>输入成本按折扣计费]
E --> F
F --> G[解码生成输出]
G --> H[新生成的 K/V 写入缓存]
缓存命中的判定粒度:各家不一样
「完全一致的前缀」具体怎么判定,不同厂商实现不同,这直接决定你的命中率:
DeepSeek:缓存前缀单元(Cache Prefix Unit) DeepSeek 官方文档明确说明,受 SWA(Sliding Window Attention,滑动窗口注意力) 机制影响,缓存前缀是独立完整的单元,后续请求必须完整匹配某个单元才能命中,不存在「部分命中」。落盘时机有三个:
- 请求边界落盘:每次请求的用户输入结束位置、模型输出结束位置各产生一个缓存前缀单元;
- 公共前缀检测落盘:检测到多次请求存在公共前缀时,把公共前缀固化为独立单元;
- 固定 token 间隔落盘:长输入/长输出按固定 token 数截取单元,避免长前缀因一直没到结束位置而永远无法缓存。
注意一个反直觉的细节:请求 A 输入是 A+B,请求 B 输入是 A+C,第一次 B 不会命中(因为 A+B ≠ A+C);但系统会识别出公共前缀 A 并落盘,第三次请求 A+D 就能命中 A 了。所以 DeepSeek 的命中率是「越用越高」的。
OpenAI:自动缓存 + 显式断点
OpenAI 对 1024 token 以上的请求全自动缓存,无需任何参数。系统会在「最新一条消息之前」自动放置缓存断点。2026 年的新版本(GPT-5.6 及更新)支持通过 prompt_cache_breakpoint 标记在 content 块上手动放置断点,配合 prompt_cache_options 指定模式(implicit 保留自动断点 / explicit 只用你指定的)和 TTL。
Anthropic:显式 cache_control 断点
Anthropic 是唯一不自动缓存的主流厂商:必须你在 content 块里显式打 cache_control 标记,模型才知道从哪里开始缓存。而且 cache write(写入缓存)要额外收费 25%,cache read(命中读取)折扣 90%。这意味着用 Anthropic 时,缓存设计错了反而更贵。
Google Gemini:显式 Context Caching + 存储费 Gemini 的缓存需要显式创建缓存对象,并且按「token × 缓存时长」收取存储费(类似租用显存),命中读取的输入 token 有折扣。适合长会话、长文档反复查询的场景。
vLLM(自托管):Automatic Prefix Caching 自己部署开源模型时,vLLM 通过 APC(Automatic Prefix Caching,自动前缀缓存) 实现同样的效果:把 KV Cache 按固定大小分块(Block),对每个块的内容做哈希,请求到来时用哈希匹配已有块,命中则直接复用,未命中才计算,并用 LRU(最近最少使用)策略驱逐旧块。它不省 API 费用,但能把吞吐提升数倍、延迟降低一个数量级。
成本模型:钱到底省在哪
缓存命中的输入 token 会以折扣价计费。汇总 2026 年 8 月的主流计费逻辑:
| 厂商 | 触发方式 | 命中输入折扣 | 隐藏费用 |
|---|---|---|---|
| OpenAI | 全自动(1024+) | 输入约 5 折 | 无 |
| Anthropic | 显式 cache_control | 读取约 1 折 | 写入加价 25% |
| DeepSeek | 全自动(默认开启) | 约 1/31(V4 系列) | 无 |
| Gemini | 显式缓存对象 | 读取有折扣 | 存储按 token×时长收费 |
| vLLM | 引擎自动(自托管) | 省算力不省费用 | 显存占用 |
数据说明:OpenAI/Anthropic/Gemini 折扣比例以各家官方价格页为准,会随模型迭代调整;DeepSeek 比例由 2026-08-26 官方定价页实测计算(详见「性能实测」章节)。
sequenceDiagram
participant U as 应用服务
participant G as API 网关 (LiteLLM)
participant P as 大模型 API
U->>G: 请求1: [静态系统提示+知识库+问题A]
G->>P: 转发请求1 (全量计算 Prefill)
P-->>G: usage.prompt_tokens=4800, cached=0<br/>KV 已写入缓存
G-->>U: 回答A + usage 明细
U->>G: 请求2: [静态系统提示+知识库+问题B]
G->>P: 转发请求2 (前缀命中缓存)
P-->>G: usage.cached_tokens=4600 折扣计费<br/>延迟大幅下降
G-->>U: 回答B + cached_tokens 明细
Note over U,G: 每次请求都解析 usage 字段,<br/>把 cached_tokens 纳入成本监控
手把手实战落地
下面进入实战。环境要求:Python 3.10+,安装依赖:
# 安装各家 SDK 与统一网关
pip install openai anthropic litellm python-dotenv
统一在 .env 中配置密钥:
OPENAI_API_KEY=sk-xxxx
ANTHROPIC_API_KEY=sk-ant-xxxx
DEEPSEEK_API_KEY=sk-xxxx
示例 1:OpenAI 自动缓存——触发并验证命中
OpenAI 不需要任何额外参数,请求满足 1024+ token 即自动缓存。关键是读取 usage.prompt_tokens_details.cached_tokens 验证是否真的命中:
import os
from openai import OpenAI
from dotenv import load_dotenv
load_dotenv()
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
# 构造一个超过 1024 token 的静态系统提示(重复文本凑长度)
static_prompt = ("你是企业智能客服,请基于以下知识库回答用户问题。"
"知识库条款:" + "本公司承诺 7 天无理由退货,运费由买家承担。" * 120)
messages = [
{"role": "system", "content": static_prompt},
{"role": "user", "content": "退货政策是什么?"},
]
def ask(question: str):
"""发送一次请求并打印缓存命中情况"""
resp = client.chat.completions.create(
model="gpt-4o",
messages=[*messages[:-1], {"role": "user", "content": question}],
temperature=0.2,
)
usage = resp.usage
cached = usage.prompt_tokens_details.cached_tokens if usage.prompt_tokens_details else 0
print(f"输入={usage.prompt_tokens} 缓存命中={cached} 输出={usage.completion_tokens}")
return resp.choices[0].message.content
# 第一次请求:全量计算,缓存未命中
ask("退货政策是什么?")
# 第二次请求:前缀完全一致,命中缓存
ask("退款需要多长时间?")
运行结果示例(第二次请求 cached_tokens 应远大于 0):
输入=1802 缓存命中=0 输出=36
输入=1802 缓存命中=1780 输出=42 ← 前缀命中,输入成本按折扣计
核心要点:第二次请求只改了 user 消息,system 前缀完全相同 → 命中。如果改动 system 里的任何字符,cached 会直接归零。
示例 2:OpenAI 精细控制——缓存键与 24 小时长缓存
对共享同一份大上下文的场景(比如所有用户共用一份文档分析系统提示),2026 年的 OpenAI 提供两个参数:
prompt_cache_key:路由提示,把相同 key 的请求路由到同一后端,提升命中率;prompt_cache_retention:缓存保留时长,"in_memory"(默认,约 5–10 分钟)或"24h"(把 KV 张量卸载到 GPU 本地存储,可跨较长时段命中)。
from openai import OpenAI
client = OpenAI()
resp = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system",
"content": "你是一位资深法律文书分析师。" + "以下是完整合同文本:" + "合同条款样例。" * 300},
{"role": "user", "content": "请总结关键条款"},
],
extra_body={
"prompt_cache_key": "legal-contract-v3", # 同 key 请求路由到同一后端
"prompt_cache_retention": "24h", # 缓存保留 24 小时
},
)
# 校验缓存命中
cached = resp.usage.prompt_tokens_details.cached_tokens
print(f"缓存命中 tokens: {cached}")
示例 3:Anthropic 显式缓存——cache_control 断点
Anthropic 必须手动指定缓存位置。把最稳定、最长的静态内容放在最前面,并在其 content 块上打 cache_control 标记:
import anthropic
client = anthropic.Anthropic()
resp = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=512,
messages=[
{
"role": "user",
"content": [
# 静态系统提示:打上缓存断点,写入缓存(收费 25% 加价)
{
"type": "text",
"text": "你是企业知识库助手。" + "以下是完整产品手册:" + "产品说明。" * 200,
"cache_control": {"type": "ephemeral"}, # 显式断点标记
},
# 动态用户问题:放在断点之后,不参与前缀缓存
{"type": "text", "text": "这款产品的保修期是多久?"},
],
}
],
)
# Anthropic 的 usage 里有 cache_creation / cache_read 两个字段
u = resp.usage
print(f"写入缓存(cache_creation)={u.cache_creation_input_tokens}")
print(f"命中读取(cache_read)={u.cache_read_input_tokens}")
注意:cache_control 必须放在你想缓存的那段内容上,且缓存的是「从请求开头到该标记位置」的前缀。标记之后的动态内容每次请求都要重新计费。另外 Anthropic 的写入加价 25% 意味着:如果同一前缀只被请求 1~2 次,显式缓存反而亏钱——一般同一前缀被复用 3 次以上才划算。
示例 4:DeepSeek 自动缓存——免费且无感
DeepSeek 的磁盘缓存默认开启、零配置、不额外收费,只需解析返回的 usage 字段确认命中率:
from openai import OpenAI # DeepSeek 兼容 OpenAI 协议
client = OpenAI(
api_key="sk-deepseek-key",
base_url="https://api.deepseek.com",
)
# 长静态上下文:金融研报分析场景
system_prompt = ("你是一位资深金融分析师,请基于以下研报内容回答问题。\n"
"研报内容:" + "本季度营收同比增长 23%,毛利率 41%……" * 150)
def ask(q: str):
resp = client.chat.completions.create(
model="deepseek-v4-flash",
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": q},
],
)
u = resp.usage
# DeepSeek 专用字段:prompt_cache_hit_tokens / prompt_cache_miss_tokens
print(f"命中={u.prompt_cache_hit_tokens} 未命中={u.prompt_cache_miss_tokens}")
return resp.choices[0].message.content
ask("请总结这份研报的关键结论")
ask("请分析公司的盈利情况") # 前缀命中,几乎不花输入钱
输出示例:
命中=0 未命中=2150
命中=2128 未命中=22 ← 输入成本按 1/31 计费
示例 5:LiteLLM 统一网关——一套代码接入多厂商缓存
生产环境往往要切换多家模型供应商,用 LiteLLM 统一网关可以一套代码拿到各家格式统一的 usage 缓存字段:
from litellm import completion
# 第一次调用(会写缓存)
resp1 = completion(
model="openai/gpt-4o",
messages=[
{"role": "system", "content": "你是一名法律助手。" + "法规条文:" * 300},
{"role": "user", "content": "第一条是什么?"},
],
)
# 第二次调用同一前缀(命中缓存)
resp2 = completion(
model="openai/gpt-4o",
messages=[
{"role": "system", "content": "你是一名法律助手。" + "法规条文:" * 300},
{"role": "user", "content": "第二条是什么?"},
],
)
# LiteLLM 统一归一化为 OpenAI 格式的 cached_tokens 字段
print("第一次缓存命中:", resp1.usage.prompt_tokens_details.cached_tokens)
print("第二次缓存命中:", resp2.usage.prompt_tokens_details.cached_tokens)
LiteLLM 的配置文件中还能为不同厂商设置路由与模型映射,切换供应商时业务代码零改动,缓存检测逻辑也完全一致。
示例 6:成本监控脚本——把节省量化出来
缓存收益必须可观测。写一个生产可用的监控函数,把每次请求的缓存命中与成本节省写入日志/时序数据库:
import json
import time
from typing import Optional
# 厂商定价参数(单位:美元 / 每百万 token,按需替换为官方最新价)
PRICING = {
"deepseek-v4-flash": {"hit": 0.014, "miss": 0.44, "output": 1.32}, # 峰值价
"gpt-4o": {"hit": 1.25, "miss": 2.50, "output": 10.0},
}
def log_cache_metrics(model: str, usage, saved_budget: Optional[str] = None):
"""解析 usage,计算缓存命中率与单次请求节省金额"""
hit = getattr(usage, "prompt_cache_hit_tokens", 0) or 0
miss = getattr(usage, "prompt_cache_miss_tokens", 0) or 0
# 兼容 OpenAI 格式(cached_tokens)与 DeepSeek 格式(hit_tokens)
details = getattr(usage, "prompt_tokens_details", None)
if details and getattr(details, "cached_tokens", 0):
hit = details.cached_tokens
miss = max(0, usage.prompt_tokens - hit)
p = PRICING.get(model, {"hit": 0, "miss": 0, "output": 0})
# 假设未命中时这些 token 按 miss 价计费,命中时按 hit 价计费
cost_if_no_cache = miss * p["miss"] / 1e6
cost_with_cache = miss * p["miss"] / 1e6 + hit * p["hit"] / 1e6
saved = cost_if_no_cache - cost_with_cache
hit_rate = hit / (hit + miss) if (hit + miss) else 0
record = {
"ts": time.time(), "model": model,
"cache_hit_tokens": hit, "cache_miss_tokens": miss,
"hit_rate": round(hit_rate, 4),
"saved_usd": round(saved, 6),
"budget": saved_budget,
}
# 生产环境写入 Prometheus / 日志采集,这里仅打印
print(json.dumps(record, ensure_ascii=False))
return record
把这个函数挂在请求出口,配合告警规则(例如「命中率连续 10 分钟低于 30% 且输入 token 量大」触发告警),缓存收益就完全透明了。
关键细节与踩坑指南
缓存看起来简单,生产环境里全是坑。以下每条都来自真实项目。
坑 1:前缀漂移——最常见的缓存杀手
错误写法:把动态内容插在静态内容前面,导致后面所有 token 位置偏移、前缀整体失效。
# ❌ 错误:动态时间戳插在最前面,每次请求前缀都不同,缓存永久失效
messages = [
{"role": "system", "content": f"今天是 {datetime.now():%Y-%m-%d}。" + STATIC_KB},
{"role": "user", "content": question},
]
正确写法:静态内容前置,动态内容后置,把每次变化的东西尽量放在 user 消息里、断点之后。
# ✅ 正确:静态知识库前置(可缓存),动态时间/问题后置(不参与前缀)
messages = [
{"role": "system", "content": STATIC_KB}, # 前缀稳定,可命中
{"role": "user", "content": f"今天是 {datetime.now():%Y-%m-%d}。{question}"},
]
坑 2:Token 级敏感——一个空格、一个换行都致命
缓存匹配发生在 token 层面而非字符层面。以下情况都会导致 miss:
- 动态模板生成了不同的空格/换行(如 Markdown 格式化差异);
- JSON 序列化时键的顺序不稳定(用
sort_keys=True固定); - 文本末尾多了个
\n。
排查方法:对两次请求的 messages 做 tokenize 后逐 token 对比,找到第一个不同位置。用 tiktoken 可以快速定位:
import tiktoken
enc = tiktoken.encoding_for_model("gpt-4o")
t1 = enc.encode(STATIC_KB + "问题A")
t2 = enc.encode(STATIC_KB + "问题B")
# 找到第一个不同 token 的位置
diff = next((i for i, (a, b) in enumerate(zip(t1, t2)) if a != b), None)
print("首个差异位置:", diff) # 若 diff 在静态内容内部,说明静态内容本身不稳定
坑 3:低于最低 token 门槛,缓存被静默跳过
各家对「参与缓存的最小输入长度」有要求,低于门槛时缓存被静默跳过且不报错:
| 厂商 | 最低输入 token |
|---|---|
| OpenAI | 1,024 |
| Anthropic Claude 3.x | 1,024 |
| Anthropic Sonnet/Opus 4.x | 2,048 |
| Anthropic Haiku 4.5+ / Opus 4.5+ | 4,096 |
| Google Gemini | 1,024 |
| DeepSeek | 无明确下限(短前缀也按单元缓存) |
所以永远不要假设缓存生效,必须通过 usage 字段验证——这正是本文所有示例都打印 cached_tokens 的原因。
坑 4:Anthropic 的缓存写入要收费——复用次数少反而亏
Anthropic 的 cache write 加价 25%:写一次缓存、只读一次,总共多付约 12.5% 的输入成本;写一次、读 3 次以上才显著划算。因此:
- 只对高频复用的静态内容打
cache_control; - 会话很短、上下文每次都不一样时,不要用 Anthropic 显式缓存,改用自动缓存的厂商或 DeepSeek。
坑 5:多轮对话与缓存失效
多轮对话里,如果把历史对话放在消息中间,每次追加新轮次都会改变「中间位置」,但前缀(system + 早期对话)通常仍能命中——只要你的历史是顺序追加的。风险在于:有的框架会「裁剪」早期历史(如只保留最近 10 轮),一旦裁剪,前缀就变了,之前缓存全部失效。对策是优先裁剪尾部历史,保持 system 提示与前几轮不动。
坑 6:缓存 TTL 与冷启动
OpenAI 默认缓存保留仅 5–10 分钟(in-memory),Anthropic ephemeral 缓存约 5 分钟,DeepSeek 磁盘缓存「几个小时到几天」自动清理。低流量场景可能刚写完缓存就过期,命中率上不去;而高流量场景缓存会被持续续期。设计上可考虑:
- 关键静态前缀用
prompt_cache_retention="24h"(OpenAI)延长; - 用
prompt_cache_key把同前缀流量汇聚到同一后端; - 对自托管 vLLM,注意显存占用:APC 会吃掉额外显存,需结合
gpu_memory_utilization调优。
生产环境最佳实践
1. 缓存友好的 Prompt 结构设计
把请求拆成「三段式」,这是所有厂商通用且收益最大的设计:
[第 1 段:静态系统提示] ← 稳定不变,一定要可缓存
[第 2 段:静态知识库/文档] ← 每轮 RAG 检索结果,尽量稳定
[第 3 段:动态指令/问题] ← 每次变化,放在最末(Anthropic 放断点之后)
graph LR
subgraph 可缓存前缀区
A[静态系统提示<br/>角色/规则/格式约束]
B[静态知识库/文档<br/>RAG 固定上下文]
end
subgraph 动态区
C[用户问题/动态指令<br/>时间戳/会话ID]
end
A --> B --> C --> D[发送请求]
D --> E{usage 校验<br/>cached_tokens > 0?}
E -- 是 --> F[计费折扣 + 延迟降低]
E -- 否 --> G[检查前缀稳定性<br/>定位首个差异 token]
2. 命中率监控与告警模板
- 指标:
cache_hit_rate = cached_tokens / prompt_tokens,按模型、按业务线聚合; - 告警:输入量大且命中率 < 30% 时告警,多半是前缀漂移或模板 bug;
- 成本看板:把
saved_usd累加进成本报表,让节省可量化、可汇报。
3. 缓存键与流量汇聚
对「多用户共享同一大上下文」的场景(公共文档分析、公共 Agent 工具说明),用 prompt_cache_key 或设计消息模板保证同一上下文的请求走同一后端,避免缓存碎片化。对「每用户独立上下文」,让用户 ID 不出现在前缀里,把身份信息挪到动态区。
4. 成本核算模板(月度)
月度输入成本 = 未命中 token × miss 单价 + 命中 token × hit 单价 + 输出 token × 输出单价。把这个公式固化到计费脚本里,替换掉「一律按 miss 价估算」的旧口径——很多团队就是因为旧口径把真实成本高估了数倍,反而错过了缓存优化。
横向对比与选型建议
| 维度 | OpenAI | Anthropic | DeepSeek | Gemini | vLLM(自托管) |
|---|---|---|---|---|---|
| 触发方式 | 全自动 | 显式断点 | 全自动 | 显式对象 | 引擎自动 |
| 最低 token | 1024 | 1024~4096 | 无硬性下限 | 1024 | 无 |
| 命中折扣 | 约 5 折 | 读取约 1 折 | 约 1/31 | 读取折扣 | 省算力 |
| 隐藏费用 | 无 | 写入 +25% | 无 | 存储费 | 显存占用 |
| 缓存时长 | 5–10min / 24h | 约 5min | 数小时~数天 | 可配置 TTL | 直到被驱逐 |
| 适用场景 | 通用、多供应商 | 长文档精读、Agent | 高并发 RAG、成本敏感 | 长会话多轮查询 | 私有化、高吞吐 |
选型决策路径:
- 成本极度敏感、输入上下文大且高频复用(RAG 客服、批量文档分析)→ DeepSeek,零配置 + 31 倍差距,性价比之王;
- 需要确定性、且同一前缀复用 ≥ 3 次 → Anthropic,显式断点可控性最强,但要算清写入加价;
- 多模型切换、不想绑定厂商 → LiteLLM 网关统一缓存检测,OpenAI 自动缓存做兜底;
- 私有化部署、追求吞吐而非省钱 → vLLM APC,注意显存预算;
- 长会话多轮、上下文不断增长 → Gemini Context Caching(按 token×时长计费,会话越长越划算)。
性能实测与效果验证
以下数据来自 DeepSeek 官方定价页(2026-08-26 抓取,单位:美元 / 每百万 token):
| 模型 | 输入 Cache Hit(峰值) | 输入 Cache Miss(峰值) | 倍差 |
|---|---|---|---|
| deepseek-v4-flash | $0.014 | $0.44 | 31.4× |
| deepseek-v4-pro | $0.044 | $1.32 | 30× |
(非峰值时段价格减半,倍差保持不变。)
回到开篇的 RAG 客服系统做量化测算。假设:每日 10 万请求,每请求输入 4800 token,其中 4600 token 为可命中的静态前缀,命中率 95%,用 deepseek-v4-flash 峰值价:
- 无缓存:4800 × 100000 / 1e6 × $0.44 = $2112/天
- 有缓存:200 × 0.44 / 1e6 × 100000 + 4600 × 0.014 / 1e6 × 100000 = $8.8 + $64.4 = $73.2/天
- 单日节省 ≈ $2039,节省比例 ≈ 96.5%,月省约 6.1 万美元
即便命中率只有 50%,成本也能降到约 $1100/天(省一半)。再叠加输出 token 折扣与峰值/非峰值调度,收益更可观。
xychart-beta
title "日输入成本对比(美元)—— RAG 客服系统 10 万请求/日"
x-axis ["无缓存", "命中率50%", "命中率80%", "命中率95%"]
y-axis "日成本(美元)" 0 --> 2200
bar [2112, 1080, 520, 73]
测算说明:命中率 50% 时,2400 token 按 miss 价、2400 按 hit 价;80% 时 960/3840;95% 时 200/4600。均为输入成本,未含输出 token 与存储费。各家折扣以官方最新价格页为准。
总结与未来展望
Prompt Caching 是 2026 年 LLM 应用降本投入产出比最高的手段:你不需要改模型、不需要换架构,只需要理解 KV Cache 的前缀命中原理、把静态内容前置、并解析 usage 字段验证命中——就能拿到 50%~96% 的输入成本降幅。核心要点回顾:命中只认完全一致的前缀 token;OpenAI/DeepSeek 自动、Anthropic 显式、Gemini 按存储计费;永远用 cached_tokens 验证而非假设。
展望未来,缓存正在从「计费折扣」演进为「系统性能力」:OpenAI 的 24 小时长缓存、显式断点标志着缓存粒度的精细化;vLLM 的 APC 与 KV 卸载(如 FlexKV、LMCache)让自托管场景的缓存更灵活;多厂商统一网关(LiteLLM)让缓存策略可编程化。下一个值得关注的进阶方向是 缓存感知的请求路由 与 前缀蒸馏——把高频前缀固化为更短的稳定表达,进一步扩大命中面。