LLM Prompt Caching 全解析:让 API 成本直降 90% 的工程实战

LLM 0 次阅读
LLM Prompt Caching 全解析:让 API 成本直降 90% 的工程实战

你的应用每秒钟都在把同一份系统提示词、同一批工具定义、同一段知识库文档重复发给大模型 API,并为此反复付费。Prompt Caching(提示词缓存)是 2026 年投入产出比最高的一项 LLM 成本优化——不改模型、不改架构,只改 Prompt 结构与几行配置,就能把输入成本砍掉 50%~90%。本文从 KV Cache 底层原理讲起,覆盖 Anthropic、OpenAI、Gemini、DeepSeek 四家最新接入方式与定价,附 7 个可运行代码与真实踩坑案例。

开篇:从一个真实业务场景说起

假设你负责一个企业级客服 RAG 机器人,架构非常简单:每次用户提问,应用把 系统提示词(约 500 tokens)+ 知识库检索结果(约 3000 tokens)+ 对话历史(约 2000 tokens)+ 用户问题(约 50 tokens) 拼成一条 5550 tokens 的请求发给 Claude。逻辑上毫无问题,账单却让你心惊肉跳。

按 Claude Sonnet 4.6 标准输入价 $3/百万 tokens 计算:日请求 1 万次、月输入约 1.67B tokens,月成本高达 $5,010——而其中 90% 的 token 是每天重复发送的同一批内容。更残酷的是,在 Agent 场景中问题会被放大十倍:一个会连续调用 30~50 次工具的 Agent,每一轮都把 System Prompt 和工具 Schema 重新发一遍。有开发者实测,一个几分钟的 Agent 任务烧掉近 50 美元,后台缓存命中率却是 0。

这就是 Prompt Caching 要解决的问题:让服务端记住你发过的前缀,下一次直接复用计算结果。Anthropic 官方宣称它能把长 Prompt 的延迟最高降低 85%,而缓存命中的输入 token 价格仅为正常的 1/10。2026 年,主流厂商已全部支持,且实现成本从"零代码改动"(OpenAI、DeepSeek)到"请求体里加一个字段"(Anthropic)不等。本文的目标很直接:让你彻底搞懂它的原理、学会四家厂商的接入方式,并避开那些让命中率归零、甚至引发数据泄露的坑。

技术背景与核心概念扫盲

要理解 Prompt Caching,得先厘清几个层层递进的概念。它们之间有严格的包含关系,很多文章混为一谈,这里一次讲清。

Tokenizer(分词器) 把文本切成 token(词元)并映射为整数 ID。相同的文本永远产生相同的 token 序列——这是缓存能工作的前提。

Embedding(嵌入) 把每个 token ID 变成高维向量。Transformer 的每一层都对这些向量做变换,其中最关键的运算是 Attention(注意力机制):每个 token 生成 Query(查询)、Key(键)、Value(值)三个向量,用 Query 与所有 Key 做点积、经 Softmax 归一化得到权重,再加权求和 Value。注意力的数学形式是:

$$\text{Attention}(Q,K,V) = \text{softmax}\left(\frac{QK^\top}{\sqrt{d_k}}\right)V$$

KV Cache(键值缓存) 是推理引擎内部的优化。LLM 逐 token 自回归生成,每生成一个新 token,理论上都要重新计算前面所有 token 的 Key/Value。KV Cache 把这些 Key/Value 向量缓存下来,后续只需计算新 token 的三个向量,复杂度从 O(n²) 降到 O(n)。这是所有生产级推理框架的标配。

Prompt Caching(提示词缓存) 是 KV Cache 的"跨请求"延伸:既然同一个前缀在多次请求中重复出现,那不如把该前缀的 KV 计算结果存在服务端,下次请求若前缀一致,直接复用——省掉的是 prefill(预填充)阶段最耗时的重复计算。它缓存的是注意力计算结果(二进制 KV 张量),不是对话文本,所以缓存命中不影响输出质量,连续发送十几次相同 Prompt 每次回复仍各不相同。

