LLM 提示词缓存深度实战:把 API 成本砍到 1/10 的完整方案

LLM 0 次阅读
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,滑动窗口注意力) 机制影响,缓存前缀是独立完整的单元,后续请求必须完整匹配某个单元才能命中,不存在「部分命中」。落盘时机有三个:

  1. 请求边界落盘:每次请求的用户输入结束位置、模型输出结束位置各产生一个缓存前缀单元;
  2. 公共前缀检测落盘:检测到多次请求存在公共前缀时,把公共前缀固化为独立单元;
  3. 固定 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、成本敏感 长会话多轮查询 私有化、高吞吐

选型决策路径

  1. 成本极度敏感、输入上下文大且高频复用(RAG 客服、批量文档分析)→ DeepSeek,零配置 + 31 倍差距,性价比之王;
  2. 需要确定性、且同一前缀复用 ≥ 3 次 → Anthropic,显式断点可控性最强,但要算清写入加价;
  3. 多模型切换、不想绑定厂商 → LiteLLM 网关统一缓存检测,OpenAI 自动缓存做兜底;
  4. 私有化部署、追求吞吐而非省钱 → vLLM APC,注意显存预算;
  5. 长会话多轮、上下文不断增长 → 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)让缓存策略可编程化。下一个值得关注的进阶方向是 缓存感知的请求路由前缀蒸馏——把高频前缀固化为更短的稳定表达,进一步扩大命中面。

延伸阅读