Prompt Caching 完全指南:从入门到精通,掌握 LLM 缓存成本优化

Prompt Caching 完全指南:从入门到精通,掌握 LLM 缓存成本优化

一、什么是 Prompt Caching?

1.1 一句话定义

Prompt Caching(提示缓存)是 AI API 提供商推出的一项成本与延迟优化功能。当你多次发送包含相同前缀内容的请求时,系统会缓存已处理过的 token 计算结果,后续请求只需支付极低的缓存读取费用,从而避免对相同内容的重复计算。

Prompt Caching 的核心逻辑是 "缓存不变前缀,仅处理动态内容" 。通过设置缓存断点,服务器会将断点之前的所有内容(如系统提示词、工具定义、长文档)缓存起来。下次请求时,只要前缀完全一致,就直接读取缓存,无需重新处理,仅对新增的动态内容计费。

1.2 它解决什么问题

痛点 现状 Prompt Caching 的解法
重复计费 每次请求都重新发送相同的系统提示词和工具定义,每次都全额付费 缓存固定前缀,后续请求仅支付缓存读取费用(低至 10%)
响应缓慢 长提示词的首次 Token 延迟(TTFT)居高不下 缓存命中时无需重新解析固定内容,TTFT 可降低 80%
成本失控 高频调用场景下,固定内容的 Token 消耗占比极高 缓存读取费用仅为标准输入价格的 0.1 倍,节省高达 90%
长上下文昂贵 RAG 场景中反复传递长文档,成本极高 文档缓存后,反复查询仅需支付极低的读取费用

一个直观的成本对比:若系统提示词为 5000 Token,每天调用 1000 次------

  • 无缓存 :5000 Token × 1000 次 × 3/百万 Token = **15/天**
  • 有缓存 :1 次写入(0.01875)+ 999 次读取(约 1.50)≈ $1.52/天
  • 每天节省 13.48,每月节省超过 400

1.3 核心原理:前缀匹配

Prompt Caching 的工作方式是前缀匹配(prefix matching) 。API 会从请求开头开始缓存,一直缓存到每个缓存断点为止。缓存命中要求字节级完全一致的前缀匹配------哪怕多一个空格或大小写不同,都会导致缓存未命中。

复制代码
请求 1: [系统提示词] + [用户: "问题 1"]
         └── 已缓存 ──┘
请求 2: [系统提示词] + [用户: "问题 2"]
         └── 缓存命中 ──┘(仅此部分全额计费)

系统使用提示词内容的加密哈希生成缓存键。只有内容完全相同的请求才能实现缓存命中。

注:

博客:

https://blog.csdn.net/badao_liumang_qizhi

二、各平台缓存支持对比

平台 缓存类型 缓存折扣 最小缓存量 缓存时效 缓存写入费用
Anthropic (Claude) 显式缓存(需手动标记) 90% 折扣 1,024 tokens 5 分钟 / 1 小时 1.25x(5分钟)/ 2x(1小时)
OpenAI (GPT) 自动缓存(无需设置) 50% 折扣 1,024 tokens 5-60 分钟 无额外费用(GPT-5.6 前)
Google (Gemini) Context Caching 75% 折扣 32,768 tokens 自定义 按存储时间计费
AWS Bedrock 显式缓存 90% 折扣 1,024 tokens 5 分钟 / 1 小时 1.25x / 2x
Azure OpenAI 自动 + 显式 折扣费率 1,024 tokens 30 分钟 标准部署无写入费

关键差异:

  • Anthropic/Claude :需要显式使用 cache_control 标记缓存断点,最多支持 4 个缓存断点 ,缓存读取享受 90% 折扣(即支付 0.1 倍价格)
  • OpenAI :自动缓存,无需代码改动,对所有 ≥1024 tokens 的请求生效,缓存读取享受 50% 折扣
  • Azure OpenAI:GPT-5.6 系列之前无缓存写入费用,GPT-5.6 及以后版本可能产生缓存写入费用

三、Spring AI 中的 Prompt Caching 配置

3.1 环境要求

组件 要求
Spring AI 1.1.0+(Anthropic 缓存支持)
Java 17+
Spring Boot 3.2+
模型 Anthropic Claude(通过 Anthropic API 或 AWS Bedrock)

3.2 Anthropic Claude 缓存策略

Spring AI 提供了 5 种预置缓存策略 (AnthropicCacheStrategy 枚举),自动处理缓存断点的放置,尊重 Anthropic 的 4 断点限制:

策略 缓存内容 断点数 适用场景
SYSTEM_ONLY 仅系统提示词 1 系统提示词较长且固定
TOOLS_ONLY 仅工具定义 1 工具定义复杂且固定
SYSTEM_AND_TOOLS 系统提示词 + 工具定义 2 两者都长且固定(推荐)
CONVERSATION_HISTORY 对话历史 1-4 多轮对话场景
NONE 不缓存 0 一次性请求