TTL(Time-to-Live,生存时间) 指缓存条目的存活时长,过期即失效。

TTFT(Time-to-First-Token,首 token 延迟) 指从发出请求到收到第一个输出 token 的耗时。prefill 是 TTFT 的主要来源,因此缓存命中能显著压低 TTFT。

一句话总结:KV Cache 是"一次请求内部"的复用,Prompt Caching 是"多次请求之间"的复用。前者是推理框架的事,后者是你要主动设计的事。

底层原理深度拆解

一次推理请求的完整旅程

大模型推理分两个阶段:prefill(预填充)decode(解码)。prefill 阶段一次性处理整个输入 Prompt,计算所有 token 的 Key/Value 张量,产生第一个输出 token;decode 阶段逐 token 生成后续内容,每步只算新 token 的 Query/Key/Value。生成 100 个 token,就要 decode 100 次。

flowchart TD
    A[用户 Prompt] --> B[Tokenizer 分词]
    B --> C[Embedding 向量化]
    C --> D["prefill 阶段<br/>一次性计算全部 token 的 K/V"]
    D --> E["KV Cache 存储<br/>K/V 张量驻留显存"]
    E --> F["decode 阶段<br/>每步仅计算新 token 的 Q/K/V"]
    F --> G[输出新 token]
    G -->|追加到上下文| E
    G -->|达到终止条件| H[生成结束]
    D -.->|"跨请求复用<br/>(Prompt Caching)"| I[服务端 KV 缓存池]
    I -.->|"前缀哈希命中则直接加载"| E

图 1:一次 LLM 推理请求的两阶段流程,以及 KV Cache(请求内复用)与 Prompt Caching(请求间复用)的关系。

没有 KV Cache 时,生成第 10 个 token 要把前 9 个 token 的注意力全部重算;有了 KV Cache,只需做一次向量-矩阵乘法。而 Prompt Caching 更进一步:如果两个请求共享前缀,第二个请求甚至不用重新执行 prefill——直接从缓存池中把这段前缀的 KV 张量拼接进新的 KV Cache 即可。

服务端是怎么判断"命中"的

各家实现细节不同,但核心机制一致:对输入前缀做哈希,哈希一致即命中。以 Anthropic 为例,你在某个内容块上打上 cache_control 断点(cache breakpoint),服务端对该断点之前的所有 token 计算哈希作为缓存 Key。下一次请求如果前缀逐 token 完全一致(连一个空格都不能差),哈希匹配,命中缓存。

flowchart TD
    A[新请求到达] --> B{前缀长度 ≥ 最小阈值?<br/>Anthropic/OpenAI 1024 tokens<br/>Gemini 4096 tokens}
    B -->|否| C[不进入缓存路径<br/>全量计费]
    B -->|是| D{断点前前缀哈希<br/>与缓存条目匹配?}
    D -->|是| E[缓存命中<br/>跳过 prefill 计算<br/>按缓存读取价计费 10%]
    D -->|否| F[缓存未命中<br/>正常 prefill 计算<br/>按缓存写入/全量价计费]
    E --> G[只计算断点后的新增部分]
    F --> H[计算结果写入缓存池<br/>更新 TTL 计时]
    G --> I[输出]
    H --> I

图 2:Prompt Caching 的命中判定流程——前缀哈希逐 token 精确匹配,任何字节差异都会导致未命中。

为什么缓存能便宜 90%:成本结构拆解

