Anthropic Chat
Anthropic Claude 是一系列基础 AI 模型,可用于多种应用场景。对于开发者和企业,您可以通过其 API 访问功能,直接基于 Anthropic 的 AI 基础设施 进行构建。
Spring AI 支持 Anthropic 的 Messaging API,可用于同步和流式文本生成。
Anthropic 的 Claude 模型也可以通过 Amazon Bedrock Converse 使用。Spring AI 也提供了专门的 Amazon Bedrock Converse Anthropic 客户端实现。
先决条件
您需要在 Anthropic 门户网站上创建一个 API 密钥。
在 Anthropic API 仪表盘 创建一个账户,并在 获取 API 密钥 页面生成 API 密钥。
Spring AI 项目定义了一个名为 spring.ai.anthropic.api-key 的配置属性,您应将其值设置为从 anthropic.com 获取的 API Key。
您可以在 application.properties 文件中设置此配置属性:
spring.ai.anthropic.api-key=<your-anthropic-api-key>
为了在处理敏感信息(如 API 密钥)时增强安全性,您可以使用 Spring 表达式语言(SpEL)来引用自定义环境变量:
# In application.yml
spring:
ai:
anthropic:
api-key: ${ANTHROPIC_API_KEY}
# In your environment or .env file
export ANTHROPIC_API_KEY=<your-anthropic-api-key>
你也可以在应用代码中以编程方式获取此配置:
// Retrieve API key from a secure source or environment variable
String apiKey = System.getenv("ANTHROPIC_API_KEY");
添加仓库和 BOM
Spring AI 构件已发布至 Maven Central 和 Spring Snapshot 仓库。请参考构件仓库章节,将这些仓库添加至您的构建系统中。
为协助进行依赖管理,Spring AI 提供了一个物料清单(BOM),以确保在整个项目中使用统一版本的 Spring AI。请参考依赖管理章节,将 Spring AI BOM 添加到你的构建系统中。
自动配置
Spring AI 的自动配置和 starter 模块的 artifact 名称有重大变化。请查阅升级说明了解更多信息。
Spring AI为Anthropic Chat Client提供了Spring Boot自动配置。要启用此功能,请将以下依赖项添加到项目的Maven pom.xml或Gradle build.gradle文件中:
- Maven
- Gradle
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-anthropic</artifactId>
</dependency>
dependencies {
implementation 'org.springframework.ai:spring-ai-starter-model-anthropic'
}
:::提示
请参考依赖管理部分,将 Spring AI BOM 添加到您的构建文件中。
:::
聊天属性
重试属性
前缀 spring.ai.retry 用作属性前缀,可用于配置 Anthropic 聊天模型的重试机制。
| 属性 | 描述 | 默认值 |
|---|---|---|
| spring.ai.retry.max-attempts | 最大重试次数。 | 10 |
| spring.ai.retry.backoff.initial-interval | 指数退避策略的初始休眠时间。 | 2 秒 |
| spring.ai.retry.backoff.multiplier | 退避间隔乘数。 | 5 |
| spring.ai.retry.backoff.max-interval | 最大退避持续时间。 | 3 分钟 |
| spring.ai.retry.on-client-errors | 若为 false,则抛出 NonTransientAiException,且不针对 4xx 客户端错误代码进行重试。 | false |
| spring.ai.retry.exclude-on-http-codes | 不应触发重试的 HTTP 状态代码列表(例如,用于抛出 NonTransientAiException)。 | 空 |
| spring.ai.retry.on-http-codes | 应触发重试的 HTTP 状态代码列表(例如,用于抛出 TransientAiException)。 | 空 |
目前重试策略暂不适用于流式 API。
连接属性
前缀 spring.ai.anthropic 被用作连接 Anthropic 服务的属性前缀。
| 属性 | 描述 | 默认值 |
|---|---|---|
| spring.ai.anthropic.base-url | 连接到的 URL | api.anthropic.com |
| spring.ai.anthropic.completions-path | 附加到基础 URL 的路径。 | /v1/chat/completions |
| spring.ai.anthropic.version | Anthropic API 版本 | 2023-06-01 |
| spring.ai.anthropic.api-key | API 密钥 | - |
| spring.ai.anthropic.beta-version | 启用新的/实验性功能。如果设置为 max-tokens-3-5-sonnet-2024-07-15,输出 tokens 限制将从 4096 增加到 8192(仅适用于 claude-3-5-sonnet)。 | tools-2024-04-04 |
配置属性
现在通过前缀为 spring.ai.model.chat 的顶级属性来配置聊天自动配置的启用与禁用。
若要启用,请设置 spring.ai.model.chat=anthropic (默认已启用)
若要禁用,请设置 spring.ai.model.chat=none (或任何与 anthropic 不匹配的值)
此项变更是为了支持配置多个模型。
前缀 spring.ai.anthropic.chat 是一个属性前缀,它允许你配置用于 Anthropic 的聊天模型实现。
| 属性 | 描述 | 默认值 |
|---|---|---|
| spring.ai.anthropic.chat.enabled (已移除且不再有效) | 启用 Anthropic 聊天模型。 | true |
| spring.ai.model.chat | 启用 Anthropic 聊天模型。 | anthropic |
| spring.ai.anthropic.chat.options.model | 这是要使用的 Anthropic Chat 模型。支持:claude-sonnet-4-5, claude-opus-4-5, claude-haiku-4-5, claude-opus-4-1, claude-opus-4-0, claude-sonnet-4-0, claude-3-7-sonnet-latest, claude-3-5-sonnet-latest, claude-3-5-haiku-latest, claude-3-opus-latest, claude-3-haiku-20240307 | claude-sonnet-4-5 |
| spring.ai.anthropic.chat.options.temperature | 用于控制生成内容表面创造力的采样温度。较高的值会使输出更随机,而较低的值会使结果更集中和确定。不建议在同一补全请求中同时修改 temperature 和 top_p,因为这两个设置之间的相互作用难以预测。 | 0.8 |
| spring.ai.anthropic.chat.options.max-tokens | 聊天补全中要生成的最大令牌数。输入令牌和生成令牌的总长度受模型上下文长度限制。 | 500 |
| spring.ai.anthropic.chat.options.stop-sequence | 将导致模型停止生成的自定义文本序列。我们的模型通常会在其自然完成其回合时停止,这将导致响应 stop_reason 为 "end_turn"。如果你希望模型在遇到自定义文本字符串时停止生成,可以使用 stop_sequences 参数。如果模型遇到其中一个自定义序列,响应 stop_reason 值将为 "stop_sequence",并且响应 stop_sequence 值将包含匹配的停止序列。 | - |
| spring.ai.anthropic.chat.options.top-p | 使用核心采样。在核心采样中,我们以降序概率顺序计算所有选项对于每个后续令牌的累积分布,并在达到 top_p 指定的特定概率时将其截断。你应该更改 temperature 或 top_p,但不要同时更改两者。仅推荐用于高级用例。通常只需要使用 temperature。 | - |
| spring.ai.anthropic.chat.options.top-k | 仅从每个后续令牌的顶部 K 个选项中采样。用于移除"长尾"低概率响应。在此处了解更多技术细节。仅推荐用于高级用例。通常只需要使用 temperature。 | - |
| spring.ai.anthropic.chat.options.tool-names | 工具名称列表,用于在单个提示请求中启用工具调用。具有这些名称的工具必须存在于 toolCallbacks 注册表中。 | - |
| spring.ai.anthropic.chat.options.tool-callbacks | 要注册到 ChatModel 的工具回调。 | - |
| spring.ai.anthropic.chat.options.toolChoice | 控制模型调用哪个(如果有)工具。none 表示模型不会调用函数,而是生成一条消息。auto 表示模型可以选择生成消息或调用工具。通过 {"type": "tool", "name": "my_tool"} 指定特定工具会强制模型调用该工具。当不存在函数时,默认值为 none。如果存在函数,默认值为 auto。 | - |
| spring.ai.anthropic.chat.options.internal-tool-execution-enabled | 如果为 false,Spring AI 将不会在内部处理工具调用,而是将它们代理给客户端。然后,处理工具调用、将它们分派给适当的函数并返回结果是客户端的责任。如果为 true(默认值),Spring AI 将在内部处理函数调用。仅适用于具有函数调用支持的聊天模型 | true |
| spring.ai.anthropic.chat.options.http-headers | 要添加到聊天补全请求中的可选 HTTP 头。 | - |
有关最新模型别名及其说明,请参阅 Anthropic 官方模型别名文档。
所有以 spring.ai.anthropic.chat.options 为前缀的属性,都可以在运行时通过向 Prompt 调用添加特定于请求的 运行时选项 来覆盖。
运行时选项
AnthropicChatOptions.java 提供了模型配置,例如使用的模型、温度、最大令牌数等。
在启动时,默认选项可以通过 AnthropicChatModel(api, options) 构造函数或 spring.ai.anthropic.chat.options.* 属性进行配置。
在运行时,你可以通过向 Prompt 调用中添加新的、特定于请求的选项来覆盖默认设置。例如,要针对某个特定请求覆盖默认的模型和温度设置:
ChatResponse response = chatModel.call(
new Prompt(
"Generate the names of 5 famous pirates.",
AnthropicChatOptions.builder()
.model("claude-3-7-sonnet-latest")
.temperature(0.4)
.build()
));
除了特定于模型的 AnthropicChatOptions 之外,你还可以使用可移植的 ChatOptions 实例,该实例可以通过 ChatOptions#builder() 创建。
提示缓存
Anthropic的提示词缓存功能允许您缓存经常使用的提示词,以降低成本并提高重复交互的响应速度。当您缓存一个提示词后,后续相同的请求可以重用缓存内容,从而显著减少需要处理的输入令牌数量。
支持的模型
提示缓存目前支持 Claude Sonnet 4.5、Claude Opus 4.5、Claude Haiku 4.5、Claude Opus 4、Claude Sonnet 4、Claude Sonnet 3.7、Claude Sonnet 3.5、Claude Haiku 3.5、Claude Haiku 3 和 Claude Opus 3。
令牌要求
不同模型对于缓存生效有不同的最低令牌阈值:
- Claude Sonnet 4:1024+ 令牌
- Claude Haiku 模型:2048+ 令牌
- 其他模型:1024+ 令牌
缓存策略
Spring AI通过AnthropicCacheStrategy枚举提供策略性的缓存放置。每种策略都会在Anthropic的4个断点限制内,自动将缓存断点放置在最优位置。
| 策略 | 使用的断点数量 | 使用场景 |
|---|---|---|
NONE | 0 | 完全禁用提示缓存。适用于一次性请求或内容太小而无法从缓存中受益的场景。 |
SYSTEM_ONLY | 1 | 缓存系统消息内容。工具定义通过 Anthropic 自动的 ~20 块回看机制隐式缓存。适用于系统提示较大且稳定、工具数量少于 20 个的场景。 |
TOOLS_ONLY | 1 | 仅缓存工具定义。系统消息保持未缓存状态,每个请求都会重新处理。适用于工具定义较大且稳定(超过 5000 个令牌),但系统提示经常变化或随租户/上下文而异的场景。 |
SYSTEM_AND_TOOLS | 2 | 显式缓存工具定义(断点 1)和系统消息(断点 2)。适用于拥有 20 个以上工具(超出自动回看范围)或希望对两个组件进行确定性缓存的场景。系统更改不会使工具缓存失效。 |
CONVERSATION_HISTORY | 1-4 | 缓存整个对话历史记录,直至当前用户问题。适用于具有聊天记忆的多轮对话,且对话历史随时间增长的场景。 |
:::重要
由于 Anthropic 的级联失效机制,更改工具定义将使所有下游缓存断点(系统、消息)失效。在使用 SYSTEM_AND_TOOLS 或 CONVERSATION_HISTORY 策略时,工具的稳定性至关重要。
:::
启用提示缓存
通过设置 AnthropicChatOptions 上的 cacheOptions 并选择一个 strategy 来启用提示缓存。
仅系统缓存
最适用于: 系统提示词稳定且工具数量少于20个的场景(通过自动回溯机制隐式缓存工具)。
// Cache system message content (tools cached implicitly)
ChatResponse response = chatModel.call(
new Prompt(
List.of(
new SystemMessage("You are a helpful AI assistant with extensive knowledge..."),
new UserMessage("What is machine learning?")
),
AnthropicChatOptions.builder()
.model("claude-sonnet-4")
.cacheOptions(AnthropicCacheOptions.builder()
.strategy(AnthropicCacheStrategy.SYSTEM_ONLY)
.build())
.maxTokens(500)
.build()
)
);
仅工具缓存
最佳适用于:具有动态系统提示的大型稳定工具集(多租户应用、A/B 测试场景)。
// Cache tool definitions, system prompt processed fresh each time
ChatResponse response = chatModel.call(
new Prompt(
List.of(
new SystemMessage("You are a " + persona + " assistant..."), // Dynamic per-tenant
new UserMessage("What's the weather like in San Francisco?")
),
AnthropicChatOptions.builder()
.model("claude-sonnet-4")
.cacheOptions(AnthropicCacheOptions.builder()
.strategy(AnthropicCacheStrategy.TOOLS_ONLY)
.build())
.toolCallbacks(weatherToolCallback) // Large tool set cached
.maxTokens(500)
.build()
)
);
系统和工具缓存
最适合:20个以上工具(超出自动回看范围)或当两个组件应独立缓存时。
// Cache both tool definitions and system message with independent breakpoints
// Changing system won't invalidate tool cache (but changing tools invalidates both)
ChatResponse response = chatModel.call(
new Prompt(
List.of(
new SystemMessage("You are a weather analysis assistant..."),
new UserMessage("What's the weather like in San Francisco?")
),
AnthropicChatOptions.builder()
.model("claude-sonnet-4")
.cacheOptions(AnthropicCacheOptions.builder()
.strategy(AnthropicCacheStrategy.SYSTEM_AND_TOOLS)
.build())
.toolCallbacks(weatherToolCallback) // 20+ tools
.maxTokens(500)
.build()
)
);
对话历史缓存
// Cache conversation history with ChatClient and memory (cache breakpoint on last user message)
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultSystem("You are a personalized career counselor...")
.defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory)
.conversationId(conversationId)
.build())
.build();
String response = chatClient.prompt()
.user("What career advice would you give me?")
.options(AnthropicChatOptions.builder()
.model("claude-sonnet-4")
.cacheOptions(AnthropicCacheOptions.builder()
.strategy(AnthropicCacheStrategy.CONVERSATION_HISTORY)
.build())
.maxTokens(500)
.build())
.call()
.content();
使用 ChatClient Fluent API
String response = ChatClient.create(chatModel)
.prompt()
.system("You are an expert document analyst...")
.user("Analyze this large document: " + document)
.options(AnthropicChatOptions.builder()
.model("claude-sonnet-4")
.cacheOptions(AnthropicCacheOptions.builder()
.strategy(AnthropicCacheStrategy.SYSTEM_ONLY)
.build())
.build())
.call()
.content();
高级缓存选项
每条消息的 TTL(5 分钟或 1 小时)
默认情况下,缓存内容使用5分钟的TTL。您可以为特定消息类型设置1小时的TTL。当使用1小时TTL时,Spring AI会自动设置所需的Anthropic beta标头。
ChatResponse response = chatModel.call(
new Prompt(
List.of(new SystemMessage(largeSystemPrompt)),
AnthropicChatOptions.builder()
.model("claude-sonnet-4")
.cacheOptions(AnthropicCacheOptions.builder()
.strategy(AnthropicCacheStrategy.SYSTEM_ONLY)
.messageTypeTtl(MessageType.SYSTEM, AnthropicCacheTtl.ONE_HOUR)
.build())
.maxTokens(500)
.build()
)
);
Extended TTL 使用了 Anthropic 的测试版功能 extended-cache-ttl-2025-04-11。
缓存资格过滤器
通过设置最小内容长度和可选的基于令牌的长度函数来控制何时使用缓存断点:
AnthropicCacheOptions cache = AnthropicCacheOptions.builder()
.strategy(AnthropicCacheStrategy.CONVERSATION_HISTORY)
.messageTypeMinContentLength(MessageType.SYSTEM, 1024)
.messageTypeMinContentLength(MessageType.USER, 1024)
.messageTypeMinContentLength(MessageType.ASSISTANT, 1024)
.contentLengthFunction(text -> MyTokenCounter.count(text))
.build();
ChatResponse response = chatModel.call(
new Prompt(
List.of(/* messages */),
AnthropicChatOptions.builder()
.model("claude-sonnet-4")
.cacheOptions(cache)
.build()
)
);
如果使用 SYSTEM_AND_TOOLS 策略,无论内容长度如何,工具定义始终会被考虑进行缓存。
使用示例
这是一个完整的示例,演示了如何在使用成本跟踪的情况下进行提示缓存:
// Create system content that will be reused multiple times
String largeSystemPrompt = "You are an expert software architect specializing in distributed systems...";
// First request - creates cache
ChatResponse firstResponse = chatModel.call(
new Prompt(
List.of(
new SystemMessage(largeSystemPrompt),
new UserMessage("What is microservices architecture?")
),
AnthropicChatOptions.builder()
.model("claude-sonnet-4")
.cacheOptions(AnthropicCacheOptions.builder()
.strategy(AnthropicCacheStrategy.SYSTEM_ONLY)
.build())
.maxTokens(500)
.build()
)
);
// Access cache-related token usage
AnthropicApi.Usage firstUsage = (AnthropicApi.Usage) firstResponse.getMetadata()
.getUsage().getNativeUsage();
System.out.println("Cache creation tokens: " + firstUsage.cacheCreationInputTokens());
System.out.println("Cache read tokens: " + firstUsage.cacheReadInputTokens());
// Second request with same system prompt - reads from cache
ChatResponse secondResponse = chatModel.call(
new Prompt(
List.of(
new SystemMessage(largeSystemPrompt),
new UserMessage("What are the benefits of event sourcing?")
),
AnthropicChatOptions.builder()
.model("claude-sonnet-4")
.cacheOptions(AnthropicCacheOptions.builder()
.strategy(AnthropicCacheStrategy.SYSTEM_ONLY)
.build())
.maxTokens(500)
.build()
)
);
AnthropicApi.Usage secondUsage = (AnthropicApi.Usage) secondResponse.getMetadata()
.getUsage().getNativeUsage();
System.out.println("Cache creation tokens: " + secondUsage.cacheCreationInputTokens()); // Should be 0
System.out.println("Cache read tokens: " + secondUsage.cacheReadInputTokens()); // Should be > 0
令牌使用跟踪
Usage 记录提供了缓存相关令牌消耗的详细信息。要获取 Anthropic 特定的缓存指标,请使用 getNativeUsage() 方法:
AnthropicApi.Usage usage = (AnthropicApi.Usage) response.getMetadata()
.getUsage().getNativeUsage();
缓存特定指标包括:
-
cacheCreationInputTokens():返回创建缓存条目时使用的令牌数量 -
cacheReadInputTokens():返回从现有缓存条目读取的令牌数量
首次发送已缓存的提示时:- cacheCreationInputTokens() 将大于 0 - cacheReadInputTokens() 将为 0
当你再次发送相同的已缓存提示时:
cacheCreationInputTokens()将为 0cacheReadInputTokens()将大于 0
实际应用场景
法律文件分析
通过跨多个问题缓存文档内容,高效分析大型法律合同或合规文件:
// Load a legal contract (PDF or text)
String legalContract = loadDocument("merger-agreement.pdf"); // ~3000 tokens
// System prompt with legal expertise
String legalSystemPrompt = "You are an expert legal analyst specializing in corporate law. " +
"Analyze the following contract and provide precise answers about terms, obligations, and risks: " +
legalContract;
// First analysis - creates cache
ChatResponse riskAnalysis = chatModel.call(
new Prompt(
List.of(
new SystemMessage(legalSystemPrompt),
new UserMessage("What are the key termination clauses and associated penalties?")
),
AnthropicChatOptions.builder()
.model("claude-sonnet-4")
.cacheOptions(AnthropicCacheOptions.builder()
.strategy(AnthropicCacheStrategy.SYSTEM_ONLY)
.build())
.maxTokens(1000)
.build()
)
);
// Subsequent questions reuse cached document - 90% cost savings
ChatResponse obligationAnalysis = chatModel.call(
new Prompt(
List.of(
new SystemMessage(legalSystemPrompt), // Same content - cache hit
new UserMessage("List all financial obligations and payment schedules.")
),
AnthropicChatOptions.builder()
.model("claude-sonnet-4")
.cacheOptions(AnthropicCacheOptions.builder()
.strategy(AnthropicCacheStrategy.SYSTEM_ONLY)
.build())
.maxTokens(1000)
.build()
)
);
批量代码审查
处理多个代码文件时应用一致的审查标准,同时缓存审查指南:
// Define comprehensive code review guidelines
String reviewGuidelines = """
You are a senior software engineer conducting code reviews. Apply these criteria:
- Security vulnerabilities and best practices
- Performance optimizations and memory usage
- Code maintainability and readability
- Testing coverage and edge cases
- Design patterns and architecture compliance
""";
List<String> codeFiles = Arrays.asList(
"UserService.java", "PaymentController.java", "SecurityConfig.java"
);
List<String> reviews = new ArrayList<>();
for (String filename : codeFiles) {
String sourceCode = loadSourceFile(filename);
ChatResponse review = chatModel.call(
new Prompt(
List.of(
new SystemMessage(reviewGuidelines), // Cached across all reviews
new UserMessage("Review this " + filename + " code:\n\n" + sourceCode)
),
AnthropicChatOptions.builder()
.model("claude-sonnet-4")
.cacheOptions(AnthropicCacheOptions.builder()
.strategy(AnthropicCacheStrategy.SYSTEM_ONLY)
.build())
.maxTokens(800)
.build()
)
);
reviews.add(review.getResult().getOutput().getText());
}
// Guidelines cached after first request, subsequent reviews are faster and cheaper
多租户 SaaS 与共享工具
构建一个多租户应用程序,其中工具被共享,但系统提示根据每个租户进行定制:
// Define large shared tool set (used by all tenants)
List<FunctionCallback> sharedTools = Arrays.asList(
weatherToolCallback, // ~500 tokens
calendarToolCallback, // ~800 tokens
emailToolCallback, // ~700 tokens
analyticsToolCallback, // ~600 tokens
reportingToolCallback, // ~900 tokens
// ... 20+ more tools, totaling 5000+ tokens
);
@Service
public class MultiTenantAIService {
public String handleTenantRequest(String tenantId, String userQuery) {
// Get tenant-specific configuration
TenantConfig config = tenantRepository.findById(tenantId);
// Dynamic system prompt per tenant
String tenantSystemPrompt = String.format("""
You are %s's AI assistant. Company values: %s.
Brand voice: %s. Compliance requirements: %s.
""", config.companyName(), config.values(),
config.brandVoice(), config.compliance());
ChatResponse response = chatModel.call(
new Prompt(
List.of(
new SystemMessage(tenantSystemPrompt), // Different per tenant, NOT cached
new UserMessage(userQuery)
),
AnthropicChatOptions.builder()
.model("claude-sonnet-4")
.cacheOptions(AnthropicCacheOptions.builder()
.strategy(AnthropicCacheStrategy.TOOLS_ONLY) // Cache tools only
.build())
.toolCallbacks(sharedTools) // Cached once, shared across all tenants
.maxTokens(800)
.build()
)
);
return response.getResult().getOutput().getText();
}
}
// Tools cached once (5000 tokens @ 10% = 500 token cost for cache hits)
// Each tenant's unique system prompt processed fresh (200-500 tokens @ 100%)
// Total per request: ~700-1000 tokens vs 5500+ without TOOLS_ONLY
客户支持与知识库
创建一个客户支持系统,用于缓存您的产品知识库,以确保提供一致且准确的回复:
// Load comprehensive product knowledge
String knowledgeBase = """
PRODUCT DOCUMENTATION:
- API endpoints and authentication methods
- Common troubleshooting procedures
- Billing and subscription details
- Integration guides and examples
- Known issues and workarounds
""" + loadProductDocs(); // ~2500 tokens
@Service
public class CustomerSupportService {
public String handleCustomerQuery(String customerQuery, String customerId) {
ChatResponse response = chatModel.call(
new Prompt(
List.of(
new SystemMessage("You are a helpful customer support agent. " +
"Use this knowledge base to provide accurate solutions: " + knowledgeBase),
new UserMessage("Customer " + customerId + " asks: " + customerQuery)
),
AnthropicChatOptions.builder()
.model("claude-sonnet-4")
.cacheOptions(AnthropicCacheOptions.builder()
.strategy(AnthropicCacheStrategy.SYSTEM_ONLY)
.build())
.maxTokens(600)
.build()
)
);
return response.getResult().getOutput().getText();
}
}
// Knowledge base is cached across all customer queries
// Multiple support agents can benefit from the same cached content
最佳实践
-
选择正确的策略:
-
对于具有少于 20 个工具的稳定系统提示(工具通过自动回溯隐式缓存),使用
SYSTEM_ONLY -
对于具有动态系统提示(多租户、A/B 测试)的大型稳定工具集(5000+ 令牌),使用
TOOLS_ONLY -
当你有 20 个以上工具(超出自动回溯)或希望两者独立缓存时,使用
SYSTEM_AND_TOOLS -
对于多轮对话,配合 ChatClient 内存使用
CONVERSATION_HISTORY -
使用
NONE明确禁用缓存
-
-
理解级联失效:Anthropic 的缓存层次结构(
tools → system → messages)意味着更改会向下传播:-
更改工具会使以下缓存失效:工具 + 系统 + 消息(所有缓存)❌❌❌
-
更改系统提示会使以下缓存失效:系统 + 消息(工具缓存保持有效)✅❌❌
-
更改消息会使以下缓存失效:仅消息(工具和系统缓存保持有效)✅✅❌
**工具的稳定性至关重要**,尤其是在使用 `SYSTEM_AND_TOOLS` 或 `CONVERSATION_HISTORY` 策略时。
-
-
SYSTEM_AND_TOOLS 的独立性:使用
SYSTEM_AND_TOOLS时,更改系统消息不会使工具缓存失效,这允许在系统提示变化时高效地重用缓存的工具。 -
满足令牌要求:重点关注满足最低令牌要求的缓存内容(Sonnet 4 模型需 1024+ 令牌,Haiku 模型需 2048+ 令牌)。
-
重用完全相同的内容:缓存最适合完全匹配的提示内容。即使是微小的更改也需要新的缓存条目。
-
监控令牌使用情况:使用缓存使用统计信息来跟踪缓存有效性:
`java AnthropicApi.Usage usage = (AnthropicApi.Usage) response.getMetadata().getUsage().getNativeUsage(); if (usage != null) { System.out.println("缓存创建:" + usage.cacheCreationInputTokens()); System.out.println("缓存读取:" + usage.cacheReadInputTokens()); }` -
策略性缓存放置:该实现会根据你选择的策略,在最佳位置自动放置缓存断点,确保符合 Anthropic 的 4 个断点限制。
-
缓存生命周期:默认 TTL 为 5 分钟;可通过
messageTypeTtl(…)为每种消息类型设置 1 小时的 TTL。每次缓存访问都会重置计时器。 -
工具缓存的限制:请注意,基于工具的交互可能不会在响应中提供缓存使用情况的元数据。
实现细节
Spring AI中提示词缓存的实现遵循以下关键设计原则:
-
战略性缓存放置:缓存断点会根据所选策略自动放置在最佳位置,确保符合 Anthropic 的 4 断点限制。
-
CONVERSATION_HISTORY策略在以下位置放置缓存断点:工具调用(如果存在)、系统消息和最后一条用户消息。 -
这使得 Anthropic 的前缀匹配能够逐步缓存不断增长的对话历史。
-
每次对话轮次都基于之前缓存的前缀进行构建,从而最大化缓存复用。
-
-
供应商可移植性:缓存配置通过
AnthropicChatOptions完成,而非针对单个消息,从而在切换不同 AI 供应商时保持兼容性。 -
线程安全性:缓存断点跟踪机制采用线程安全的方式实现,能够正确处理并发请求。
-
自动内容排序:该实现确保 JSON 内容块和缓存控制能够根据 Anthropic API 的要求,在传输时进行正确的排序。
-
聚合资格检查:对于
CONVERSATION_HISTORY策略,该实现在判断组合内容是否达到缓存所需的最小 token 阈值时,会考虑最后约 20 个内容块内的所有消息类型(用户、助手、工具)。
未来增强功能
当前的缓存策略旨在有效处理90%的常见使用场景。对于需要更精细化控制的应用,未来的增强功能可能包括:
-
消息级缓存控制,实现细粒度断点设置
-
多区块内容缓存,在单个消息内部实现
-
高级缓存边界选择,适用于复杂工具场景
-
混合TTL策略,优化缓存层级结构
这些改进将保持完全的向后兼容性,同时为特定用例解锁 Anthropic 的完整提示缓存功能。
思考
Anthropic Claude 模型支持“思考”功能,该功能允许模型在给出最终答案前展示其推理过程。此功能实现了更透明、更详细的问题解决方式,尤其适用于需要逐步推理的复杂问题。
支持的模型
以下 Claude 模型支持思维功能:
-
Claude 4 模型 (
claude-opus-4-20250514,claude-sonnet-4-20250514) -
Claude 3.7 Sonnet (
claude-3-7-sonnet-20250219)
模型能力:
-
Claude 3.7 Sonnet: 返回完整的思维输出。行为一致,但不支持摘要式或交错式思维。
-
Claude 4 模型: 支持摘要式思维、交错式思维以及增强的工具集成。
所有支持模型的 API 请求结构是相同的,但输出行为有所不同。
思考配置
要启用任何支持的 Claude 模型进行思考,请在请求中包含以下配置:
必填配置
-
添加
thinking对象:-
"type": "enabled" -
budget_tokens: 用于推理的令牌限制(建议从 1024 开始)
-
-
令牌预算规则:
-
budget_tokens通常必须小于max_tokens -
Claude 使用的令牌数可能少于分配的数量
-
更大的预算会增加推理深度,但可能影响延迟
-
当使用工具调用与交错思考(仅限 Claude 4)时,此限制会放宽,但 Spring AI 目前尚未支持。
-
关键注意事项
-
Claude 3.7 在响应中返回完整的思考内容
-
Claude 4 返回模型内部推理的摘要版本,以减少延迟并保护敏感内容
-
思考令牌作为输出令牌的一部分计费(即使并非所有内容在响应中都可见)
-
交错思考功能仅在 Claude 4 模型上可用,并且需要测试版请求头
interleaved-thinking-2025-05-14
工具集成与交错思考
Claude 4模型支持在工具调用间进行交错式思考,允许模型在工具调用之间进行推理。
当前的 Spring AI 实现分别支持基础思考和工具使用功能,但尚不支持在多次工具调用之间进行交错式思考(即跨多个工具调用持续思考)。
关于交错式思维与工具使用的详细信息,请参阅Anthropic文档。
非流式处理示例
以下是如何在使用 ChatClient API 的非流式请求中启用思考功能:
ChatClient chatClient = ChatClient.create(chatModel);
// For Claude 3.7 Sonnet - explicit thinking configuration required
ChatResponse response = chatClient.prompt()
.options(AnthropicChatOptions.builder()
.model("claude-3-7-sonnet-latest")
.temperature(1.0) // Temperature should be set to 1 when thinking is enabled
.maxTokens(8192)
.thinking(AnthropicApi.ThinkingType.ENABLED, 2048) // Must be ≥1024 && < max_tokens
.build())
.user("Are there an infinite number of prime numbers such that n mod 4 == 3?")
.call()
.chatResponse();
// For Claude 4 models - thinking is enabled by default
ChatResponse response4 = chatClient.prompt()
.options(AnthropicChatOptions.builder()
.model("claude-opus-4-0")
.maxTokens(8192)
// No explicit thinking configuration needed
.build())
.user("Are there an infinite number of prime numbers such that n mod 4 == 3?")
.call()
.chatResponse();
// Process the response which may contain thinking content
for (Generation generation : response.getResults()) {
AssistantMessage message = generation.getOutput();
if (message.getText() != null) {
// Regular text response
System.out.println("Text response: " + message.getText());
}
else if (message.getMetadata().containsKey("signature")) {
// Thinking content
System.out.println("Thinking: " + message.getMetadata().get("thinking"));
System.out.println("Signature: " + message.getMetadata().get("signature"));
}
}
流式处理示例
你也可以在流式响应中使用思维链功能:
ChatClient chatClient = ChatClient.create(chatModel);
// For Claude 3.7 Sonnet - explicit thinking configuration
Flux<ChatResponse> responseFlux = chatClient.prompt()
.options(AnthropicChatOptions.builder()
.model("claude-3-7-sonnet-latest")
.temperature(1.0)
.maxTokens(8192)
.thinking(AnthropicApi.ThinkingType.ENABLED, 2048)
.build())
.user("Are there an infinite number of prime numbers such that n mod 4 == 3?")
.stream();
// For Claude 4 models - thinking is enabled by default
Flux<ChatResponse> responseFlux4 = chatClient.prompt()
.options(AnthropicChatOptions.builder()
.model("claude-opus-4-0")
.maxTokens(8192)
// No explicit thinking configuration needed
.build())
.user("Are there an infinite number of prime numbers such that n mod 4 == 3?")
.stream();
// For streaming, you might want to collect just the text responses
String textContent = responseFlux.collectList()
.block()
.stream()
.map(ChatResponse::getResults)
.flatMap(List::stream)
.map(Generation::getOutput)
.map(AssistantMessage::getText)
.filter(text -> text != null && !text.isBlank())
.collect(Collectors.joining());
工具使用集成
Claude 4 模型集成了思维与工具使用能力:
-
Claude 3.7 Sonnet: 同时支持思维和工具使用,但两者独立运行,需要更显式的配置
-
Claude 4 模型: 原生地交织思维和工具使用,在工具交互过程中提供更深层次的推理
使用思维的好处
思考功能提供以下优势:
-
透明度:查看模型的推理过程及其得出结论的方式
-
调试:识别模型可能犯逻辑错误的地方
-
教育:将逐步推理作为教学工具使用
-
复杂问题解决:在数学、逻辑和推理任务上取得更好的结果
请注意,启用思维链功能需要更高的令牌预算,因为思维过程本身会消耗您分配到的令牌。
工具/函数调用
你可以向 AnthropicChatModel 注册自定义的 Java 工具,让 Anthropic Claude 模型智能地选择输出包含调用一个或多个已注册函数所需参数的 JSON 对象。这是一种将大语言模型能力与外部工具及 API 连接起来的强大技术。了解更多关于工具调用的信息。
工具选择
tool_choice 参数允许你控制模型如何使用所提供的工具。这一功能让你能够对工具执行行为进行细粒度控制。
有关完整的API详细信息,请参阅Anthropic tool_choice文档。
工具选择选项
Spring AI 通过 AnthropicApi.ToolChoice 接口提供了四种工具选择策略:
-
ToolChoiceAuto(默认):模型自动决定是否使用工具或直接以文本回应 -
ToolChoiceAny:模型必须至少使用一个可用工具 -
ToolChoiceTool:模型必须按名称使用特定工具 -
ToolChoiceNone:模型不能使用任何工具
禁用并行工具使用
所有工具选择选项(除 ToolChoiceNone 外)都支持 disableParallelToolUse 参数。当设置为 true 时,模型最多只会输出一个工具使用。
使用示例
自动模式(默认行为)
让模型决定是否使用工具:
ChatResponse response = chatModel.call(
new Prompt(
"What's the weather in San Francisco?",
AnthropicChatOptions.builder()
.toolChoice(new AnthropicApi.ToolChoiceAuto())
.toolCallbacks(weatherToolCallback)
.build()
)
);
强制工具使用(任意)
要求模型至少使用一种工具:
ChatResponse response = chatModel.call(
new Prompt(
"What's the weather?",
AnthropicChatOptions.builder()
.toolChoice(new AnthropicApi.ToolChoiceAny())
.toolCallbacks(weatherToolCallback, calculatorToolCallback)
.build()
)
);
强制指定工具
要求模型使用指定名称的工具:
ChatResponse response = chatModel.call(
new Prompt(
"What's the weather in San Francisco?",
AnthropicChatOptions.builder()
.toolChoice(new AnthropicApi.ToolChoiceTool("get_weather"))
.toolCallbacks(weatherToolCallback, calculatorToolCallback)
.build()
)
);
禁用工具使用
禁止模型使用任何工具:
ChatResponse response = chatModel.call(
new Prompt(
"What's the weather in San Francisco?",
AnthropicChatOptions.builder()
.toolChoice(new AnthropicApi.ToolChoiceNone())
.toolCallbacks(weatherToolCallback)
.build()
)
);
禁用并行工具使用
强制模型一次仅使用一个工具:
ChatResponse response = chatModel.call(
new Prompt(
"What's the weather in San Francisco and what's 2+2?",
AnthropicChatOptions.builder()
.toolChoice(new AnthropicApi.ToolChoiceAuto(true)) // disableParallelToolUse = true
.toolCallbacks(weatherToolCallback, calculatorToolCallback)
.build()
)
);
使用 ChatClient API
你也可以通过流畅的 ChatClient API 使用工具选择功能:
String response = ChatClient.create(chatModel)
.prompt()
.user("What's the weather in San Francisco?")
.options(AnthropicChatOptions.builder()
.toolChoice(new AnthropicApi.ToolChoiceTool("get_weather"))
.build())
.call()
.content();
使用场景
-
验证:使用
ToolChoiceTool确保关键操作调用特定工具 -
效率:当明确必须使用工具时,使用
ToolChoiceAny以避免不必要的文本生成 -
控制:使用
ToolChoiceNone临时禁用工具访问,同时保持工具定义已注册 -
顺序处理:使用
disableParallelToolUse强制按顺序执行依赖操作的工具
多模态
多模态指的是模型同时理解和处理来自不同来源信息的能力,这些来源包括文本、PDF、图像、数据格式等。
图像
目前,Anthropic Claude 3 支持 images 的 base64 源类型,以及 image/jpeg、image/png、image/gif 和 image/webp 媒体类型。更多信息请查阅 Vision 指南。Anthropic Claude 3.5 Sonnet 还支持 application/pdf 文件的 pdf 源类型。
Spring AI 的 Message 接口通过引入 Media 类型来支持多模态 AI 模型。该类型包含消息中媒体附件的数据和信息,使用 Spring 的 org.springframework.util.MimeType 和 java.lang.Object 来存储原始媒体数据。
以下是从 AnthropicChatModelIT.java 中提取的一个简单代码示例,展示了如何将用户文本与图像相结合。
var imageData = new ClassPathResource("/multimodal.test.png");
var userMessage = new UserMessage("Explain what do you see on this picture?",
List.of(new Media(MimeTypeUtils.IMAGE_PNG, this.imageData)));
ChatResponse response = chatModel.call(new Prompt(List.of(this.userMessage)));
logger.info(response.getResult().getOutput().getContent());
它以 multimodal.test.png 图像作为输入:

同时,附上文本信息“解释一下你在这张图片中看到了什么?”,并生成类似这样的回应:
The image shows a close-up view of a wire fruit basket containing several pieces of fruit.
...
PDF
从Sonnet 3.5版本开始,提供了PDF支持(测试版)。使用 application/pdf 媒体类型将PDF文件附加到消息中:
var pdfData = new ClassPathResource("/spring-ai-reference-overview.pdf");
var userMessage = new UserMessage(
"You are a very professional document summarization specialist. Please summarize the given document.",
List.of(new Media(new MimeType("application", "pdf"), pdfData)));
var response = this.chatModel.call(new Prompt(List.of(userMessage)));
引用
Anthropic 的 Citations API 能够让 Claude 在生成回复时引用所提供文档中的特定部分。当提示词中包含了引用文档时,Claude 可以引用源材料,并且响应元数据中会返回引用元数据(字符范围、页码或内容块)。
引文有助于改进:
-
准确性验证:用户可依据源材料来核实 Claude 的回应
-
透明度:清晰查看文档中哪些部分支撑了回答内容
-
合规性:满足受监管行业对于信息来源归属的要求
-
可信度:通过展示信息来源建立用户信任
支持的模型
Claude 3.7 Sonnet 以及 Claude 4 系列模型 (Opus 和 Sonnet) 支持引用功能。
文档类型
支持三种类型的引用文档:
-
纯文本:包含字符级引用的文本内容
-
PDF:包含页面级引用的 PDF 文档
-
自定义内容:包含区块级引用的用户自定义内容块
创建引用文档
使用 CitationDocument 构建器来创建可引用的文档:
纯文本文档
CitationDocument document = CitationDocument.builder()
.plainText("The Eiffel Tower was completed in 1889 in Paris, France. " +
"It stands 330 meters tall and was designed by Gustave Eiffel.")
.title("Eiffel Tower Facts")
.citationsEnabled(true)
.build();
PDF 文档
// From file path
CitationDocument document = CitationDocument.builder()
.pdfFile("path/to/document.pdf")
.title("Technical Specification")
.citationsEnabled(true)
.build();
// From byte array
byte[] pdfBytes = loadPdfBytes();
CitationDocument document = CitationDocument.builder()
.pdf(pdfBytes)
.title("Product Manual")
.citationsEnabled(true)
.build();
自定义内容块
如需进行细粒度的引用控制,请使用自定义内容块:
CitationDocument document = CitationDocument.builder()
.customContent(
"The Great Wall of China is approximately 21,196 kilometers long.",
"It was built over many centuries, starting in the 7th century BC.",
"The wall was constructed to protect Chinese states from invasions."
)
.title("Great Wall Facts")
.citationsEnabled(true)
.build();
在请求中使用引用
在聊天选项中包含引用文档:
ChatResponse response = chatModel.call(
new Prompt(
"When was the Eiffel Tower built and how tall is it?",
AnthropicChatOptions.builder()
.model("claude-3-7-sonnet-latest")
.maxTokens(1024)
.citationDocuments(document)
.build()
)
);
多个文档
你可以提供多份文档供Claude参考:
CitationDocument parisDoc = CitationDocument.builder()
.plainText("Paris is the capital city of France with a population of 2.1 million.")
.title("Paris Information")
.citationsEnabled(true)
.build();
CitationDocument eiffelDoc = CitationDocument.builder()
.plainText("The Eiffel Tower was designed by Gustave Eiffel for the 1889 World's Fair.")
.title("Eiffel Tower History")
.citationsEnabled(true)
.build();
ChatResponse response = chatModel.call(
new Prompt(
"What is the capital of France and who designed the Eiffel Tower?",
AnthropicChatOptions.builder()
.model("claude-3-7-sonnet-latest")
.citationDocuments(parisDoc, eiffelDoc)
.build()
)
);
访问引文
引用内容会在响应元数据中返回:
ChatResponse response = chatModel.call(prompt);
// Get citations from metadata
List<Citation> citations = (List<Citation>) response.getMetadata().get("citations");
// Optional: Get citation count directly from metadata
Integer citationCount = (Integer) response.getMetadata().get("citationCount");
System.out.println("Total citations: " + citationCount);
// Process each citation
for (Citation citation : citations) {
System.out.println("Document: " + citation.getDocumentTitle());
System.out.println("Location: " + citation.getLocationDescription());
System.out.println("Cited text: " + citation.getCitedText());
System.out.println("Document index: " + citation.getDocumentIndex());
System.out.println();
}
引用类型
引文包含的位置信息因文档类型而异:
字符位置(纯文本)
对于纯文本文档,引用包含字符索引:
Citation citation = citations.get(0);
if (citation.getType() == Citation.LocationType.CHAR_LOCATION) {
int start = citation.getStartCharIndex();
int end = citation.getEndCharIndex();
String text = citation.getCitedText();
System.out.println("Characters " + start + "-" + end + ": " + text);
}
页面位置(PDF)
对于PDF文档,引用的内容需要包含页码:
Citation citation = citations.get(0);
if (citation.getType() == Citation.LocationType.PAGE_LOCATION) {
int startPage = citation.getStartPageNumber();
int endPage = citation.getEndPageNumber();
System.out.println("Pages " + startPage + "-" + endPage);
}
内容块位置(自定义内容)
对于自定义内容,引用指向特定的内容块:
Citation citation = citations.get(0);
if (citation.getType() == Citation.LocationType.CONTENT_BLOCK_LOCATION) {
int startBlock = citation.getStartBlockIndex();
int endBlock = citation.getEndBlockIndex();
System.out.println("Content blocks " + startBlock + "-" + endBlock);
}
完整示例
以下是一个完整的示例,展示如何引用文献:
// Create a citation document
CitationDocument document = CitationDocument.builder()
.plainText("Spring AI is an application framework for AI engineering. " +
"It provides a Spring-friendly API for developing AI applications. " +
"The framework includes abstractions for chat models, embedding models, " +
"and vector databases.")
.title("Spring AI Overview")
.citationsEnabled(true)
.build();
// Call the model with the document
ChatResponse response = chatModel.call(
new Prompt(
"What is Spring AI?",
AnthropicChatOptions.builder()
.model("claude-3-7-sonnet-latest")
.maxTokens(1024)
.citationDocuments(document)
.build()
)
);
// Display the response
System.out.println("Response: " + response.getResult().getOutput().getText());
System.out.println("\nCitations:");
// Process citations
List<Citation> citations = (List<Citation>) response.getMetadata().get("citations");
if (citations != null && !citations.isEmpty()) {
for (int i = 0; i < citations.size(); i++) {
Citation citation = citations.get(i);
System.out.println("\n[" + (i + 1) + "] " + citation.getDocumentTitle());
System.out.println(" Location: " + citation.getLocationDescription());
System.out.println(" Text: " + citation.getCitedText());
}
} else {
System.out.println("No citations were provided in the response.");
}
最佳实践
-
使用描述性标题:为引用文档提供有意义的标题,以帮助用户在引文中识别来源。
-
检查空引用:并非所有回答都包含引用,因此在访问引用元数据之前,请务必验证其是否存在。
-
考虑文档大小:较大的文档提供更多上下文,但会消耗更多输入令牌,并可能影响响应时间。
-
利用多个文档:当回答涉及多个来源的问题时,应在单个请求中提供所有相关文档,而不是进行多次调用。
-
使用适当的文档类型:简单内容选择纯文本,现有文档选择 PDF,当需要对引用粒度进行精细控制时,选择自定义内容块。
实际应用场景
法律文件分析
分析合同与法律文件,同时保持来源归属:
CitationDocument contract = CitationDocument.builder()
.pdfFile("merger-agreement.pdf")
.title("Merger Agreement 2024")
.citationsEnabled(true)
.build();
ChatResponse response = chatModel.call(
new Prompt(
"What are the key termination clauses in this contract?",
AnthropicChatOptions.builder()
.model("claude-sonnet-4")
.maxTokens(2000)
.citationDocuments(contract)
.build()
)
);
// Citations will reference specific pages in the PDF
客户支持知识库
提供准确且可验证来源的客户支持回答:
CitationDocument kbArticle1 = CitationDocument.builder()
.plainText(loadKnowledgeBaseArticle("authentication"))
.title("Authentication Guide")
.citationsEnabled(true)
.build();
CitationDocument kbArticle2 = CitationDocument.builder()
.plainText(loadKnowledgeBaseArticle("billing"))
.title("Billing FAQ")
.citationsEnabled(true)
.build();
ChatResponse response = chatModel.call(
new Prompt(
"How do I reset my password and update my billing information?",
AnthropicChatOptions.builder()
.model("claude-3-7-sonnet-latest")
.citationDocuments(kbArticle1, kbArticle2)
.build()
)
);
// Citations show which KB articles were referenced
研究与合规
生成需要引用来源以确保合规性的报告:
CitationDocument clinicalStudy = CitationDocument.builder()
.pdfFile("clinical-trial-results.pdf")
.title("Clinical Trial Phase III Results")
.citationsEnabled(true)
.build();
CitationDocument regulatoryGuidance = CitationDocument.builder()
.plainText(loadRegulatoryDocument())
.title("FDA Guidance Document")
.citationsEnabled(true)
.build();
ChatResponse response = chatModel.call(
new Prompt(
"Summarize the efficacy findings and regulatory implications.",
AnthropicChatOptions.builder()
.model("claude-sonnet-4")
.maxTokens(3000)
.citationDocuments(clinicalStudy, regulatoryGuidance)
.build()
)
);
// Citations provide audit trail for compliance
引文文档选项
Context 字段
可选地提供关于文档的背景信息,这些信息不会被引用,但可以指导Claude的理解:
CitationDocument document = CitationDocument.builder()
.plainText("...")
.title("Legal Contract")
.context("This is a merger agreement dated January 2024 between Company A and Company B")
.build();
控制引用
默认情况下,所有文档均禁用引用功能(需手动启用)。要启用引用,请明确设置 citationsEnabled(true):
CitationDocument document = CitationDocument.builder()
.plainText("The Eiffel Tower was completed in 1889...")
.title("Historical Facts")
.citationsEnabled(true) // Explicitly enable citations for this document
.build();
您也可以提供不带引用的文件作为背景资料:
CitationDocument backgroundDoc = CitationDocument.builder()
.plainText("Background information about the industry...")
.title("Context Document")
// citationsEnabled defaults to false - Claude will use this but not cite it
.build();
Anthropic 要求同一请求中的所有文档具有一致的引用设置。您不能在同一个请求中混用启用引用和禁用引用的文档。
技能
Anthropic 的 Skills API 通过专门的、预打包的文档生成能力扩展了 Claude 的功能。Skills 使得 Claude 能够创建实际可下载的文件——Excel 电子表格、PowerPoint 演示文稿、Word 文档和 PDF——而不仅仅是描述这些文档可能包含什么内容。
技能解决了传统大语言模型的一个根本局限:
-
传统Claude:"你的销售报告会是这样的……"(仅文本描述)
-
具备技能的Claude:创建一个真实的
sales_report.xlsx文件,你可以下载并在 Excel 中打开
支持的模型
Claude Sonnet 4、Claude Sonnet 4.5、Claude Opus 4 及后续模型支持技能功能。
要求
- 技能功能需要代码执行能力(Spring AI 会自动启用此功能)
- 每个请求最多可使用 8 个技能
- 生成的文件可通过 Files API 下载,有效期为 24 小时
预构建的 Anthropic 技能
Spring AI 通过 AnthropicSkill 枚举提供对 Anthropic 预构建技能的类型安全访问:
| 技能 | 描述 | 生成的文件类型 |
|---|---|---|
XLSX | Excel 电子表格生成与操作 | .xlsx (Microsoft Excel) |
PPTX | PowerPoint 演示文稿创建 | .pptx (Microsoft PowerPoint) |
DOCX | Word 文档生成 | .docx (Microsoft Word) |
PDF | PDF 文档创建 | .pdf (Portable Document Format) |
基本用法
通过将技能添加到你的 AnthropicChatOptions 来启用技能:
ChatResponse response = chatModel.call(
new Prompt(
"Create an Excel spreadsheet with Q1 2025 sales data. " +
"Include columns for Month, Revenue, and Expenses with 3 rows of sample data.",
AnthropicChatOptions.builder()
.model("claude-sonnet-4-5")
.maxTokens(4096)
.anthropicSkill(AnthropicApi.AnthropicSkill.XLSX)
.build()
)
);
// Claude will generate an actual Excel file
String responseText = response.getResult().getOutput().getText();
System.out.println(responseText);
// Output: "I've created an Excel spreadsheet with your Q1 2025 sales data..."
多重技能
您可以在单个请求中启用多个技能(最多8个):
ChatResponse response = chatModel.call(
new Prompt(
"Create a sales report with both an Excel file containing the raw data " +
"and a PowerPoint presentation summarizing the key findings.",
AnthropicChatOptions.builder()
.model("claude-sonnet-4-5")
.maxTokens(8192)
.anthropicSkill(AnthropicApi.AnthropicSkill.XLSX)
.anthropicSkill(AnthropicApi.AnthropicSkill.PPTX)
.build()
)
);
使用 SkillContainer 进行高级配置
如需更多控制,请直接使用 SkillContainer:
AnthropicApi.SkillContainer container = AnthropicApi.SkillContainer.builder()
.anthropicSkill(AnthropicApi.AnthropicSkill.XLSX)
.anthropicSkill(AnthropicApi.AnthropicSkill.PPTX, "20251013") // Specific version
.build();
ChatResponse response = chatModel.call(
new Prompt(
"Generate the quarterly report",
AnthropicChatOptions.builder()
.model("claude-sonnet-4-5")
.maxTokens(4096)
.skillContainer(container)
.build()
)
);
使用 ChatClient 流畅式 API
技能与 ChatClient 流畅 API 无缝协作:
String response = ChatClient.create(chatModel)
.prompt()
.user("Create a PowerPoint presentation about Spring AI with 3 slides: " +
"Title, Key Features, and Getting Started")
.options(AnthropicChatOptions.builder()
.model("claude-sonnet-4-5")
.maxTokens(4096)
.anthropicSkill(AnthropicApi.AnthropicSkill.PPTX)
.build())
.call()
.content();
使用技能进行流式处理
技能支持流式响应:
Flux<ChatResponse> responseFlux = chatModel.stream(
new Prompt(
"Create a Word document explaining machine learning concepts",
AnthropicChatOptions.builder()
.model("claude-sonnet-4-5")
.maxTokens(4096)
.anthropicSkill(AnthropicApi.AnthropicSkill.DOCX)
.build()
)
);
responseFlux.subscribe(response -> {
String content = response.getResult().getOutput().getText();
System.out.print(content);
});
下载生成的文件
当Claude通过Skills生成文件时,响应中会包含文件ID,这些ID可用于通过Files API下载实际文件。Spring AI提供了SkillsResponseHelper工具类,用于提取文件ID并下载文件。
提取文件ID
import org.springframework.ai.anthropic.SkillsResponseHelper;
ChatResponse response = chatModel.call(prompt);
// Extract all file IDs from the response
List<String> fileIds = SkillsResponseHelper.extractFileIds(response);
for (String fileId : fileIds) {
System.out.println("Generated file ID: " + fileId);
}
获取文件元数据
在下载之前,您可以检索文件元数据:
@Autowired
private AnthropicApi anthropicApi;
// Get metadata for a specific file
String fileId = fileIds.get(0);
AnthropicApi.FileMetadata metadata = anthropicApi.getFileMetadata(fileId);
System.out.println("Filename: " + metadata.filename()); // e.g., "sales_report.xlsx"
System.out.println("Size: " + metadata.size() + " bytes"); // e.g., 5082
System.out.println("MIME Type: " + metadata.mimeType()); // e.g., "application/vnd..."
下载文件内容
// Download file content as bytes
byte[] fileContent = anthropicApi.downloadFile(fileId);
// Save to local file system
Path outputPath = Path.of("downloads", metadata.filename());
Files.write(outputPath, fileContent);
System.out.println("Saved file to: " + outputPath);
便捷方法:下载所有文件
SkillsResponseHelper 提供了一个便捷方法,用于一次性下载所有生成的文件:
// Download all files to a target directory
Path targetDir = Path.of("generated-files");
Files.createDirectories(targetDir);
List<Path> savedFiles = SkillsResponseHelper.downloadAllFiles(response, anthropicApi, targetDir);
for (Path file : savedFiles) {
System.out.println("Downloaded: " + file.getFileName() +
" (" + Files.size(file) + " bytes)");
}
完整文件下载示例
以下是一个使用文件下载功能展示技能运用的完整示例:
@Service
public class DocumentGenerationService {
private final AnthropicChatModel chatModel;
private final AnthropicApi anthropicApi;
public DocumentGenerationService(AnthropicChatModel chatModel, AnthropicApi anthropicApi) {
this.chatModel = chatModel;
this.anthropicApi = anthropicApi;
}
public Path generateSalesReport(String quarter, Path outputDir) throws IOException {
// Generate Excel report using Skills
ChatResponse response = chatModel.call(
new Prompt(
"Create an Excel spreadsheet with " + quarter + " sales data. " +
"Include Month, Revenue, Expenses, and Profit columns.",
AnthropicChatOptions.builder()
.model("claude-sonnet-4-5")
.maxTokens(4096)
.anthropicSkill(AnthropicApi.AnthropicSkill.XLSX)
.build()
)
);
// Extract file IDs from the response
List<String> fileIds = SkillsResponseHelper.extractFileIds(response);
if (fileIds.isEmpty()) {
throw new RuntimeException("No file was generated");
}
// Download the generated file
String fileId = fileIds.get(0);
AnthropicApi.FileMetadata metadata = anthropicApi.getFileMetadata(fileId);
byte[] content = anthropicApi.downloadFile(fileId);
// Save to output directory
Path outputPath = outputDir.resolve(metadata.filename());
Files.write(outputPath, content);
return outputPath;
}
}
Files API 操作
AnthropicApi 提供对 Files API 的直接访问:
| 方法名 | 描述 |
|---|---|
getFileMetadata(fileId) | 获取文件元数据,包括文件名、大小、MIME 类型和过期时间 |
downloadFile(fileId) | 将文件内容作为字节数组下载 |
listFiles(limit, page) | 列出文件,支持分页 |
deleteFile(fileId) | 立即删除一个文件(文件会在 24 小时后自动过期) |
列出文件
// List files with pagination
AnthropicApi.FilesListResponse files = anthropicApi.listFiles(20, null);
for (AnthropicApi.FileMetadata file : files.data()) {
System.out.println(file.id() + ": " + file.filename());
}
// Check for more pages
if (files.hasMore()) {
AnthropicApi.FilesListResponse nextPage = anthropicApi.listFiles(20, files.nextPage());
// Process next page...
}
提取容器 ID
在与Skills进行多轮对话时,你可能需要提取容器ID:
String containerId = SkillsResponseHelper.extractContainerId(response);
if (containerId != null) {
System.out.println("Container ID for reuse: " + containerId);
}
最佳实践
-
使用合适的模型:技能功能在 Claude Sonnet 4 及更高版本的模型中效果最佳。请确保您使用的是受支持的模型。
-
设置足够的最大令牌数:文档生成可能需要大量令牌。对于复杂文档,请使用
maxTokens(4096)或更高的值。 -
在提示中明确具体:提供关于文档结构、内容和格式的清晰、详细的说明。
-
及时处理文件下载:生成的文件将在 24 小时后失效。请在生成后尽快下载文件。
-
检查文件 ID:在尝试下载之前,务必确认已返回文件 ID。某些提示可能会导致文本响应,而不会生成文件。
-
使用防御性错误处理:将文件操作包装在 try-catch 块中,以优雅地处理网络问题或文件过期的情况。
List<String> fileIds = SkillsResponseHelper.extractFileIds(response);
if (fileIds.isEmpty()) {
// Claude may have responded with text instead of generating a file
String text = response.getResult().getOutput().getText();
log.warn("No files generated. Response: {}", text);
return;
}
try {
byte[] content = anthropicApi.downloadFile(fileIds.get(0));
// Process file...
} catch (Exception e) {
log.error("Failed to download file: {}", e.getMessage());
}
实际应用场景
自动报告生成
根据数据生成格式化的商业报告:
@Service
public class ReportService {
private final AnthropicChatModel chatModel;
private final AnthropicApi anthropicApi;
public byte[] generateMonthlyReport(SalesData data) throws IOException {
String prompt = String.format(
"Create a PowerPoint presentation summarizing monthly sales performance. " +
"Total Revenue: $%,.2f, Total Expenses: $%,.2f, Net Profit: $%,.2f. " +
"Include charts and key insights. Create 5 slides: " +
"1) Title, 2) Revenue Overview, 3) Expense Breakdown, " +
"4) Profit Analysis, 5) Recommendations.",
data.revenue(), data.expenses(), data.profit()
);
ChatResponse response = chatModel.call(
new Prompt(prompt,
AnthropicChatOptions.builder()
.model("claude-sonnet-4-5")
.maxTokens(8192)
.anthropicSkill(AnthropicApi.AnthropicSkill.PPTX)
.build()
)
);
List<String> fileIds = SkillsResponseHelper.extractFileIds(response);
return anthropicApi.downloadFile(fileIds.get(0));
}
}
数据导出服务
将结构化数据导出为Excel格式:
@RestController
public class ExportController {
private final AnthropicChatModel chatModel;
private final AnthropicApi anthropicApi;
private final CustomerRepository customerRepository;
@GetMapping("/export/customers")
public ResponseEntity<byte[]> exportCustomers() throws IOException {
List<Customer> customers = customerRepository.findAll();
String dataDescription = customers.stream()
.map(c -> String.format("%s, %s, %s", c.name(), c.email(), c.tier()))
.collect(Collectors.joining("\n"));
ChatResponse response = chatModel.call(
new Prompt(
"Create an Excel spreadsheet with customer data. " +
"Columns: Name, Email, Tier. Format the header row with bold text. " +
"Data:\n" + dataDescription,
AnthropicChatOptions.builder()
.model("claude-sonnet-4-5")
.maxTokens(4096)
.anthropicSkill(AnthropicApi.AnthropicSkill.XLSX)
.build()
)
);
List<String> fileIds = SkillsResponseHelper.extractFileIds(response);
byte[] content = anthropicApi.downloadFile(fileIds.get(0));
AnthropicApi.FileMetadata metadata = anthropicApi.getFileMetadata(fileIds.get(0));
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + metadata.filename() + "\"")
.contentType(MediaType.parseMediaType(metadata.mimeType()))
.body(content);
}
}
多格式文档生成
从单个请求生成多种文档格式:
public Map<String, byte[]> generateProjectDocumentation(ProjectInfo project) throws IOException {
ChatResponse response = chatModel.call(
new Prompt(
"Create project documentation for: " + project.name() + "\n" +
"Description: " + project.description() + "\n\n" +
"Generate:\n" +
"1. An Excel file with the project timeline and milestones\n" +
"2. A PowerPoint overview presentation (3-5 slides)\n" +
"3. A Word document with detailed specifications",
AnthropicChatOptions.builder()
.model("claude-sonnet-4-5")
.maxTokens(16384)
.anthropicSkill(AnthropicApi.AnthropicSkill.XLSX)
.anthropicSkill(AnthropicApi.AnthropicSkill.PPTX)
.anthropicSkill(AnthropicApi.AnthropicSkill.DOCX)
.build()
)
);
Map<String, byte[]> documents = new HashMap<>();
List<String> fileIds = SkillsResponseHelper.extractFileIds(response);
for (String fileId : fileIds) {
AnthropicApi.FileMetadata metadata = anthropicApi.getFileMetadata(fileId);
byte[] content = anthropicApi.downloadFile(fileId);
documents.put(metadata.filename(), content);
}
return documents;
}
与其他功能结合使用
技能可以与其他Anthropic功能相结合,例如Prompt Caching:
ChatResponse response = chatModel.call(
new Prompt(
List.of(
new SystemMessage("You are an expert data analyst and document creator..."),
new UserMessage("Create a financial summary spreadsheet")
),
AnthropicChatOptions.builder()
.model("claude-sonnet-4-5")
.maxTokens(4096)
.anthropicSkill(AnthropicApi.AnthropicSkill.XLSX)
.cacheOptions(AnthropicCacheOptions.builder()
.strategy(AnthropicCacheStrategy.SYSTEM_ONLY)
.build())
.build()
)
);
关于自定义技能
除了上面文档中提到的预构建技能外,Anthropic 还支持组织为专门的文档模板、格式规则或特定领域行为创建自定义技能。
重要提示: 自定义技能必须通过 Anthropic 的平台(控制台或 API)创建并上传后才能使用。Spring AI 目前支持在自定义技能设置完成后使用它们,但不提供用于创建或管理自定义技能的 API。
如果您的 Anthropic 工作区中配置了自定义技能,您可以通过其技能 ID 来引用它们:
AnthropicApi.SkillContainer container = AnthropicApi.SkillContainer.builder()
.customSkill("skill_abc123") // 来自 Anthropic 的自定义技能 ID
.build();
有关在您的工作区中创建和管理自定义技能的详细信息,请参阅 Anthropic Skills API 文档。
示例控制器
创建一个新的Spring Boot项目,并将spring-ai-starter-model-anthropic添加到你的pom(或gradle)依赖中。
在 src/main/resources 目录下添加 application.properties 文件,以启用和配置 Anthropic 聊天模型:
spring.ai.anthropic.api-key=YOUR_API_KEY
spring.ai.anthropic.chat.options.model=claude-3-5-sonnet-latest
spring.ai.anthropic.chat.options.temperature=0.7
spring.ai.anthropic.chat.options.max-tokens=450
将 api-key 替换为您的 Anthropic 凭证。
这将创建一个 AnthropicChatModel 实现,你可以将其注入到你的类中。以下是一个使用该聊天模型进行文本生成的简单 @Controller 类示例。
@RestController
public class ChatController {
private final AnthropicChatModel chatModel;
@Autowired
public ChatController(AnthropicChatModel chatModel) {
this.chatModel = chatModel;
}
@GetMapping("/ai/generate")
public Map generate(@RequestParam(value = "message", defaultValue = "Tell me a joke") String message) {
return Map.of("generation", this.chatModel.call(message));
}
@GetMapping("/ai/generateStream")
public Flux<ChatResponse> generateStream(@RequestParam(value = "message", defaultValue = "Tell me a joke") String message) {
Prompt prompt = new Prompt(new UserMessage(message));
return this.chatModel.stream(prompt);
}
}
手动配置
AnthropicChatModel 实现了 ChatModel 和 StreamingChatModel 接口,并使用底层 AnthropicApi 客户端来连接到 Anthropic 服务。
将 spring-ai-anthropic 依赖项添加到项目的 Maven pom.xml 文件中:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-anthropic</artifactId>
</dependency>
或添加到您的 Gradle build.gradle 构建文件中。
dependencies {
implementation 'org.springframework.ai:spring-ai-anthropic'
}
:::提示
参考依赖管理章节,将 Spring AI BOM 添加到您的构建文件中。
:::
接下来,创建一个 AnthropicChatModel 并使用它进行文本生成:
var anthropicApi = new AnthropicApi(System.getenv("ANTHROPIC_API_KEY"));
var anthropicChatOptions = AnthropicChatOptions.builder()
.model("claude-3-7-sonnet-20250219")
.temperature(0.4)
.maxTokens(200)
.build()
var chatModel = AnthropicChatModel.builder().anthropicApi(anthropicApi)
.defaultOptions(anthropicChatOptions).build();
ChatResponse response = this.chatModel.call(
new Prompt("Generate the names of 5 famous pirates."));
// Or with streaming responses
Flux<ChatResponse> response = this.chatModel.stream(
new Prompt("Generate the names of 5 famous pirates."));
AnthropicChatOptions 为聊天请求提供配置信息。AnthropicChatOptions.Builder 是一个流畅的选项构建器。
低级 AnthropicApi 客户端
AnthropicApi 为 Anthropic Message API 提供了一个轻量级的 Java 客户端。
以下类图展示了 AnthropicApi 聊天接口及其构建模块:


以下是使用 API 的简单代码片段:
AnthropicApi anthropicApi =
new AnthropicApi(System.getenv("ANTHROPIC_API_KEY"));
AnthropicMessage chatCompletionMessage = new AnthropicMessage(
List.of(new ContentBlock("Tell me a Joke?")), Role.USER);
// Sync request
ResponseEntity<ChatCompletionResponse> response = this.anthropicApi
.chatCompletionEntity(new ChatCompletionRequest(AnthropicApi.ChatModel.CLAUDE_3_OPUS.getValue(),
List.of(this.chatCompletionMessage), null, 100, 0.8, false));
// Streaming request
Flux<StreamResponse> response = this.anthropicApi
.chatCompletionStream(new ChatCompletionRequest(AnthropicApi.ChatModel.CLAUDE_3_OPUS.getValue(),
List.of(this.chatCompletionMessage), null, 100, 0.8, true));
有关更多信息,请参考 AnthropicApi.java 的 JavaDoc。
低级 API 示例
- AnthropicApiIT.java 测试提供了一些关于如何使用该轻量级库的通用示例。