3.3 Spring AI 配置示例

java 复制代码
@Service
public class CachedChatService {

    private final ChatClient chatClient;

    public CachedChatService(ChatClient.Builder builder) {
        this.chatClient = builder.build();
    }

    public String askWithCache(String question) {
        // 配置 Anthropic 缓存策略
        AnthropicChatOptions options = AnthropicChatOptions.builder()
            .model("claude-sonnet-4-5-20250929")
            .strategy(AnthropicCacheStrategy.SYSTEM_AND_TOOLS)  // 同时缓存系统提示词和工具定义
            .cacheTtl(AnthropicCacheTtl.FIVE_MINUTES)            // 5 分钟 TTL(默认)
            .build();

        return chatClient.prompt()
            .system("你是一个专业的法律文档分析师...(约 5000 字的系统提示词)")
            .user(question)
            .options(options)
            .call()
            .content();
    }
}

核心配置项说明:

配置项 说明 默认值
strategy(AnthropicCacheStrategy) 缓存策略 CONVERSATION_HISTORY
cacheTtl(AnthropicCacheTtl) 缓存有效期 FIVE_MINUTES(5 分钟)
multiBlockSystemCaching(true) 多块系统缓存支持 false

3.4 AWS Bedrock 缓存配置

java 复制代码
@Service
public class BedrockCachedService {

    private final ChatClient chatClient;

    public String askWithCache(String question) {
        BedrockCacheOptions cacheOptions = BedrockCacheOptions.builder()
            .strategy(BedrockCacheStrategy.SYSTEM_ONLY)
            .ttl(BedrockCacheTtl.ONE_HOUR)  // 1 小时 TTL
            .build();

        return chatClient.prompt()
            .system("长系统提示词...")
            .user(question)
            .options(BedrockChatOptions.builder()
                .model("anthropic.claude-sonnet-4-5-20250929-v1:0")
                .cacheOptions(cacheOptions)
                .build())
            .call()
            .content();
    }
}

3.5 缓存使用量追踪

Spring AI 提供了缓存使用量的追踪字段,每次响应中都会返回:

java 复制代码
ChatResponse response = chatClient.prompt(...).call().chatResponse();

// 获取缓存使用情况
Usage usage = response.getMetadata().getUsage();
// cache_creation_input_tokens:缓存写入的 token 数
// cache_read_input_tokens:缓存读取的 token 数

// 计算缓存命中率
double hitRate = (double) usage.getCacheReadInputTokens() /
    (usage.getCacheReadInputTokens() + usage.getCacheCreationInputTokens());

四、LangChain4j 中的 Prompt Caching 配置

4.1 依赖要求

LangChain4j 从 1.11.0 开始原生支持 Prompt Caching,1.21.0 版本进一步增强了 OpenAI 缓存支持。

xml 复制代码
<dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-bedrock</artifactId>
    <version>1.11.0+</version>
</dependency>

4.2 配置示例

java 复制代码
// AWS Bedrock 缓存配置
BedrockChatRequestParameters params = BedrockChatRequestParameters.builder()
    .promptCaching(BedrockCachePointPlacement.AFTER_SYSTEM)  // 在系统消息后放置缓存点
    .build();

ChatModel model = BedrockChatModel.builder()
    .modelId("anthropic.claude-sonnet-4-5-20250929-v1:0")
    .defaultRequestParameters(params)
    .build();

支持的缓存点位置 :AFTER_SYSTEM(系统消息后)、AFTER_USER_MESSAGE(用户消息后)、AFTER_TOOLS(工具定义后)。

五、提升缓存命中率的实战技巧

5.1 核心原则:固化核心 System Prompt

Prompt Caching 的核心逻辑是把重复使用的 System Prompt 和常用上下文标记为缓存,后续相同请求命中缓存后,这部分 Token 按折扣价计费。缓存策略优化的三个关键是:固化核心 System Prompt、动态内容后置、分层设计 Prompt。把跨请求不变的核心指令放在 Prompt 最前面并保持绝对不变,把时间戳、会话 ID 等动态变量移到 User Message 中。

5.2 使用 prompt_cache_key 提升命中率

在 GPT-5.6 及后续模型家族中,设置 prompt_cache_key 参数并重复使用相同的键,可以改善共享长提示前缀的请求的缓存匹配。

json 复制代码
{
  "model": "your-model",
  "prompt_cache_key": "conv-6900xxxx",
  "messages": [
    {"role": "system", "content": "长系统提示词..."},
    {"role": "user", "content": "用户问题"}
  ]
}

关键实践 :不同对话使用不同的 prompt_cache_key,避免缓存污染。同一个会话中始终使用相同的键。