对服务商而言,prefill 是计算密集(compute-bound)阶段,吃 GPU 算力;decode 是访存密集(memory-bound)阶段。Prompt Caching 让高频重复的 prefill 只算一次,服务商省下算力,自然愿意给缓存读取大幅折扣。Anthropic 的定价逻辑最具代表性:

  • 缓存写入(cache creation):正常价的 1.25 倍($3.00/M → $3.75/M,Sonnet 4.6)——首次计算要付出溢价
  • 缓存读取(cache read):正常价的 0.1 倍($3.00/M → $0.30/M)——之后每次命中只付 1 折
  • 盈亏平衡点:同一个前缀被读取 2 次以上 即回本,此后每一次命中都是纯赚

这个 1.25x 写入溢价极其关键,它意味着:低频、一次性请求用缓存反而更贵。缓存的经济性必须建立在"前缀稳定 + 高频重复"的前提上。DeepSeek 则凭借 MLA(Multi-head Latent Attention,多头潜在注意力)架构把 KV Cache 压缩到极小,敢把缓存直接落到硬盘阵列,缓存命中价压到常规的约 1/10,且对用户完全透明、默认开启。

手把手实战落地

下面按厂商逐一实战。所有代码基于 2026 年 8 月各 SDK 最新稳定版本,环境要求 Python ≥ 3.10,安装对应官方 SDK:

# Anthropic / OpenAI / Gemini 官方 SDK
pip install -U anthropic openai google-genai

实战 1:Anthropic 显式缓存(cache_control)

Anthropic 的缓存是手动开启的,你把哪个内容块标记为可缓存,服务端就缓存哪个块。最典型的场景是 RAG:把 5000 tokens 的产品知识库放进 system 并打上 cache_control,用户问题保持动态。

import anthropic

client = anthropic.Anthropic(api_key="YOUR_API_KEY")

# 静态知识库:所有用户共享、长期不变,是最高价值的缓存目标
KNOWLEDGE_BASE = """[此处放入 5000+ tokens 的产品文档/知识库内容]"""

def ask_question(user_question: str, conversation_history: list) -> str:
    """RAG 客服问答:知识库静态缓存,用户问题动态传入"""
    response = client.messages.create(
        model="claude-sonnet-4-6",          # 2026 年最新稳定模型
        max_tokens=1024,
        system=[
            {
                "type": "text",
                "text": "你是一个专业的售后客服助手,只能依据知识库回答。",
            },
            {
                "type": "text",
                "text": KNOWLEDGE_BASE,
                # 关键:标记该内容块可缓存(cache breakpoint)
                "cache_control": {"type": "ephemeral"},
            },
        ],
        messages=[*conversation_history, {"role": "user", "content": user_question}],
    )

    # 通过 usage 字段验证缓存命中情况
    usage = response.usage
    print(f"缓存写入 tokens: {usage.cache_creation_input_tokens}")
    print(f"缓存读取 tokens: {usage.cache_read_input_tokens}")
    return response.content[0].text

# 第一次调用:cache_creation_input_tokens > 0(建立缓存)
result1 = ask_question("如何申请退款?", [])
# TTL 内的后续调用:cache_read_input_tokens > 0(命中缓存,按 1 折计费)
result2 = ask_question("退款多久到账?", [{"role": "assistant", "content": result1}])

代码 1:Anthropic 显式缓存接入。首次调用 cache_creation_input_tokens 非零,后续 TTL 内 cache_read_input_tokens 非零且输入成本降到 1/10。

实战 2:Anthropic 多断点分层缓存

真实 Prompt 包含多种变化频率的内容,Anthropic 支持最多 4 个缓存断点,正好用于分层。原则:完全静态的放最前,次静态的随后,动态的放最后不缓存

def multi_breakpoint_call(system_prompt: str, tenant_config: str,
                          user_message: str) -> str:
    """三层缓存:系统规则 + 租户配置缓存,用户消息不缓存"""
    response = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=1024,
        system=[
            # 断点 1:全局系统规则(所有人共享,缓存价值最高)
            {"type": "text", "text": system_prompt,
             "cache_control": {"type": "ephemeral"}},
            # 断点 2:租户级配置(同租户共享、跨租户不同,天然隔离)
            {"type": "text", "text": tenant_config,
             "cache_control": {"type": "ephemeral"}},
        ],
        messages=[{"role": "user", "content": user_message}],
    )
    return response.content[0].text

