一、先说清楚:缓存命中 vs 缓存未命中

1.1 一句话说清楚

大模型处理你的请求时,主要成本在计算而不是“读取”。如果这次请求的前缀内容和之前某次请求一模一样,平台就不用从头再算一遍,而是复用之前保存下来的中间结果。

  • 缓存命中(Cache Hit) :你的请求前缀已经被处理过并缓存了 → 直接复用 → 便宜 + 快

  • 缓存未命中(Cache Miss) :你的请求前缀没被处理过 → 从头计算 → 贵 + 慢

1.2 价格差距有多大?用真实数据说话

DeepSeek V4 Flash为例(平时价格):

类型 百万Token价格 备注
输入(缓存命中) 0.02元 便宜到几乎可以忽略
输入(缓存未命中) 1元 是命中价格的50倍
输出 2元 输出无法缓存

再看DeepSeek V4 Pro

类型 百万Token价格
输入(缓存命中) 0.025元
输入(缓存未命中) 3元
输出 6元

差距有多大? 同样是100万Token的输入,命中缓存只要2分钱,没命中要3块钱——差了120倍

缓存命中的价格通常是未命中的十分之一到百分之一,具体取决于模型和厂商。

1.3 缓存命中 vs 未命中:一张图看懂

二、模型是怎么判断缓存是否命中的?——命中逻辑架构

2.1 核心原则:精确前缀匹配(Exact Prefix Match)

所有主流模型的缓存判断逻辑,都遵循同一个核心原则:

缓存命中要求两次请求的“前缀”在字节级别完全一致。

“完全一致”的意思是——哪怕多一个空格、改一个标点,缓存都可能失效

2.2 三步判断流程

整个缓存判断过程可以分为三步:

2.3 不同厂商的缓存实现差异

OpenAI:内存缓存 + 扩展缓存

OpenAI的缓存机制:

  • 自动开启:所有API请求自动启用,无需改代码

  • 触发条件:提示词至少1024个Token才会触发缓存

  • 命中单位:以128个Token为增量进行缓存命中

  • 缓存时长

    • 内存缓存:5-10分钟不活跃即失效,最长1小时

    • 扩展缓存:最长24小时

  • 路由机制:基于前缀哈希值路由到特定机器

DeepSeek:硬盘缓存(全球首创)

DeepSeek采用硬盘缓存技术,是全球第一家在大范围API服务中采用硬盘缓存的大模型厂商:

  • 默认开启:所有用户默认享受,无需修改代码

  • 缓存落盘时机有三种:

    1. 请求边界落盘:每次请求结束位置产生缓存单元

    2. 公共前缀检测落盘:多次请求检测到公共前缀时独立落盘

    3. 固定Token间隔落盘:长输入按固定间隔截取缓存

  • 匹配规则:后续请求必须完整匹配缓存前缀单元才能命中

  • 查看命中:返回的usage字段包含prompt_cache_hit_tokensprompt_cache_miss_tokens

阿里云:显式缓存 vs 隐式缓存

阿里云提供两种模式:

模式 特点 命中价格
隐式缓存(自动) 系统自动识别公共前缀,无需配置 输入单价的20%
显式缓存(手动) 主动创建缓存块,确定性高 输入单价的10%

显式缓存需要主动添加cache_control标记:

{
  "role": "system",
  "content": [
    {"type": "text", "text": "这里放长文本...", 
     "cache_control": {"type": "ephemeral"}}
  ]
}

2.4 为什么说“缓存命中的判断是精确匹配”?

来看DeepSeek官方文档的两个例子:

例1:能命中

第一次请求:A + B
第二次请求:A + B + C  → 完整匹配 A+B → ✅ 命中

例2:第一次不能命中,但后续可以

第一次请求:A + B
第二次请求:A + C     → 不能完整匹配 A+B → ❌ 未命中
                     → 但系统检测到公共前缀 A,把它缓存了
第三次请求:A + D     → 完整匹配 A → ✅ 命中

关键洞察:缓存是动态学习的。即使某次没命中,系统也会从失败中提取公共前缀,为后续请求创造命中机会。

三、怎么判断你的请求是否命中了缓存?

3.1 查看API返回的usage字段

大多数模型API会在返回中告诉你缓存命中情况。

DeepSeek API返回示例

{
  "usage": {
    "prompt_tokens": 1500,
    "prompt_cache_hit_tokens": 1200,   // ← 命中了1200个Token
    "prompt_cache_miss_tokens": 300,   // ← 没命中300个Token
    "completion_tokens": 200,
    "total_tokens": 1700
  }
}

计费公式

费用 = 命中Token数 × 命中单价 + 未命中Token数 × 未命中单价 + 输出Token数 × 输出单价

3.2 VSCode代码:解析缓存命中情况

import requests
import json

