大模型提示词缓存实战:3 行代码让 API 调用成本直降 50%(Anthropic + LangChain 双方案)

阿里云上云采购季 2核2G云服务器99元/年 续费同价

你每天调大模型是不是每次都把 system prompt 原封不动发一遍?你知不知道这部分其实是白花花的冤枉钱?今天这篇文章,看完就能省。


你可能在浪费 40% 的 API 费用

先看一组真实数据。我用一个典型的 RAG 应用做测试——每次请求都带 3000 token 的 system prompt,加上用户问题和检索到的文档。

场景 单次请求 Token 数 Prompt 缓存命中 节省金额
system prompt(每次都发) 3000 0% ¥0
用户问题(每次不同) 500 0% ¥0
检索文档(每次不同) 2000 0% ¥0
总计 5500 0% —

但如果你启用了 Prompt Caching:

场景 单次请求 Token 数 Prompt 缓存命中 实际计费 节省
system prompt 3000 ✅ 命中缓存 按缓存价计费 90% off
用户问题 500 ❌ 原价 —
检索文档(部分重复) 2000 部分命中 部分优惠 ~50% off
总计 5500 — 约 2200 token 按原价 ~43%

每天调 10 万次,一个月能省多少钱不用我算了吧。


Prompt Caching 是什么?

简单说:大模型会把”经常重复出现的 prompt 片段”缓存起来,第二次遇到同样的内容时,就不用重新推理了,直接复用缓存结果——这是从底层推理引擎(Transformer)偷来的优化,不是简单的客户端缓存。

技术原理也不复杂。大模型推理时,每个 token 都要经过 Attention 计算。如果你连续两次请求的 prompt 有大量重复前缀(比如 system prompt、few-shot 示例),这些 token 的 Key-Value 矩阵是可以复用的。

三巨头缓存价格对比

厂商 缓存 Token 折扣 最小缓存长度 缓存有效期 支持状态
Anthropic Claude 90% off 1024 token 5 分钟(自动续) ✅ 已上线
OpenAI GPT 50% off 1024 token 5-10 分钟 ✅ 已上线
DeepSeek 官方支持中 待公布 待公布 🔄 即将上线
通义千问 50% off 1024 token 5 分钟 ✅ 已上线

Anthropic 的 90% off 折扣是最狠的——意味着你用 Claude 的成本几乎可以和 DeepSeek 打平。


实战一:Anthropic Claude 原生 Prompt Caching

最简写法:3 行代码搞定

import anthropic

client = anthropic.Anthropic(api_key="sk-ant-xxx")

response = client.beta.prompt_caching.messages.create(
    model="claude-sonnet-4-20250514",
    max_tokens=1024,
    system=[
        {
            "type": "text",
            "text": "你是一个资深 Python 后端工程师,擅长云原生开发和数据库优化。回答问题时给出可运行的代码示例,并在代码中添加中文注释。",
            "cache_control": {"type": "ephemeral"}  # 就这一行!
        }
    ],
    messages=[{"role": "user", "content": "帮我写一个 FastAPI 的中间件,记录每个请求的耗时"}]
)

print(response.content[0].text)
# 查看缓存命中情况
print(f"Input tokens: {response.usage.input_tokens}")
# 返回中会显示 cache_creation_input_tokens 和 cache_read_input_tokens
print(response.usage)

cache_control: {"type": "ephemeral"} 这一行告诉 Claude:“这个 system prompt 帮我缓存起来”。之后 5 分钟内的每一次请求,只要 system prompt 不变,这部分就按 10% 的价格计费。

更复杂的场景:缓存多轮对话的前几轮

import anthropic

client = anthropic.Anthropic(api_key="sk-ant-xxx")