代码 2:多断点分层。注意租户配置块放在独立断点后——两个租户的字节不同,前缀哈希自然不同,不会互相命中,安全性由字节级匹配天然保证。

实战 3:OpenAI 自动缓存

OpenAI 的缓存是全自动的,无需任何标记。你只需要保证:① 前缀足够稳定;② 前缀长度超过 1024 tokens。它在服务端自动做前缀匹配并计价折扣。

from openai import OpenAI

client = OpenAI(api_key="YOUR_API_KEY")

LONG_SYSTEM_PROMPT = """你是企业的智能客服...(此处展开,务必超过 1024 tokens 才会触发缓存)"""

def auto_cached_chat(user_message: str) -> str:
    """OpenAI 自动缓存:零配置,但必须保证 system 前缀逐字节稳定"""
    response = client.chat.completions.create(
        model="gpt-5.4",                     # 2026 年主推旗舰,缓存读 90% off
        messages=[
            {"role": "system", "content": LONG_SYSTEM_PROMPT},
            {"role": "user", "content": user_message},
        ],
    )
    # 关键:通过 usage.prompt_tokens_details.cached_tokens 统计命中率
    details = response.usage.prompt_tokens_details
    cached = details.cached_tokens if details else 0
    total = response.usage.prompt_tokens
    print(f"缓存命中率: {cached / total:.1%}({cached}/{total} tokens)")
    return response.choices[0].message.content

代码 3:OpenAI 自动缓存。cached_tokens 字段持续为 0 时,说明动态内容泄漏进了 system 前缀,需要回头检查 Prompt 结构。

实战 4:Gemini 显式 Context Caching

Gemini 走的是另一条路:显式创建一个命名缓存对象,按小时付存储费,请求时用 cached_content 引用它。适合"一份大文档要被反复分析"的场景,比如代码审查流水线把整个代码库缓存一次,跑多轮分析。

import datetime
from google import genai
from google.genai import types

client = genai.Client(api_key="YOUR_API_KEY")

LARGE_DOCUMENT = """[100k tokens 的代码库/长文档]"""

# 创建缓存:一次性计算并存储,之后按存储时长计费
cache = client.caches.create(
    model="models/gemini-3-flash-preview",   # 2026 年最新模型
    config=types.CreateCachedContentConfig(
        display_name="codebase_cache",
        system_instruction="你是一个资深的代码审查助手。",
        contents=[types.Content(role="user",
                                parts=[types.Part(text=LARGE_DOCUMENT)])],
        ttl=datetime.timedelta(hours=1),     # TTL 即"真相衰减预算",按需设置
    ),
)

def review_code(code_snippet: str) -> str:
    """复用已创建的缓存做多轮分析,只对新增 snippet 计费"""
    response = client.models.generate_content(
        model="models/gemini-3-flash-preview",
        contents=code_snippet,
        config=types.GenerateContentConfig(cached_content=cache.name),
    )
    return response.text

# 用完后删除缓存,停止存储计费(Gemini 独有的收费项,务必清理)
client.caches.delete(name=cache.name)

代码 4:Gemini 显式 Context Caching。存储费是按 token 数 × 小时计算的,低频任务用显式缓存前必须先算账。

实战 5:DeepSeek 硬盘缓存(零改动)

DeepSeek 的 Context Caching on Disk 对所有用户默认开启,无需任何代码改动。它把 KV Cache 存到分布式硬盘阵列(得益于 MLA 架构,缓存体积极小),只要后续请求与之前请求存在前缀重复即自动命中。你唯一要做的是读取 usage 字段验证并统计收益

from openai import OpenAI   # DeepSeek 兼容 OpenAI SDK 协议