5.3 监控缓存命中率

像监控 uptime 一样监控缓存命中率。 对 cache break 做告警,并把它当成事故来处理------哪怕只是几个百分点的 miss rate。

java 复制代码
@Component
public class CacheMetrics {

    private final MeterRegistry registry;

    public void recordCacheUsage(String model, int cachedTokens, int totalTokens) {
        double hitRate = (double) cachedTokens / totalTokens;

        registry.gauge("prompt.cache.hit_rate",
            Tags.of("model", model), hitRate);

        if (hitRate < 0.8) {
            log.warn("缓存命中率低于 80%:{},model={}", hitRate, model);
        }
    }
}

5.4 避免常见陷阱

陷阱 后果 正确做法
每次请求修改系统提示词 缓存永远无法命中 将动态内容移至 User Message
中途切换工具或模型 缓存失效 用工具表达状态变化,不修改系统提示词
前缀中存在时间戳/会话 ID 每次请求前缀不同 将动态变量放在缓存断点之后
缓存前缀小于 1024 tokens 不满足最小缓存条件 确保缓存内容 ≥1024 tokens
频繁写入缓存 写入成本(1.25x)超过节省 提高缓存读取频率,而非频繁写入

六、成本节省计算实战

6.1 计算公式

复制代码
首次请求成本 = (tokens × base_rate × 1.25)          // 缓存写入(25% 溢价)
后续请求成本 = (new_tokens × base_rate) + (cached_tokens × base_rate × 0.1)
节省金额 = 原始成本 - 缓存后成本

6.2 完整计算示例

场景:法律文档分析系统,系统提示词 10,000 Token,每天处理 500 次查询。

java 复制代码
public class CacheCostCalculator {

    public static void main(String[] args) {
        double baseRate = 3.0;          // $3/百万 Token
        int systemTokens = 10_000;      // 系统提示词 Token 数
        int dailyQueries = 500;         // 每日查询次数
        int avgUserTokens = 200;        // 平均用户问题 Token 数

        // 无缓存成本
        double noCacheCost = (systemTokens + avgUserTokens) / 1_000_000.0
            * baseRate * dailyQueries;

        // 有缓存成本
        double writeCost = systemTokens / 1_000_000.0 * baseRate * 1.25; // 首次写入
        double readCost = (dailyQueries - 1) * systemTokens / 1_000_000.0
            * baseRate * 0.1;                                             // 后续读取
        double userCost = dailyQueries * avgUserTokens / 1_000_000.0 * baseRate;
        double cachedCost = writeCost + readCost + userCost;

        System.out.printf("无缓存日成本: $%.2f%n", noCacheCost);
        System.out.printf("有缓存日成本: $%.2f%n", cachedCost);
        System.out.printf("日节省: $%.2f (%.1f%%)%n",
            noCacheCost - cachedCost,
            (noCacheCost - cachedCost) / noCacheCost * 100);
        System.out.printf("月节省: $%.2f%n", (noCacheCost - cachedCost) * 30);
    }
}

输出结果:

复制代码
无缓存日成本: $15.30
有缓存日成本: $0.94
日节省: $14.36 (93.9%)
月节省: $430.80

七、实战示例:长上下文 RAG 缓存优化

7.1 场景描述

企业 RAG 管道处理大量法律文档或代码库,每次查询都需传递同一份长文档。通过 Prompt Caching,文档只需缓存一次,后续查询仅支付极低的读取费用。

7.2 完整实现

java 复制代码
@Service
public class CachedRagService {

    private final ChatClient chatClient;
    private final JtokkitTokenCounter tokenCounter;

    public CachedRagService(ChatClient.Builder builder) {
        this.chatClient = builder.build();
        this.tokenCounter = new JtokkitTokenCounter();
    }

    public String queryWithCachedDocument(String document, String question) {
        int docTokens = tokenCounter.countTokens(document);

        // 确保文档满足最小缓存要求(1024 tokens)
        if (docTokens < 1024) {
            return chatClient.prompt()
                .system("基于以下文档回答问题:\n" + document)
                .user(question)
                .call().content();
        }

        // 使用 Anthropic 缓存策略,将文档作为系统提示词缓存
        AnthropicChatOptions options = AnthropicChatOptions.builder()
            .strategy(AnthropicCacheStrategy.SYSTEM_ONLY)
            .cacheTtl(AnthropicCacheTtl.ONE_HOUR)  // 长文档使用 1 小时 TTL
            .build();

        return chatClient.prompt()
            .system("基于以下文档回答问题:\n\n" + document)
            .user(question)
            .options(options)
            .call()
            .content();
    }
}

7.3 与 Promptfoo 集成进行缓存效果评估

