领先一步
VMware 提供培训和认证,助您加速进步。
了解更多当应用程序重复发送相同的提示内容时,大型语言模型 API 成本会迅速累积。一个典型场景:您正在构建一个文档分析器,每次请求都包含一个 3,000 令牌的文档。关于该文档的五个问题意味着以全价处理 15,000 令牌的相同内容。
Anthropic 的提示缓存通过允许您重用以前处理过的提示段来解决此问题。Spring AI 通过策略性缓存模式提供全面支持,这些模式会自动处理缓存断点放置和管理。
在这篇博文中,我们将解释提示缓存的工作原理、何时使用它,以及 Spring AI 如何简化其实现。
提示缓存允许您标记提示中的部分内容,以便在多个 API 请求中重复使用。启用后,Anthropic 会缓存指定内容,并在后续请求中对缓存部分收取较低费用。
缓存通过精确前缀匹配进行操作。考虑以下序列
Request 1: [System Prompt] + [User: "Question 1"]
└─ Cached ──┘
Request 2: [System Prompt] + [User: "Question 2"]
└─ Cache Hit ─┘ (Only this part incurs full cost)
系统使用提示内容(直到指定的缓存控制点)的加密哈希生成缓存键。只有内容完全相同的请求才能实现缓存命中——即使是一个字符的更改也会创建一个新的缓存条目。
Request 1: "You are a helpful assistant." → Cache created
Request 2: "You are a helpful assistant." → Cache hit
Request 3: "You are a helpful assistant " → Cache miss (extra space)
Request 4: "You are a Helpful assistant." → Cache miss (capitalization)
缓存未命中时,系统会以标准费率处理内容并创建新的缓存条目。这就是为什么保持一致的提示模板对于有效缓存很重要。
性能影响:除了节省成本外,Anthropic 报告称,对于长提示,延迟可降低高达 85%。在他们的公告中,一个 100K 令牌的书籍示例显示,启用缓存后,响应时间从 11.5 秒降至 2.4 秒。请注意,实际的延迟改进取决于您缓存的内容量和缓存命中率。
缓存生命周期:条目在 TTL 窗口(默认 5 分钟,可选 1 小时)内每次使用时都会刷新。TTL 过期后,下一次请求会创建一个新的缓存条目。
定价因模型层级而异
| 模型 | 基础输入 | 缓存写入 | 缓存读取 | 节省 |
|---|---|---|---|---|
| Claude Sonnet 4.5 | $3/MTok | $3.75/MTok (+25%) | $0.30/MTok | 90% |
| Claude Sonnet 4 | $3/MTok | $3.75/MTok (+25%) | $0.30/MTok | 90% |
| Claude Opus 4.1 | $15/MTok | $18.75/MTok (+25%) | $1.50/MTok | 90% |
| Claude Opus 4 | $15/MTok | $18.75/MTok (+25%) | $1.50/MTok | 90% |
| Claude Haiku 4.5 | $1/MTok | $1.25/MTok (+25%) | $0.10/MTok | 90% |
| Claude Haiku 3.5 | $0.80/MTok | $1/MTok (+25%) | $0.08/MTok | 90% |
| Claude Haiku 3 | $0.25/MTok | $0.30/MTok (+25%) | $0.03/MTok | 90% |
本博客中的所有代码示例均使用 Claude Sonnet 4.5 定价,除非另有说明。
有关定价的更多信息,请参阅 - Anthropic 提示缓存定价
示例计算 (Claude 3.5 Sonnet, 5,000 令牌)
最小令牌阈值因模型而异
| 模型 | 最小可缓存令牌 |
|---|---|
| Claude Sonnet 4.5、Claude Sonnet 4、Claude Opus 4.1、Claude Opus 4 | 1,024 |
| Claude Haiku 3.5、Claude Haiku 3 | 2,048 |
| Claude Haiku 4.5 | 4,096 |
低于这些阈值的提示无法缓存,即使您使用缓存控制指令标记它们也是如此。请注意,与其他模型相比,Claude Haiku 4.5 需要更多的令牌(4,096)。
有关这方面的更多信息,请参阅 Anthropic 的可缓存提示长度限制。
其他限制
Anthropic 以特定顺序处理请求组件,此顺序决定了缓存失效的工作方式。
┌─────────────────────────────────────────┐
│ Request Processing Order: │
│ │
│ 1. Tools │
│ ↓ │
│ 2. System Message │
│ ↓ │
│ 3. Message History │
└─────────────────────────────────────────┘
更改通过此层次结构向下级联
┌──────────────────────────────────────────────────────┐
│ Cascade Invalidation: │
│ │
│ Change Tools → Invalidates: Tools, System, Msgs │
│ Change System → Invalidates: System, Msgs │
│ Change Messages → Invalidates: Msgs only │
└──────────────────────────────────────────────────────┘
理解此行为对于选择缓存策略至关重要。层次结构中较高组件的更改会使所有下游缓存失效,这直接影响您的缓存命中率。
Spring AI 无需您手动放置缓存断点(这可能容易出错且繁琐),而是通过 AnthropicCacheStrategy 枚举提供了五种战略模式。每种策略都会自动处理缓存控制指令的放置,同时尊重 Anthropic 的 4 断点限制。
| 策略 | 断点 | 缓存内容 | 典型用例 |
|---|---|---|---|
NONE |
0 | 无 | 一次性请求,测试 |
仅系统消息 |
1 | 系统消息 | 稳定的系统提示,少于 20 个工具 |
仅工具 |
1 | 工具定义 | 大型工具,动态系统提示 |
系统和工具 |
2 | 工具 + 系统 | 20 多个工具,两者都稳定 |
会话历史记录 |
1-4 | 完整会话 | 多轮对话 |
让我们通过实际示例来看看每种策略。
此策略缓存系统消息内容。由于工具在 Anthropic 的请求层次结构中出现在系统消息之前(工具 → 系统 → 消息),当您在系统消息上放置缓存断点时,它们会自动成为缓存前缀的一部分。
重要:由于缓存层次结构,更改任何工具定义都会使系统缓存失效。
String systemPrompt = """
You are an expert software architect specializing in distributed systems.
You have deep knowledge of microservices, event-driven architecture, and cloud-native patterns.
When analyzing systems, consider scalability, resilience, and maintainability.
[... additional context ...]
""";
ChatResponse response = chatModel.call(
new Prompt(
List.of(
new SystemMessage(systemPrompt),
new UserMessage("What is microservices architecture?")
),
AnthropicChatOptions.builder()
.model(AnthropicApi.ChatModel.CLAUDE_3_5_SONNET)
.cacheOptions(AnthropicCacheOptions.builder()
.strategy(AnthropicCacheStrategy.SYSTEM_ONLY)
.build())
.maxTokens(500)
.build()
)
);
// Access cache metrics
AnthropicApi.Usage usage = (AnthropicApi.Usage) response.getMetadata()
.getUsage().getNativeUsage();
if (usage != null) {
System.out.println("Cache creation: " + usage.cacheCreationInputTokens());
System.out.println("Cache read: " + usage.cacheReadInputTokens());
}
在第一次请求时,cacheCreationInputTokens 将大于零,cacheReadInputTokens 将为零。后续使用相同系统提示的请求将显示 cacheCreationInputTokens 为零,cacheReadInputTokens 为正值。这是您验证缓存是否在应用程序中按预期工作的方式。
当您的系统提示很大(达到最小令牌阈值)且稳定,但用户问题各不相同时,请使用此策略。
此策略缓存工具定义,同时在每个请求上重新处理系统消息。在工具共享但系统提示需要定制的多租户场景中,此用例变得清晰。
考虑一个服务多个组织的 SaaS 应用程序
@Service
public class MultiTenantAIService {
private final List<FunctionCallback> sharedTools = List.of(
weatherTool, // 500 tokens
calendarTool, // 800 tokens
emailTool, // 700 tokens
analyticsTool, // 600 tokens
reportingTool, // 900 tokens
// ... 15 more tools totaling 5,000+ tokens
);
public String handleRequest(String tenantId, String userQuery) {
TenantConfig config = tenantRepository.findById(tenantId);
// Each tenant requires a unique system prompt
String systemPrompt = """
You are %s's AI assistant.
Company values: [custom data]
Brand voice: [custom data]
Compliance requirements: [custom data]
""";
ChatResponse response = chatModel.call(
new Prompt(
List.of(
new SystemMessage(systemPrompt),
new UserMessage(userQuery)
),
AnthropicChatOptions.builder()
.model(AnthropicApi.ChatModel.CLAUDE_3_5_SONNET)
.cacheOptions(AnthropicCacheOptions.builder()
.strategy(AnthropicCacheStrategy.TOOLS_ONLY)
.build())
.toolCallbacks(sharedTools)
.maxTokens(800)
.build()
)
);
return response.getResult().getOutput().getText();
}
}
以下是发生的情况
所有租户共享相同的缓存工具定义,而每个租户接收其定制的系统提示。对于 5,000 令牌工具集,这意味着创建缓存只需支付一次 $0.01875,然后每次请求支付 $0.0015 用于缓存读取——无论哪个租户发出请求。
此策略创建两个独立的缓存断点:一个用于工具(断点 1),一个用于系统消息(断点 2)。当您拥有 20 多个工具或需要对这两个组件进行确定性缓存时,这种分离很重要。
关键优势:更改系统消息不会使工具缓存失效。
ChatResponse response = chatModel.call(
new Prompt(
List.of(
new SystemMessage(systemPrompt),
new UserMessage(userQuery)
),
AnthropicChatOptions.builder()
.model(AnthropicApi.ChatModel.CLAUDE_3_5_SONNET)
.cacheOptions(AnthropicCacheOptions.builder()
.strategy(AnthropicCacheStrategy.SYSTEM_AND_TOOLS)
.build())
.toolCallbacks(toolCallbacks)
.maxTokens(500)
.build()
)
);
缓存键的工作方式如下
hash(tools)hash(tools + system)级联行为
这使得 SYSTEM_AND_TOOLS 在您的系统提示更改比工具更频繁时成为理想选择,从而实现工具缓存的有效重用。
对于多轮对话,此策略会增量缓存整个对话历史记录。Spring AI 在对话历史记录中的最后一条用户消息上放置一个缓存断点。这在构建对话式 AI 应用程序(例如聊天机器人、虚拟助手和客户支持系统)时特别有用。
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultSystem("You are a personalized career counselor with 20 years of experience...")
.defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory)
.conversationId(conversationId)
.build())
.build();
String response = chatClient.prompt()
.user("What career advice would you give me based on our conversation?")
.options(AnthropicChatOptions.builder()
.model(AnthropicApi.ChatModel.CLAUDE_3_5_SONNET)
.cacheOptions(AnthropicCacheOptions.builder()
.strategy(AnthropicCacheStrategy.CONVERSATION_HISTORY)
.build())
.maxTokens(500)
.build())
.call()
.content();
缓存随着对话的进行而增量增长
Turn 1: Cache [U1]
Turn 2: Reuse [U1], cache [U1 + A1 + U2]
Turn 3: Reuse [U1 + A1 + U2], cache [U1 + A1 + U2 + A2 + U3]
Turn 4: Reuse [U1 + A1 + U2 + A2 + U3], cache [full history + U4]
在对话的第 10 轮,您可能缓存了 20,000 个令牌的历史记录,在每个后续轮次中,只需支付正常成本的 10% 即可获得该上下文。
关键要求:工具和系统稳定性
使用 CONVERSATION_HISTORY 时,工具和系统提示在整个对话过程中必须保持稳定。对任何一个的更改都会使整个对话缓存失效。
这是一个完整的示例,展示了多问题文档分析的缓存效率
@Service
public class ContractAnalyzer {
private final AnthropicChatModel chatModel;
private final PdfExtractor pdfExtractor;
public AnalysisReport analyzeAgreement(String agreementPdf) {
// Extract text from PDF (typically 2,000-5,000 tokens)
String agreementText = pdfExtractor.extract(agreementPdf);
String systemPrompt = """
You are an expert business analyst specializing in partnership agreements.
Analyze the following partnership agreement and provide insights about
collaboration terms, value propositions, and strategic opportunities.
AGREEMENT:
%s
""".formatted(agreementText);
String[] questions = {
"What are the key collaboration opportunities outlined in this agreement?",
"Summarize the revenue sharing and financial arrangements.",
"What intellectual property rights and licensing terms are defined?",
"Identify the strategic value propositions for both parties.",
"What are the performance milestones and success metrics?"
};
AnalysisReport report = new AnalysisReport();
AnthropicChatOptions options = AnthropicChatOptions.builder()
.model(AnthropicApi.ChatModel.CLAUDE_3_5_SONNET)
.cacheOptions(AnthropicCacheOptions.builder()
.strategy(AnthropicCacheStrategy.SYSTEM_ONLY)
.messageTypeTtl(MessageType.SYSTEM, AnthropicCacheTtl.ONE_HOUR)
.build())
.maxTokens(1000)
.build();
for (int i = 0; i < questions.length; i++) {
ChatResponse response = chatModel.call(
new Prompt(
List.of(
new SystemMessage(systemPrompt),
new UserMessage(questions[i])
),
options
)
);
String answer = response.getResult().getOutput().getText();
report.addSection(questions[i], answer);
// Log cache performance
AnthropicApi.Usage usage = (AnthropicApi.Usage)
response.getMetadata().getUsage().getNativeUsage();
if (usage != null) {
if (i == 0) {
logger.info("First question - Cache created: {} tokens",
usage.cacheCreationInputTokens());
} else {
logger.info("Question {} - Cache read: {} tokens",
i + 1, usage.cacheReadInputTokens());
}
}
}
return report;
}
}
使用 Claude 3.5 Sonnet 的 3,500 令牌系统提示(协议 + 指令)
这表示缓存系统提示部分成本降低了 68%。实际总节省会更低,因为您考虑了用户问题令牌和输出令牌(未缓存),但随着问题数量或文档大小的增加,节省会变得更显著。
注意:Spring AI 1.1.0 及更高版本支持提示缓存。请尝试使用最新的 1.1.0-SNAPSHOT 版本。
将 Spring AI Anthropic Starter 添加到您的项目
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-anthropic-spring-boot-starter</artifactId>
</dependency>
在您的应用程序属性中配置您的 API 密钥
spring.ai.anthropic.api-key=${ANTHROPIC_API_KEY}
在您的应用程序中注入并使用
@Autowired
private AnthropicChatModel chatModel;
// Start using caching strategies immediately
默认缓存 TTL 为 5 分钟。对于请求不频繁的场景,您可以配置 1 小时缓存
AnthropicChatOptions options = AnthropicChatOptions.builder()
.model(AnthropicApi.ChatModel.CLAUDE_3_5_SONNET)
.cacheOptions(AnthropicCacheOptions.builder()
.strategy(AnthropicCacheStrategy.SYSTEM_ONLY)
.messageTypeTtl(MessageType.SYSTEM, AnthropicCacheTtl.ONE_HOUR)
.build())
.maxTokens(500)
.build();
当您配置 1 小时 TTL 时,Spring AI 会自动添加所需的 beta 标头(anthropic-beta: extended-cache-ttl-2025-04-11)。
何时使用不同的 TTL
权衡:1 小时缓存写入成本是 5 分钟写入的两倍(Claude 3.5 Sonnet 为 $6/M,而 5 分钟为 $3.75/M)。评估您的应用程序的请求模式和稳定性要求,以确定哪种 TTL 有意义。
您可以为每种消息类型设置最小内容长度,以优化断点使用
AnthropicCacheOptions cache = AnthropicCacheOptions.builder()
.strategy(AnthropicCacheStrategy.CONVERSATION_HISTORY)
.messageTypeMinContentLength(MessageType.SYSTEM, 1024)
.messageTypeMinContentLength(MessageType.USER, 512)
.messageTypeMinContentLength(MessageType.ASSISTANT, 512)
.build();
为了精确的令牌计数,您可以提供一个自定义长度函数
AnthropicCacheOptions cache = AnthropicCacheOptions.builder()
.strategy(AnthropicCacheStrategy.CONVERSATION_HISTORY)
.contentLengthFunction(text -> customTokenCounter.count(text))
.build();
默认情况下,Spring AI 使用字符串长度作为令牌计数的代理,但在生产场景中,您可能需要考虑注入适当的令牌计数器。
对于对内部机制感兴趣的人,以下是 Spring AI 处理缓存管理的方式
┌─────────────────────────────────────────────────────────┐
│ Request Flow: │
│ │
│ Application │
│ ↓ │
│ AnthropicChatModel │
│ ↓ │
│ CacheEligibilityResolver │
│ (created from strategy) │
│ ↓ │
│ For each component: │
│ - Check strategy eligibility │
│ - Verify minimum content length │
│ - Confirm breakpoint availability (<4) │
│ - Add cache_control if eligible │
│ ↓ │
│ Build request with cache markers │
│ ↓ │
│ Add beta headers if needed (1h TTL) │
│ ↓ │
│ Anthropic API │
│ ↓ │
│ Response with cache metrics │
└─────────────────────────────────────────────────────────┘
CacheEligibilityResolver 根据所选策略、消息类型资格、内容长度要求和可用断点来确定每条消息或工具是否符合缓存条件。CacheBreakpointTracker 通过每个请求的线程安全跟踪来强制执行 Anthropic 的 4 断点限制。
对于 CONVERSATION_HISTORY,Spring AI 使用聚合资格检查——它在确定缓存资格时会考虑最后约 20 个内容块中所有消息类型(用户、助手、工具)的组合内容。这可以防止简短的用户问题(例如“告诉我更多”)在对话历史记录中有大量助手响应时阻止缓存创建。
在以下情况下避免缓存
避免这些常见错误
缓存与流式传输和非流式传输响应无缝协作。使用流式传输时,缓存指标会出现在最终响应块中。两种模式下的缓存行为没有区别。
Anthropic Claude 中的提示缓存为具有可重用提示内容的应用程序提供了显著的成本和延迟优势。Spring AI 的战略方法通过自动处理缓存断点放置、断点限制和 TTL 配置来简化实现。
五种缓存策略涵盖了常见的使用模式,从简单的系统提示缓存到复杂的多轮对话。对于大多数应用程序,根据内容稳定性模式选择适当的策略就足够了——Spring AI 处理实现细节。
有关更多信息,请参阅 Spring AI Anthropic 文档和 Anthropic 的提示缓存指南。