client = OpenAI(
    api_key="YOUR_API_KEY",
    base_url="https://api.deepseek.com",
)

def deepseek_chat(user_message: str, history: list) -> str:
    response = client.chat.completions.create(
        model="deepseek-chat",
        messages=[*history, {"role": "user", "content": user_message}],
    )
    # 命中缓存的输入 token 会出现在 cached_tokens 中
    details = response.usage.prompt_tokens_details
    print(f"本次缓存命中 tokens: {details.cached_tokens if details else 0}")
    return response.choices[0].message.content

代码 5:DeepSeek 零配置缓存。官方定价中缓存命中的输入价格约为常规的 1/10,高峰/低谷时段价格不同,命中判定同样是前缀匹配。

实战 6:缓存命中率监控与成本核算

没有观测就没有优化。生产环境必须把每次响应的 usage 汇总成可追踪的指标,用真实数据驱动决策——而不是靠"感觉便宜了"。

from dataclasses import dataclass, field
from collections import deque

@dataclass
class CacheMetrics:
    """缓存命中率与成本节省追踪器(以 Claude Sonnet 4.6 为例)"""
    total_requests: int = 0
    cache_hits: int = 0                       # 有缓存读取的请求数
    total_input_tokens: int = 0
    cached_tokens: int = 0
    recent_hit_rates: deque = field(default_factory=lambda: deque(maxlen=100))

    BASE_PRICE = 3.00   # 标准输入 $/M tokens
    READ_PRICE = 0.30   # 缓存读取 $/M tokens(1 折)

    @property
    def request_hit_rate(self) -> float:
        """请求级命中率:有多少请求吃到了缓存"""
        return self.cache_hits / self.total_requests if self.total_requests else 0.0

    @property
    def token_cache_rate(self) -> float:
        """Token 级命中率:输入 token 中有多少走了缓存"""
        return self.cached_tokens / self.total_input_tokens if self.total_input_tokens else 0.0

    @property
    def saved_usd(self) -> float:
        """相比无缓存省下的钱:命中 token × (标准价 - 缓存价)"""
        return self.cached_tokens * (self.BASE_PRICE - self.READ_PRICE) / 1_000_000

metrics = CacheMetrics()

def tracked_call(system_prompt: str, user_message: str) -> str:
    """带缓存追踪的 LLM 调用(Anthropic 版)"""
    response = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=1024,
        system=[{"type": "text", "text": system_prompt,
                 "cache_control": {"type": "ephemeral"}}],
        messages=[{"role": "user", "content": user_message}],
    )
    usage = response.usage
    cache_read = getattr(usage, "cache_read_input_tokens", 0) or 0

    metrics.total_requests += 1
    metrics.total_input_tokens += usage.input_tokens
    metrics.cached_tokens += cache_read
    if cache_read > 0:
        metrics.cache_hits += 1

    # 每 10 次请求输出一次关键指标,接入 Prometheus 后改为上报
    if metrics.total_requests % 10 == 0:
        print(f"请求命中率: {metrics.request_hit_rate:.1%} | "
              f"Token 缓存率: {metrics.token_cache_rate:.1%} | "
              f"累计节省: ${metrics.saved_usd:.2f}")
    return response.content[0].text

代码 6:缓存监控类。两个命中率指标含义不同——请求命中率低说明"前缀不稳定",Token 缓存率高说明"静态内容占比大",要分开看。

实战 7:缓存友好 Prompt 构建器

最后把全部经验固化成"缓存友好 Prompt 构建器",作为团队统一的 Prompt 组装入口,从源头杜绝动态内容泄漏。

import json

