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_tokenscache_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% 的成本节省。

相关推荐
kujirashark1 小时前
CrewAI 完全学习手册
ai
李博士每天要洗澡1 小时前
AI Agent 怎样参与视频剪辑?从任务描述、MCP 到可编辑时间线
大数据·人工智能·ai·django·pygame
FIT2CLOUD飞致云2 小时前
GPU监控支持华为昇腾设备,防火墙管理焕新,虚拟机管理下放至专业版,1Panel v2.3.0版本发布
运维·ai·开源·1panel·运维面板
修电脑的猫11 小时前
在中国使用 Claude Code 解决 403 错误(VS CODE)
ai·sap
蚕豆糯米饭13 小时前
Github Copilot 研发效能提升实战指南
ai·github·copilot·ai编程
Token掘金室14 小时前
Aider配置自定义API教程
ai
Young丶16 小时前
讲透 Claude Code 系列 (四):Claude Skills 完全指南:可复用的“专业能力包”从入门到精通
人工智能·ai·ai编程·ai coding
小七-七牛开发者16 小时前
谷歌利用果蝇实现“AI 突围”?Cognition 再融 20 亿美元;AI 三巨头集体呼吁放慢脚步
ai·agent·token·skill·周一上线