def call_with_cache_check(prompt, api_key, model="deepseek-chat"):
    """
    调用API并检查缓存命中情况
    """
    url = "https://api.deepseek.com/v1/chat/completions"
    headers = {
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json"
    }
    data = {
        "model": model,
        "messages": [
            {"role": "system", "content": "你是一个乐于助人的助手"},
            {"role": "user", "content": prompt}
        ]
    }
    
    response = requests.post(url, headers=headers, json=data)
    result = response.json()
    
    # 提取缓存命中信息
    usage = result.get("usage", {})
    hit_tokens = usage.get("prompt_cache_hit_tokens", 0)
    miss_tokens = usage.get("prompt_cache_miss_tokens", 0)
    total_prompt = usage.get("prompt_tokens", 0)
    
    # 计算命中率
    if total_prompt > 0:
        hit_rate = (hit_tokens / total_prompt) * 100
    else:
        hit_rate = 0
    
    print(f"📊 缓存命中统计:")
    print(f"   - 命中Token: {hit_tokens}")
    print(f"   - 未命中Token: {miss_tokens}")
    print(f"   - 总输入Token: {total_prompt}")
    print(f"   - 命中率: {hit_rate:.1f}%")
    
    # 估算费用(以DeepSeek V4 Flash为例)
    hit_price = 0.02 / 1_000_000   # 0.02元/百万Token
    miss_price = 1.0 / 1_000_000   # 1元/百万Token
    cost = hit_tokens * hit_price + miss_tokens * miss_price
    print(f"   - 预估费用: {cost:.6f}元")
    
    return result

# 使用示例
# 第一次调用:构建缓存
result1 = call_with_cache_check("中国的首都是哪里?", "your-api-key")

# 第二次调用:相同前缀,应该命中缓存
result2 = call_with_cache_check("中国的首都是哪里?请详细介绍一下", "your-api-key")

代码解析

  • prompt_cache_hit_tokens:命中的Token数,按低价计费

  • prompt_cache_miss_tokens:未命中的Token数,按高价计费

  • 命中率 = 命中Token ÷ 总输入Token

四、怎么提高缓存命中率?——实战技巧

4.1 黄金法则:稳定的放前面,变化的放后面

这是最核心、最有效的一条原则:

❌ 错误做法(动态内容在前):
[当前时间: 2026-07-07 10:30] + [系统提示词] + [用户问题]
→ 时间每分每秒都在变 → 缓存永远失效

✅ 正确做法(稳定内容在前):
[系统提示词] + [工具定义] + [项目规则] + [用户问题]
→ 前三项长期不变 → 缓存持续命中

对于Coding Agent来说,请求结构通常是这样:

[tools] 工具定义        ← 长期稳定
[system prompt] 系统提示  ← 长期稳定
[project rules] 项目规则  ← 长期稳定
[conversation history]    ← 相对稳定(追加内容)
[tool results] 工具结果   ← 每轮不同
[new user message] "继续" ← 真正新增的只有这几个字

越稳定越靠前,缓存越管用;越动态越靠前,缓存越容易废

4.2 避免动态内容破坏缓存

以下内容只要出现在请求前缀位置,就会破坏缓存命中

动态内容 为什么破坏缓存
当前时间戳 每分每秒都在变
随机Request ID 每次请求都不同
重排过的工具列表 顺序一变,前缀就变了
动态拼接的System Prompt 每次可能多一句话

解决方案:把这些动态内容放到请求的最后面,不要放在开头。

4.3 使用缓存Key(OpenAI)

OpenAI提供了prompt_cache_key参数,可以手动控制路由:

# 相同cache_key的请求会被路由到同一台机器
response = client.chat.completions.create(
    model="gpt-4o",
    messages=messages,
    prompt_cache_key="my-app-v1"  # ← 手动指定缓存Key
)

这样即使前缀略有不同,只要prompt_cache_key相同,请求也会被路由到同一台机器,提高命中概率。

4.4 多轮对话中保持上下文连贯

在多轮对话中,把历史对话放在前面,新问题放在最后

# ✅ 好的做法:历史在前,新问题在后
messages = [
    {"role": "system", "content": system_prompt},      # 稳定
    {"role": "user", "content": "第一轮问题"},          # 已固化
    {"role": "assistant", "content": "第一轮回答"},     # 已固化
    {"role": "user", "content": "第二轮问题"},          # 已固化
    {"role": "assistant", "content": "第二轮回答"},     # 已固化
    {"role": "user", "content": "新问题"}              # ← 只有这个是新增的
]

前面的内容因为已经处理过并被缓存,新增的只有最后一小段,大部分Token都能命中缓存

五、总结:一张表看懂全部

维度 缓存命中 缓存未命中
发生了什么 复用之前计算好的中间结果 从头开始计算
价格 极低(通常为未命中的10%-20%) 正常价格
速度 快(首Token延迟降低80%)
DeepSeek V4 Flash 0.02元/百万Token 1元/百万Token
DeepSeek V4 Pro 0.025元/百万Token 3元/百万Token
判断方式 请求前缀精确匹配缓存 请求前缀没有匹配项
命中条件 稳定内容在前 + 动态内容在后 动态内容破坏了前缀匹配

小编建议

  1. 检查API返回的usage字段:看prompt_cache_hit_tokensprompt_cache_miss_tokens,知道自己到底命中率多少

  2. 重排你的Prompt结构:稳定内容放前面,动态内容放后面

  3. 避免在请求前缀插入时间戳、随机ID:这些会让缓存完全失效

  4. 多轮对话中保持历史上下文顺序:新问题追加在最后

  5. 善用缓存Key(如有) :把相同类型的请求路由到同一台机器

小编团队自主研发  大型文件翻译、润色文本链接

https://blog.csdn.net/weixin_54524403/article/details/162078447?spm=1011.2415.3001.10575&sharefrom=mp_manage_linkhttps://blog.csdn.net/weixin_54524403/article/details/162078447?spm=1011.2415.3001.10575&sharefrom=mp_manage_link

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