class CacheOptimizedPromptBuilder:
    """缓存友好的三层 Prompt 构建器:
    第 1 层静态(跨会话不变)→ 第 2 层会话级(会话内稳定)→ 第 3 层动态(每次变化)
    """

    def __init__(self):
        self.static_context = ""       # 公司信息、产品手册、工具定义等永远不变的内容
        self.session_context = ""      # 用户画像、偏好等会话内稳定的内容
        self.dynamic_content = ""      # 每次请求都不同的内容

    def set_static(self, text: str):
        self.static_context = text

    def set_session(self, user_profile: dict, preferences: dict):
        """会话级上下文:对字段排序,确保相同内容生成字节级一致的字符串
        这是缓存命中的前提——dict 的键顺序不稳定会直接打碎前缀"""
        self.session_context = (
            "用户信息:" + json.dumps(user_profile, ensure_ascii=False, sort_keys=True)
            + "\n偏好设置:" + json.dumps(preferences, ensure_ascii=False, sort_keys=True)
        )

    def build(self, user_message: str) -> dict:
        """动态内容永远追加在最后,绝不插入静态前缀中间"""
        return {
            "system": [
                {"type": "text", "text": self.static_context,
                 "cache_control": {"type": "ephemeral"}},
                {"type": "text", "text": self.session_context,
                 "cache_control": {"type": "ephemeral"}},
            ],
            "messages": [
                {"role": "user",
                 "content": f"[{self.dynamic_content}]\n{user_message}"},
            ],
        }

代码 7:缓存友好构建器。sort_keys=True 与动态内容后置,是让缓存命中率从 0 拉到 80%+ 的两个关键细节。

关键细节与踩坑指南

这一节是真实生产经验的核心。下面是 5 个高频坑,每一个都对应真实的账单事故。

坑 1:系统提示里混入动态内容,命中率直接归零

最常见的错误:把时间戳、请求 ID、用户问候语塞进 System Prompt。

# ❌ 错误:时间戳在 System Prompt 里,每秒都不同 → 缓存永远不命中
system = f"当前时间是 {datetime.now().isoformat()}。你是客服助手..."

# ✅ 正确:时间戳挪到 user 消息里,System Prompt 保持完全静态
system = "你是客服助手。..."
user_msg = f"[{datetime.now().isoformat()}] 用户问:{question}"

同样的坑还有:在 system 里动态拼接用户实时状态、把 AGENTS.md / MEMORY.md 每轮实时刷新后塞进 system。凡是要变的,全部放到断点之后

坑 2:知识库检索结果排序不稳定

RAG 场景里,相同的查询可能因为向量检索排序微变、或命中文档顺序不同,产生字节不同的上下文,缓存随之失效。解决:按文档 ID 稳定排序后再拼接。

# ❌ 错误:检索结果顺序不稳定 → 相同查询不同前缀
context = "\n".join(doc.content for doc in retrieved_docs)

# ✅ 正确:按文档 ID 排序,保证相同查询永远得到相同字节的前缀
context = "\n".join(
    doc.content for doc in sorted(retrieved_docs, key=lambda d: d.id)
)

坑 3:Agent 的 Plan/Act 模式切换改了 System Prompt 和工具列表

Agent 场景命中率为 0 的头号元凶:进入 Plan 模式时换了一套 System Prompt、增减了工具列表,导致前缀彻底变化。排查实录显示,某 Agent 一个任务几分钟烧掉近 50 美元,后台命中率 0,原因正是"模式切换改前缀"。正确做法:System Prompt 与工具 Schema 全局恒定,模式限制交给 Policy 层在应用侧兜底,而不是每轮改 Prompt。

坑 4:MCP 工具动态刷新打碎 Tools Schema

工具列表里的 JSON Schema 一旦有字段增减、描述微调,前缀哈希就变。工具定义是 Agent 最大的静态缓存块(常占 2000~5000 tokens),务必让它在会话生命周期内保持不变;低频变更内容做快照,不要每轮实时刷新。

坑 5:缓存 Key 是正确性边界,不是计费旋钮