在 CI/CD 中,可以用 Promptfoo 测试缓存前后的成本与效果差异:

yaml 复制代码
# promptfooconfig.yaml
prompts:
  - file://prompts/cached-rag-v1.txt

providers:
  - id: anthropic:claude-sonnet-4-5
    config:
      cache:
        enabled: true
        strategy: SYSTEM_ONLY

tests:
  - vars:
      document: file://docs/long-legal-doc.txt
      question: "这份合同的违约责任条款是什么?"
    assert:
      - type: llm-rubric
        value: "回答应准确引用合同中的违约责任条款"

八、最佳实践总结

实践 说明 预期收益
固化 System Prompt 跨请求不变的核心指令放在最前面并保持绝对不变 缓存命中率 > 90%
动态内容后置 时间戳、会话 ID 等动态变量移至 User Message 避免缓存失效
缓存内容 ≥1024 tokens 满足最小缓存条件 确保缓存生效
使用 prompt_cache_key 为共享前缀的请求使用相同键 提升缓存匹配率
监控命中率 记录 cache_read / cache_creation 比例 及时发现命中率下降
发版前预热 少量模拟请求提前构建缓存 避免突增流量导致 miss
分层设计 Prompt 固定层(缓存)+ 可变层(不缓存) 最大化缓存收益
选择合适 TTL 5 分钟(默认)vs 1 小时(长任务) 平衡写入成本与读取收益

九、常见问题

Q1:Prompt Caching 会自动生效吗?

A:OpenAI 的缓存是自动的,无需代码改动。Anthropic 的缓存需要显式使用 cache_control 标记(Spring AI 通过 AnthropicCacheStrategy 自动处理)。

Q2:缓存的最小 Token 数是多少?

A:Anthropic 和 OpenAI 都是 1,024 tokens ,Gemini 是 32,768 tokens。小于最小值的提示词不会被缓存。

Q3:缓存命中要求完全一致吗?

A:是的,需要字节级完全一致的前缀匹配------哪怕多一个空格或大小写不同,都会导致缓存未命中。

Q4:Spring AI 中如何知道缓存是否命中?

A:通过 Usage 对象中的 cache_creation_input_tokens 和 cache_read_input_tokens 字段。如果 cache_read_input_tokens > 0,说明有缓存命中。

Q5:缓存 TTL 过了怎么办?

A:TTL 过期后,下一次请求会创建新的缓存条目,按缓存写入费率计费。频繁读取的缓存会在每次读取时刷新 TTL,保持活跃。

Q6:Java 团队如何快速验证缓存效果?

A:用 Promptfoo 在 CI 中运行缓存前后的对比测试,或直接在 Java 代码中记录 cache_read_input_tokens 并计算命中率。

十、总结

维度 内容
核心原理 前缀匹配,缓存不变内容,仅处理动态部分
最大收益 成本节省 90% ,TTFT 降低 80%
最小缓存量 1,024 tokens(Anthropic/OpenAI)
Spring AI 支持 AnthropicCacheStrategy + BedrockCacheOptions
LangChain4j 支持 1.11.0+ 原生支持
关键实践 固化 System Prompt、动态内容后置、监控命中率
入门门槛 低------Spring AI 一行配置即可启用

核心价值:Prompt Caching 是将 LLM 应用从"能用"推向"经济高效"的关键优化技术。它让长上下文 RAG、多轮对话、Agent 工作流等场景的成本从"难以承受"变为"可持续运营"。对于 Java 团队,Spring AI 和 LangChain4j 都已提供了开箱即用的缓存支持,只需几行配置即可享受最高 90% 的成本节省。

相关推荐
菜_小_白42 分钟前
codex
linux·vscode·ai
孙启超1 小时前
【FDE开发指南】第 5 课:中国市场的 FDE
人工智能·ai·职场技能
空心木偶☜2 小时前
Langgraph操作时常见的错误
python·ai·ai编程·langgraph
c萱3 小时前
AI产品经理——03Prompt Engineering提示词工程
ai·prompt·aigc·产品经理·ai编程·ai-native
xuhe23 小时前
Codex解决新模型无法使用/model选择的问题
linux·ai·codex
我才是银古6 小时前
AI 啃 DWG图纸:从二维到三维
ai·cad·dwg
codigger7 小时前
把 AI Agent 养在自己电脑上:从本地部署到远程接管的一份完整思路
ai·编程·#人工智能·#agent
空心木偶☜7 小时前
LangGraph
ai·ai编程·langgraph
空心木偶☜7 小时前
langgraph加上“循环”和“记忆”
ai·ai编程·langgraph
Martina_03217 小时前
AI生成的盔甲换动作后漂移?用6步检查挂点、骨架映射与 Bind Pose
人工智能·游戏·3d·ai·自然语言处理·aigc·游戏策划