def chat_with_cache(user_message, conversation_history=None):
    """带缓存的多轮对话"""

    messages = []

    # 缓存前面几轮的对话(比如缓存前 2 轮作为上下文锚点)
    if conversation_history and len(conversation_history) > 2:
        # 前 2 轮对话标记为可缓存
        cached_part = conversation_history[:2]
        uncached_part = conversation_history[2:]

        for msg in cached_part:
            messages.append({
                "role": msg["role"],
                "content": [{
                    "type": "text",
                    "text": msg["content"],
                    "cache_control": {"type": "ephemeral"}
                }]
            })

        for msg in uncached_part:
            messages.append({"role": msg["role"], "content": msg["content"]})

    # 添加新消息
    messages.append({"role": "user", "content": user_message})

    response = client.beta.prompt_caching.messages.create(
        model="claude-sonnet-4-20250514",
        max_tokens=1024,
        messages=messages
    )

    return {
        "reply": response.content[0].text,
        "cache_hit": response.usage.cache_read_input_tokens or 0,
        "cache_created": response.usage.cache_creation_input_tokens or 0,
        "total_input": response.usage.input_tokens
    }

# 模拟多轮对话
history = []

# 第一轮:创建缓存
result = chat_with_cache("什么是 Kubernetes 的 Service?", history)
print(f"第1轮 - 缓存创建: {result['cache_created']} token, 总输入: {result['total_input']}")
history.append({"role": "user", "content": "什么是 Kubernetes 的 Service?"})
history.append({"role": "assistant", "content": result["reply"]})

# 第二轮:命中缓存!
result = chat_with_cache("那 ClusterIP 和 NodePort 有什么区别?", history[:2])
print(f"第2轮 - 缓存命中: {result['cache_hit']} token, 总输入: {result['total_input']}")

运行结果大概是:

第1轮 - 缓存创建: 1520 token, 总输入: 1580
第2轮 - 缓存命中: 1520 token, 总输入: 1730  ← 1520 token 按 10% 计费!

实战二:LangChain 自动缓存

如果你用 LangChain,原生也已经支持了。不用手写 cache_control:

from langchain_anthropic import ChatAnthropic

# 创建带缓存的 Claude 模型
llm = ChatAnthropic(
    model="claude-sonnet-4-20250514",
    anthropic_api_key="sk-ant-xxx",
    max_tokens=1024,
    temperature=0,
)

# 用 LangChain 的缓存 API
from langchain_core.caches import InMemoryCache
from langchain_core.globals import set_llm_cache

from anthropic_cache import AnthropicPromptCache  # LangChain 的 Anthropic 缓存封装

# 启用 Anthropic 服务端 Prompt Caching
cache = AnthropicPromptCache()
set_llm_cache(cache)

# 第一次调用 —— 创建缓存
response = llm.invoke("写一个 Python 冒泡排序")
print(response.content)

# 第二次调用(同样的 system prompt 前缀)
# —— 自动命中缓存,输入 token 按 10% 计费
response = llm.invoke("写一个 Python 快速排序")
print(response.content)

更实用:用 LangChain 管理缓存策略

from langchain_core.prompts import ChatPromptTemplate
from langchain_anthropic import ChatAnthropic

# 把 system prompt 设计为"高速缓存友好"的结构
system_template = """
你是一个 {role},擅长 {skills}。

回答规则:
1. 代码要完整可运行
2. 添加中文注释
3. 遇到不确定的直接说不知道
"""

prompt = ChatPromptTemplate.from_messages([
    ("system", system_template),
    ("user", "{question}")
])

llm = ChatAnthropic(model="claude-sonnet-4-20250514", anthropic_api_key="sk-ant-xxx")

chain = prompt | llm

# 同一角色、不同问题 —— system prompt 部分命中缓存
for q in ["写个排序", "写个二分查找", "写个链表反转"]:
    response = chain.invoke({
        "role": "资深 Python 后端工程师",
        "skills": "算法和数据结构",
        "question": q
    })
    print(f"Q: {q} → {response.content[:50]}...")

进阶策略:5 个让缓存命中率翻倍的技巧

技巧 1:把 system prompt 放在请求最前面

缓存是按 前缀匹配 的。system prompt 放最前面,后面接入动态内容,确保前缀始终可命中:

# ✅ 正确:system prompt 在最前面
messages = [
    {"role": "system", "content": "你是一个 Python 专家..."},  # 缓存
    {"role": "user", "content": f"帮我写代码:{dynamic_content}"}  # 动态
]

# ❌ 错误:system prompt 在 user 后面(缓存失效)
messages = [
    {"role": "user", "content": f"参考这个:{dynamic_content}"},
    {"role": "system", "content": "你是一个 Python 专家..."}  # 没缓存!
]

技巧 2:工具定义也放缓存区

如果你已经定义了 tools 数组且不变,Anthropic 支持缓存工具定义:

tools = [
    {
        "name": "get_weather",
        "description": "获取指定城市的天气",
        "input_schema": {
            "type": "object",
            "properties": {"city": {"type": "string"}},
            "required": ["city"]
        },
        "cache_control": {"type": "ephemeral"}  # 缓存工具定义
    }
]

技巧 3:长文档分段缓存

RAG 场景中,如果每次检索出的 top-k 文档有一部分是重复的,把最常出现的文档标记为可缓存:

# 高频文档打缓存标记
cached_docs = []   # 出现 > 50% 的文档
fresh_docs = []    # 每次不同的新文档

for doc in retrieved_docs:
    if doc["frequency"] > 0.5:
        cached_docs.append({
            "type": "text",
            "text": doc["content"],
            "cache_control": {"type": "ephemeral"}
        })
    else:
        fresh_docs.append({"type": "text", "text": doc["content"]})

技巧 4:缓存最小 1024 token,内容要够长

Anthropic 的缓存只对 ≥ 1024 token 的内容生效。如果你的 system prompt 太短(比如只有 200 token),缓存不会触发。解决方案:把 system prompt 写详细一点,或者把一些固定的示例也放进去。

技巧 5:开发环境用客户端缓存,生产环境用服务端缓存

from functools import lru_cache

# 开发环境:Python 客户端缓存(完全免费)
@lru_cache(maxsize=100)
def cached_llm_call(prompt_hash):
    return call_llm(prompt_hash)

# 生产环境:Anthropic 服务端缓存
def production_llm_call(messages):
    return client.beta.prompt_caching.messages.create(
        model="claude-sonnet-4-20250514",
        max_tokens=1024,
        system=[{
            "type": "text", "text": SYSTEM_PROMPT,
            "cache_control": {"type": "ephemeral"}
        }],
        messages=messages
    )

真实成本对比:10 万次请求省多少?

方案 System Prompt User Prompt 缓存折扣 单次成本 10 万次总成本
Claude Sonnet 无缓存 3000 token 1500 token 0% ~$0.022 ~$2,200
Claude Sonnet 有缓存 3000 token 1500 token 90% off 缓存部分 ~$0.011 ~$1,100
DeepSeek-V3 无缓存 3000 token 1500 token — ~$0.002 ~$200
Claude + 缓存 — — — — 省 ~$1,100

Claude Sonnet 虽然比 DeepSeek-V3 贵,但启用缓存后差距缩小很多。如果你需要 Claude 的推理质量,但预算有限——缓存是必选项。


总结

Prompt Caching 不是玄学,是确定性优化:

  1. Anthropic Claude 支持最完善,system prompt + 工具定义 + 长上下文全部可缓存,90% off
  2. OpenAI GPT 也支持了,50% off,用法类似
  3. LangChain 已原生集成,开箱即用
  4. 缓存对用户完全透明,不需要改业务逻辑

下次你写 RAG 或者 Agent 应用时,花 5 分钟加个 cache_control,可能就省了 40% 的 API 费用。省下来的钱,够请团队喝一周的奶茶了。


👤 作者简介

一枚在大中原腹地(河南)卖公有云的从业者,主营腾讯云/阿里云/火山云,曾踩坑无数,现专注AI大模型应用落地。关注公众号「公有云cloud」,围观AI前沿动态~

博客:y1cloud.cn

发表评论