这是最隐蔽、最危险的坑。缓存断点之上的内容,等于你向服务商宣告"这些事实可以在请求之间互换"。如果你为了凑够前缀长度、提高命中率,把用户级权限、租户配置、个性化偏好放到了断点之上,那么当两个用户的字节前缀一致时,一个用户会读到另一个用户的上下文——这不是缓存泄露,是你把边界划错了地方

业界已有实证:NDSS 2025 发表的 PROMPTPEEK 研究表明,多租户共享 KV 缓存存在侧信道攻击,攻击者能通过计时"哪些前缀已被预热"来重建其他用户的 Prompt。同时,缓存的 TTL 就是缓存内容的"真相衰减预算":5 分钟 TTL 意味着你愿意在事实变化后 5 分钟内继续提供旧事实。用户改了权限却立刻看到旧行为,用户会认为这是 Bug

修复原则

  • 跨租户泄露 → 把主体身份纳入缓存 Key(租户块放在独立断点,字节不同自然隔离)
  • 过期个性化 → 加失效逻辑:事实变化时更新块内的版本号/内容哈希,强制前缀哈希改变导致未命中
  • 命中率必须与"正确性探针"并列监控:构造跨租户、跨新鲜度的评估请求,断言响应不携带他人事实

生产环境最佳实践

Prompt 结构黄金法则:不变在前,变化在后

flowchart TB
    subgraph 缓存层1["断点 1:完全静态(缓存价值最高)"]
        A1[System Prompt 系统规则]
        A2[Tool Definitions 工具 Schema]
        A3[Few-shot 示例]
    end
    subgraph 缓存层2["断点 2:租户级 / 半静态"]
        B1[租户策略与品牌配置]
        B2[知识库检索结果(按 ID 稳定排序)]
    end
    subgraph 不缓存["断点之后:动态(每次全价)"]
        C1[对话历史]
        C2[用户最新消息 / 时间戳]
    end
    A1 --> A2 --> A3 --> B1 --> B2 --> C1 --> C2

图 4:缓存友好的 Prompt 分层结构——静态内容按变化频率分层缓存,动态内容全部后置。

可直接复用的生产配置模板

  1. System Prompt 完全静态化:版本号管理,变更走发布流程,不热插拔
  2. 知识库块按 ID 稳定排序 + 内容哈希版本化(知识更新时改版本号,强制重写缓存)
  3. Agent 场景:工具列表会话内恒定;模式切换不碰 Prompt,交给应用层 Policy
  4. TTL 按层级选择:静态共享内容用长 TTL;用户级可变内容用短 TTL 或不缓存
  5. 监控:请求命中率 + Token 缓存率 + 节省金额三指标接入 Prometheus/Grafana;配套跨租户正确性探针
  6. 清理:Gemini 显式缓存用完即删,避免存储费累积;DeepSeek 无需管理

与 Batch API 叠加

Anthropic 的 Batch API 给所有 token 打 5 折,与缓存读取的 1 折叠加后,重复部分总节省可达 95%。批量数据提取、离线分析等任务强烈建议组合使用。

横向对比与选型建议

截至 2026 年 8 月,四家主流厂商的缓存能力对比如下(定价为官方公开价,可能随版本调整):

维度 Anthropic Claude OpenAI GPT-5.x Google Gemini DeepSeek
接入方式 显式 cache_control 断点 全自动,零代码 显式命名缓存 + 隐式缓存 全自动,默认开启
缓存读折扣 90%($3.00→$0.30/M,Sonnet 4.6) 90%($2.50→$0.25/M,gpt-5.4) 90%(显式缓存读 $0.0075/M,Flash) ~90%(缓存命中约 $0.007/M 非高峰)
缓存写成本 1.25x 溢价 无额外费用 无写入费,但按小时收存储费 无额外费用
最小缓存长度 1024 tokens 1024 tokens 4096 tokens(显式) 无公开门槛
TTL 5 分钟(默认,命中刷新) 约 24 小时 1 分钟 ~ 24 小时可配 较宽松
断点数量 最多 4 个 自动前缀 1 个缓存对象 自动前缀
与 Batch 叠加 支持(最高 95% 节省) 不支持 不支持

选型决策路径

  • 已经在用 OpenAI:先修 System Prompt 顺序,迁移到 gpt-5.4 享受 90% 缓存折扣,零代码改动,收益立现。
  • 在 Anthropic 且 system 长 / RAG 文档大:加 cache_control,盯 cache_read_input_tokens。高频场景(命中率 >80%)Claude 总成本最低,但要接受首次写入 1.25x 溢价。
  • 一份大文档被反复分析(代码审查、文档问答):Gemini 显式 Context Caching 最合适——缓存一次、多轮复用,但务必算清存储费、用完即删。
  • 国内链路 / 预算极敏感:DeepSeek 硬盘缓存零配置、零管理成本,MLA 架构让缓存命中价格压到极低,是性价比之选。

性能实测与效果验证

权威评测:PwC 三家厂商 500 会话实测

2026 年 1 月,普华永道团队在 DeepResearch Bench 上对 OpenAI、Anthropic、Google 三家旗舰模型做了系统性评测(arXiv:2601.06007),每个 agent 会话携带 10000-token system prompt,累计 500+ 会话:

  • API 成本降低 41%~80%,四款模型全部达到统计显著
  • TTFT 提升 13%~31%(缓存命中的前缀越长,收益越明显)
  • 关键发现:策略性缓存边界控制(只缓存 system prompt / 排除动态工具结果)比"朴素全量缓存"收益更一致——朴素全量缓存可能因动态内容反复触发缓存写入而反而增加延迟

PwC 评测:各模型在最优缓存策略下的成本与 TTFT 降低幅度

图 3:PwC 2026 年评测结果——Prompt Caching 在各旗舰模型上带来 41%~80% 的成本降低与 13%~31% 的 TTFT 提升(来源:arXiv:2601.06007)。

成本核算模型(可复算)

以客服 RAG 场景,Claude Sonnet 4.6 为例(日 1 万次、月 30 天、输入 5550 tokens/次,其中静态 5000 + 动态 550):

场景 计算过程 月输入成本
无缓存 5550 × 30 万 × $3/M $4,995
有缓存(高命中) 动态 550×$3/M + 缓存读 5000×$0.30/M $945
节省比例 (4995 − 945) / 4995 81%

再叠加 Anthropic Batch API 的 5 折,总节省可达 95%。注意:若前缀不稳定导致频繁"写入",写入 1.25x 溢价会吃掉收益——这正是把缓存监控指标接入生产的原因。

延迟收益

Anthropic 官方声称长 Prompt 延迟最高降低 85%;实测中,当全部输入 token 命中缓存时,TTFT 下降明显,而逐 token 生成速度(TTST)保持不变——因为缓存只跳过了 prefill。这意味着缓存命中的请求既便宜又快,是少见的"省钱又提速"的优化。

总结与未来展望

Prompt Caching 是 2026 年 LLM 成本优化中投入产出比最高的一项技术:不改模型、不改架构,只需把静态内容前置、动态内容后置,再按厂商规则接入,即可获得 50%~90% 的输入成本下降和 13%~31% 的首 token 延迟改善。它的本质是 KV 计算结果的跨请求复用,缓存的是注意力张量而非文本,因此不影响输出质量。但请记住:缓存断点是正确性边界,不是计费旋钮——静态共享内容进缓存,用户级可变内容必须留在断点之下,命中率要配合正确性探针一起监控。未来,随着 DeepSeek MLA 类架构与 LMCache 类 KV 卸载层(NVMe/远端存储)的成熟,缓存将走向跨引擎、跨节点的"AI 原生知识"复用,Prompt Caching 会从"可选项"彻底变成所有 LLM 应用的默认底座。

延伸阅读