|
此版本仍在开发中,尚未被视为稳定版。为了获取最新的快照版本,请使用Spring AI 1.1.3! |
Anthropic Chat
Spring AI 通过官方的 Anthropic Java SDK 支持 Anthropic 的 Claude 模型,从而可以通过 Anthropic 的 API 访问 Claude。
前提条件
在 Anthropic 控制台上创建一个账户,并在 API 密钥页面上生成一个 API 密钥。
自动配置
Spring Boot 自动配置可通过 spring-ai-starter-model-anthropic starter 获得。
-
Maven
-
Gradle
将其添加到项目的 Maven pom.xml 文件中:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-anthropic</artifactId>
</dependency>
或者添加到你的 Gradle build.gradle 构建文件中:
dependencies {
implementation 'org.springframework.ai:spring-ai-starter-model-anthropic'
}
| 参考以下依赖管理部分,添加Spring AI BOM到你的构建文件中。 |
配置属性
使用 spring.ai.anthropic.* 个属性来配置 Anthropic 连接和聊天选项:
| 属性 | 描述 | 默认 |
|---|---|---|
|
Anthropic API 密钥 |
- |
|
API 基础 URL |
|
|
模型名称 |
|
|
最大标记数 |
|
|
采样温度 |
- |
|
Top-p 采样 |
- |
|
Top-k 采样 |
- |
手动配置
The AnthropicChatModel implements the ChatModel interface and uses the official Anthropic Java SDK to connect to Claude.
-
Maven
-
Gradle
将 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到你的构建文件中。 |
身份验证
通过编程方式或环境变量配置您的 API 密钥:
var chatOptions = AnthropicChatOptions.builder()
.model("claude-sonnet-4-20250514")
.maxTokens(1024)
.apiKey(System.getenv("ANTHROPIC_API_KEY"))
.build();
var chatModel = new AnthropicChatModel(chatOptions);
或者设置环境变量,让 SDK 自动检测:
export ANTHROPIC_API_KEY=<your-api-key>
// API key will be detected from ANTHROPIC_API_KEY environment variable
var chatModel = new AnthropicChatModel(
AnthropicChatOptions.builder()
.model("claude-sonnet-4-20250514")
.maxTokens(1024)
.build());
运行时选项
The AnthropicChatOptions.java class provides model configurations such as the model to use, temperature, max tokens, etc.
在启动时,使用AnthropicChatModel(options)构造函数配置默认选项。
在运行时,您可以通过向 Prompt 调用添加新的、特定于请求的选项来覆盖默认选项。
例如,要为特定请求覆盖默认模型和温度:
ChatResponse response = chatModel.call(
new Prompt(
"Generate the names of 5 famous pirates.",
AnthropicChatOptions.builder()
.model("claude-sonnet-4-20250514")
.temperature(0.4)
.build()
));
聊天选项
| 选项 | 描述 | 默认 |
|---|---|---|
模型 |
要使用的Claude模型名称。模型包括: |
|
maxTokens |
生成响应时的最大标记数。 |
4096 |
温度 |
控制响应中的随机性。较高的值会使输出更具随机性,较低的值则使其更具确定性。范围:0.0-1.0 |
1.0 |
topP |
核采样参数。模型会考虑具有 top_p 概率质量的标记。 |
- |
topK |
仅从每个标记的前 K 个选项中采样。 |
- |
stopSequences |
自定义序列,这些序列将导致模型停止生成。 |
- |
apiKey |
用于身份验证的 API 密钥。如果未设置,则会自动从 |
- |
baseUrl |
Anthropic API 的基础 URL。 |
|
超时 |
请求超时持续时间。 |
60秒 |
maxRetries |
失败请求的最大重试次数。 |
2 |
代理 |
HTTP 客户端的代理设置。 |
- |
customHeaders |
自定义要在所有请求上包含的 HTTP 头(客户端级别)。 |
- |
httpHeaders |
每请求的 HTTP 头。这些头通过 |
- |
思考 |
思考配置。使用便捷的构建器 |
- |
outputConfig |
结构化输出(JSON Schema)和工作量控制的输出配置。使用 |
- |
工具调用选项
| 选项 | 描述 | 默认 |
|---|---|---|
toolChoice |
控制模型调用哪个工具(如果有的话)。使用 |
AUTO |
toolCallbacks |
要注册到模型的工具回调列表。 |
- |
toolNames |
一组将在运行时解析的工具名称。 |
- |
internalToolExecutionEnabled |
如果为 false,则工具调用将被代理到客户端以供手动处理。如果为 true,则 Spring AI 将在内部处理工具调用。 |
true |
disableParallelToolUse |
当为真时,模型每次响应最多使用一个工具。 |
false |
| 除了特定于模型的 AnthropicChatOptions 之外,您还可以使用一个可移植的 ChatOptions 实例,该实例通过 ChatOptions#builder() 创建。 |
工具调用
您可以使用 AnthropicChatModel 注册自定义的 Java 函数或方法,并让 Claude 智能地选择输出一个包含参数的 JSON 对象,以调用一个或多个已注册的函数/工具。
这是一种将 LLM 能力与外部工具和 API 相连接的强大技术。
了解更多关于 工具调用 的信息。
基本工具调用
var chatOptions = AnthropicChatOptions.builder()
.model("claude-sonnet-4-20250514")
.toolCallbacks(List.of(
FunctionToolCallback.builder("getCurrentWeather", new WeatherService())
.description("Get the weather in location")
.inputType(WeatherService.Request.class)
.build()))
.build();
var chatModel = new AnthropicChatModel(chatOptions);
ChatResponse response = chatModel.call(
new Prompt("What's the weather like in San Francisco?", chatOptions));
工具选择选项
使用 toolChoice 选项控制 Claude 如何使用工具:
import com.anthropic.models.messages.ToolChoiceAny;
import com.anthropic.models.messages.ToolChoiceTool;
import com.anthropic.models.messages.ToolChoiceNone;
// Force Claude to use any available tool
var options = AnthropicChatOptions.builder()
.toolChoice(ToolChoiceAny.builder().build())
.toolCallbacks(...)
.build();
// Force Claude to use a specific tool
var options = AnthropicChatOptions.builder()
.toolChoice(ToolChoiceTool.builder().name("getCurrentWeather").build())
.toolCallbacks(...)
.build();
// Prevent tool use entirely
var options = AnthropicChatOptions.builder()
.toolChoice(ToolChoiceNone.builder().build())
.toolCallbacks(...)
.build();
|
Anthropic Java SDK 为常见的工具选择提供了便捷的静态工厂方法,这可以使您的代码更加简洁:
|
流式工具调用
Anthropic SDK 模块完全支持流式模式下的工具调用。当 Claude 决定在流式过程中调用工具时:
-
工具调用参数由部分 JSON 差值累积而成。
-
工具在内容块完成时执行。
-
结果被发送回Claude
-
对话会递归地持续进行,直到Claude给出最终回复。
Flux<ChatResponse> stream = chatModel.stream(
new Prompt("What's the weather in Paris, Tokyo, and New York?", chatOptions));
String response = stream
.collectList()
.block()
.stream()
.map(r -> r.getResult().getOutput().getContent())
.filter(Objects::nonNull)
.collect(Collectors.joining());
流式传输
Anthropic SDK 模块同时支持同步响应和流式响应。流式响应允许 Claude 在生成响应时逐步返回结果。
Flux<ChatResponse> stream = chatModel.stream(new Prompt("Tell me a story"));
stream.subscribe(response -> {
String content = response.getResult().getOutput().getContent();
if (content != null) {
System.out.print(content);
}
});
扩展思考
Anthropic Claude 模型支持“思考”功能,允许模型在给出最终答案之前展示其推理过程。这对于需要逐步推理的复杂问题尤为有用,例如数学、逻辑和分析任务。
|
支持的模型 思维功能由以下 Claude 模型支持:
模型能力:
API 请求结构在所有支持的模型中都相同,但输出行为有所不同。 |
便捷构建方法
AnthropicChatOptions.Builder 提供了三种思维模式的便捷方法:
// Enable thinking with a specific token budget
var options = AnthropicChatOptions.builder()
.model("claude-sonnet-4-20250514")
.temperature(1.0)
.maxTokens(16000)
.thinkingEnabled(10000L) // budget must be >= 1024 and < maxTokens
.build();
// Let Claude adaptively decide whether to think
var options = AnthropicChatOptions.builder()
.model("claude-sonnet-4-20250514")
.thinkingAdaptive()
.build();
// Explicitly disable thinking
var options = AnthropicChatOptions.builder()
.model("claude-sonnet-4-20250514")
.thinkingDisabled()
.build();
您也可以直接使用原始 SDK ThinkingConfigParam:
import com.anthropic.models.messages.ThinkingConfigParam;
import com.anthropic.models.messages.ThinkingConfigEnabled;
var options = AnthropicChatOptions.builder()
.thinking(ThinkingConfigParam.ofEnabled(
ThinkingConfigEnabled.builder().budgetTokens(10000L).build()))
.build();
非流式示例
var options = AnthropicChatOptions.builder()
.model("claude-sonnet-4-20250514")
.temperature(1.0)
.maxTokens(16000)
.thinkingEnabled(10000L)
.build();
ChatResponse response = chatModel.call(
new Prompt("Are there an infinite number of prime numbers such that n mod 4 == 3?", options));
// The response contains multiple generations:
// - ThinkingBlock generations (with "signature" in metadata)
// - TextBlock generations (with the final answer)
for (Generation generation : response.getResults()) {
AssistantMessage message = generation.getOutput();
if (message.getMetadata().containsKey("signature")) {
// This is a thinking block - contains Claude's reasoning
System.out.println("Thinking: " + message.getText());
System.out.println("Signature: " + message.getMetadata().get("signature"));
}
else if (message.getMetadata().containsKey("data")) {
// This is a redacted thinking block (safety-redacted reasoning)
System.out.println("Redacted thinking data: " + message.getMetadata().get("data"));
}
else if (message.getText() != null && !message.getText().isBlank()) {
// This is the final text response
System.out.println("Answer: " + message.getText());
}
}
流式示例
在流式模式下,完全支持思维。思维增量和签名增量会在到达时立即发出:
var options = AnthropicChatOptions.builder()
.model("claude-sonnet-4-20250514")
.temperature(1.0)
.maxTokens(16000)
.thinkingEnabled(10000L)
.build();
Flux<ChatResponse> stream = chatModel.stream(
new Prompt("Are there an infinite number of prime numbers such that n mod 4 == 3?", options));
stream.subscribe(response -> {
Generation generation = response.getResult();
AssistantMessage message = generation.getOutput();
if (message.getMetadata().containsKey("thinking")) {
// Incremental thinking content
System.out.print(message.getText());
}
else if (message.getMetadata().containsKey("signature")) {
// Thinking block signature (emitted at end of thinking)
System.out.println("\nSignature: " + message.getMetadata().get("signature"));
}
else if (message.getText() != null) {
// Final text content
System.out.print(message.getText());
}
});
多模态支持
Anthropic SDK 模块支持多模态输入,允许您在提示中同时发送图像和 PDF 文档与文本。
图片输入
使用 Media 类将图像发送到 Claude 进行分析:
var imageResource = new ClassPathResource("/test-image.png");
var userMessage = UserMessage.builder()
.text("What do you see in this image?")
.media(List.of(new Media(MimeTypeUtils.IMAGE_PNG, imageResource)))
.build();
ChatResponse response = chatModel.call(new Prompt(List.of(userMessage)));
支持的图像格式:PNG、JPEG、GIF、WebP。图像可以提供为:
-
字节数组(自动进行 Base64 编码)
-
HTTPS URLs(直接传递给 API)
PDF文档输入
发送 PDF 文档供 Claude 分析:
var pdfResource = new ClassPathResource("/document.pdf");
var userMessage = UserMessage.builder()
.text("Please summarize this document.")
.media(List.of(new Media(new MimeType("application", "pdf"), pdfResource)))
.build();
ChatResponse response = chatModel.call(new Prompt(List.of(userMessage)));
引用
Anthropic的引用API允许Claude在生成回复时引用所提供文档的具体部分。 当提示中包含引用文档时,Claude可以引用源材料,并且引用元数据(字符范围、页码或内容块)会返回在响应元数据中。
引用有助于提升:
-
准确性验证:用户可以将Claude的回复与源材料进行核对
-
透明度:准确查看文档的哪些部分为响应提供了信息
-
合规性:满足受监管行业中的源代码署名要求
-
信任:通过展示信息的来源来建立信心
|
支持的模型 引用功能在 Claude 3.7 Sonnet 和 Claude 4 模型(Opus 和 Sonnet)上受支持。 文档类型 支持三种类型的引用文档:
|
创建引文文档
使用 AnthropicCitationDocument 构建器来创建可以被引用的文档:
纯文本文档
AnthropicCitationDocument document = AnthropicCitationDocument.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
AnthropicCitationDocument document = AnthropicCitationDocument.builder()
.pdfFile("path/to/document.pdf")
.title("Technical Specification")
.citationsEnabled(true)
.build();
// From byte array
byte[] pdfBytes = loadPdfBytes();
AnthropicCitationDocument document = AnthropicCitationDocument.builder()
.pdf(pdfBytes)
.title("Product Manual")
.citationsEnabled(true)
.build();
自定义内容块
对于细粒度的引用控制,请使用自定义内容块:
AnthropicCitationDocument document = AnthropicCitationDocument.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-sonnet-4-20250514")
.maxTokens(1024)
.citationDocuments(document)
.build()
)
);
多个文档
您可以为 Claude 提供多个文档作为参考:
AnthropicCitationDocument parisDoc = AnthropicCitationDocument.builder()
.plainText("Paris is the capital city of France with a population of 2.1 million.")
.title("Paris Information")
.citationsEnabled(true)
.build();
AnthropicCitationDocument eiffelDoc = AnthropicCitationDocument.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-sonnet-4-20250514")
.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
AnthropicCitationDocument document = AnthropicCitationDocument.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-sonnet-4-20250514")
.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.");
}
最佳实践
-
使用描述性标题: 为引文文档提供有意义的标题,以帮助用户在引文中识别来源。
-
检查空引用: 并非所有响应都包含引用,因此在访问引用元数据之前,请始终验证其是否存在。
-
考虑文档大小: 更大的文档提供了更多的上下文,但会消耗更多的输入Tokens,并可能影响响应时间。
-
利用多个文档:当回答涉及多个来源的问题时,请在单次请求中提供所有相关文档,而不是进行多次调用。
-
使用适当的文档类型:对于简单内容选择纯文本,对于现有文档选择PDF,当需要对引用粒度进行精细控制时选择自定义内容块。
引用文档选项
上下文字段
可选地提供关于文档的背景信息,这些信息不会被引用,但可以帮助 Claude 更好地理解内容:
AnthropicCitationDocument document = AnthropicCitationDocument.builder()
.plainText("...")
.title("Legal Contract")
.context("This is a merger agreement dated January 2024 between Company A and Company B")
.build();
控制引用
默认情况下,所有文档的引用功能均被禁用(需手动启用)。
要启用引用功能,请显式设置 citationsEnabled(true):
AnthropicCitationDocument document = AnthropicCitationDocument.builder()
.plainText("The Eiffel Tower was completed in 1889...")
.title("Historical Facts")
.citationsEnabled(true) // Explicitly enable citations for this document
.build();
你也可以提供不包含引文的文档,以作为背景信息:
AnthropicCitationDocument backgroundDoc = AnthropicCitationDocument.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的提示缓存通过在API调用之间缓存重复上下文来降低费用和延迟。Anthropic SDK模块支持提示缓存,具有可配置的策略、TTL以及每种消息类型的设置。
缓存策略
通过 AnthropicCacheStrategy 提供了五种缓存策略:
| 策略模式 | 描述 |
|---|---|
|
无缓存(默认)。不添加任何缓存控制头。 |
|
缓存系统消息内容。使用 1 个缓存断点。 |
|
仅包含缓存工具定义。使用 1 个缓存断点。 |
|
缓存系统消息和工具定义。使用 2 个缓存断点。 |
|
缓存系统消息、工具定义和对话消息。最多使用 4 个缓存断点。 |
| Anthropic 允许每次请求最多设置 4 个缓存断点。实现会跟踪断点的使用情况,并在达到限制后停止添加缓存控制。 |
基本用法
var options = AnthropicChatOptions.builder()
.model("claude-sonnet-4-20250514")
.maxTokens(1024)
.cacheOptions(AnthropicCacheOptions.builder()
.strategy(AnthropicCacheStrategy.SYSTEM_ONLY)
.build())
.build();
ChatResponse response = chatModel.call(
new Prompt(List.of(
new SystemMessage("You are an expert assistant with deep domain knowledge..."),
new UserMessage("What is the capital of France?")),
options));
缓存配置选项
AnthropicCacheOptions 提供对缓存行为的细粒度控制:
var cacheOptions = AnthropicCacheOptions.builder()
.strategy(AnthropicCacheStrategy.SYSTEM_AND_TOOLS)
.messageTypeTtl(MessageType.SYSTEM, AnthropicCacheTtl.ONE_HOUR) // 1 hour TTL
.messageTypeMinContentLength(MessageType.SYSTEM, 100) // Min 100 chars
.multiBlockSystemCaching(true) // Per-block caching
.build();
| 选项 | 描述 | 默认 |
|---|---|---|
|
要使用的缓存策略。 |
|
|
每种消息类型的TTL。可用值: |
|
|
缓存消息类型前所需的最小内容长度。 |
|
|
自定义函数,用于计算内容长度(例如,统计Tokens数量)。 |
|
|
当 |
|
多块系统缓存
当你同时拥有静态系统提示和动态指令时,使用多块系统缓存仅缓存静态部分:
var cacheOptions = AnthropicCacheOptions.builder()
.strategy(AnthropicCacheStrategy.SYSTEM_ONLY)
.multiBlockSystemCaching(true)
.build();
ChatResponse response = chatModel.call(
new Prompt(List.of(
new SystemMessage("You are an expert knowledge base assistant..."), // Static (cached)
new SystemMessage("Today's date is 2025-02-23. User timezone: PST"), // Dynamic
new UserMessage("What are the latest updates?")),
AnthropicChatOptions.builder()
.model("claude-sonnet-4-20250514")
.cacheOptions(cacheOptions)
.build()));
访问缓存Tokens使用情况
缓存Tokens指标可通过原生SDK Usage 对象获取:
ChatResponse response = chatModel.call(prompt);
com.anthropic.models.messages.Usage sdkUsage =
(com.anthropic.models.messages.Usage) response.getMetadata().getUsage().getNativeUsage();
long cacheCreation = sdkUsage.cacheCreationInputTokens().orElse(0L);
long cacheRead = sdkUsage.cacheReadInputTokens().orElse(0L);
System.out.println("Cache creation tokens: " + cacheCreation);
System.out.println("Cache read tokens: " + cacheRead);
在首次请求时,cacheCreationInputTokens 将不为零(Tokens写入缓存)。在后续具有相同缓存前缀的请求中,cacheReadInputTokens 将不为零(以较低成本从缓存中读取Tokens)。
会话历史缓存
The CONVERSATION_HISTORY 策略会缓存整个对话上下文,包括系统消息、工具定义以及最后一条用户消息。这对于多轮对话非常有用,因为在这种情况下,不断增长的上下文否则会在每次请求时被重新处理:
var cacheOptions = AnthropicCacheOptions.builder()
.strategy(AnthropicCacheStrategy.CONVERSATION_HISTORY)
.build();
var options = AnthropicChatOptions.builder()
.model("claude-sonnet-4-20250514")
.cacheOptions(cacheOptions)
.build();
// First turn
ChatResponse response1 = chatModel.call(
new Prompt(List.of(
new SystemMessage("You are a helpful assistant."),
new UserMessage("What is machine learning?")),
options));
// Second turn - previous context is cached
ChatResponse response2 = chatModel.call(
new Prompt(List.of(
new SystemMessage("You are a helpful assistant."),
new UserMessage("What is machine learning?"),
new AssistantMessage(response1.getResult().getOutput().getText()),
new UserMessage("Can you give me an example?")),
options));
结构化输出
结构化输出会限制 Claude 生成符合 JSON 模式的响应。Anthropic SDK 模块还支持 Anthropic 的努力控制功能,用于调整响应质量与速度之间的平衡。
|
模型要求 结构化输出和努力控制需要 Schema Requirements 模式要求 当使用JSON模式输出时,Anthropic要求架构中所有对象类型都为 |
JSON Schema 输出
使用 outputSchema 便捷方法将 Claude 的响应限制为特定的 JSON 模式:
var options = AnthropicChatOptions.builder()
.model("claude-sonnet-4-6")
.outputSchema("""
{
"type": "object",
"properties": {
"name": {"type": "string"},
"capital": {"type": "string"},
"population": {"type": "integer"}
},
"required": ["name", "capital"],
"additionalProperties": false
}
""")
.build();
ChatResponse response = chatModel.call(new Prompt("Tell me about France.", options));
// Response text will be valid JSON conforming to the schema
努力控制
控制 Claude 在生成响应时投入的计算资源。较低的努力程度意味着更快、更便宜的响应;较高的努力程度意味着更深入的推理。
| 努力程度 | 描述 |
|---|---|
|
快速且简洁的响应,尽量减少推理 |
|
速度与全面性之间的平衡权衡 |
|
更深入的推理和详细的回答 |
|
最彻底响应的最大计算量 |
var options = AnthropicChatOptions.builder()
.model("claude-sonnet-4-6")
.effort(OutputConfig.Effort.LOW)
.build();
ChatResponse response = chatModel.call(new Prompt("What is the capital of France?", options));
组合模式与努力
你可以将 JSON 模式输出与努力控制结合使用:
var options = AnthropicChatOptions.builder()
.model("claude-sonnet-4-6")
.outputSchema("""
{
"type": "object",
"properties": {
"answer": {"type": "integer"},
"explanation": {"type": "string"}
},
"required": ["answer", "explanation"],
"additionalProperties": false
}
""")
.effort(OutputConfig.Effort.HIGH)
.build();
ChatResponse response = chatModel.call(
new Prompt("What is 15 * 23? Show your reasoning.", options));
直接输出配置
为了完全控制,请直接使用SDK的OutputConfig:
import com.anthropic.models.messages.OutputConfig;
import com.anthropic.models.messages.JsonOutputFormat;
import com.anthropic.core.JsonValue;
var outputConfig = OutputConfig.builder()
.effort(OutputConfig.Effort.HIGH)
.format(JsonOutputFormat.builder()
.schema(JsonOutputFormat.Schema.builder()
.putAdditionalProperty("type", JsonValue.from("object"))
.putAdditionalProperty("properties", JsonValue.from(Map.of(
"name", Map.of("type", "string"))))
.putAdditionalProperty("additionalProperties", JsonValue.from(false))
.build())
.build())
.build();
var options = AnthropicChatOptions.builder()
.model("claude-sonnet-4-6")
.outputConfig(outputConfig)
.build();
ChatResponse response = chatModel.call(new Prompt("Tell me about France.", options));
每个请求的HTTP头
Anthropic SDK 模块支持每请求 HTTP 头,这些头会被注入到各个 API 调用中。这与 customHeaders(在客户端级别为所有请求设置)不同。
按请求的头部信息对于以下情况很有用:
-
请求跟踪: 为每个请求添加关联ID或跟踪头
-
Beta API 访问: 为特定请求包含 beta 功能头
-
路由: 为负载均衡添加路由或优先级头
var options = AnthropicChatOptions.builder()
.httpHeaders(Map.of(
"X-Request-Id", "req-12345",
"X-Custom-Tracking", "my-tracking-value"))
.build();
ChatResponse response = chatModel.call(new Prompt("Hello", options));
httpHeaders 是按请求设置的,并通过 MessageCreateParams.putAdditionalHeader() 进行配置。它们不会影响其他请求。对于应应用于所有请求的头部,请使用 customHeaders。 |
示例控制器
这是一个使用聊天模型进行文本生成的简单 @RestController 类的示例:
@RestController
public class ChatController {
private final AnthropicChatModel chatModel;
public ChatController() {
var options = AnthropicChatOptions.builder()
.model("claude-sonnet-4-20250514")
.maxTokens(1024)
.apiKey(System.getenv("ANTHROPIC_API_KEY"))
.build();
this.chatModel = new AnthropicChatModel(options);
}
@GetMapping("/ai/generate")
public Map<String, String> generate(
@RequestParam(value = "message", defaultValue = "Tell me a joke") String message) {
return Map.of("generation", 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 chatModel.stream(prompt);
}
}
访问原始响应
完整的Anthropic SDK Message 对象在响应元数据的 "anthropic-response" 键下可用。这提供了对Spring AI抽象未明确映射的任何字段的访问:
ChatResponse response = chatModel.call(new Prompt("Hello"));
com.anthropic.models.messages.Message rawMessage =
(com.anthropic.models.messages.Message) response.getMetadata().get("anthropic-response");
// Access native SDK fields
rawMessage.stopReason(); // Optional<StopReason>
rawMessage.content(); // List<ContentBlock>
rawMessage.usage(); // Usage with cache token details
| 原始响应仅对同步调用可用。流式响应不包含它。 |
技能
Anthropic的技能API通过专门的预打包功能扩展了Claude的能力,用于文档生成。 技能使Claude能够创建实际可下载的文件——Excel电子表格、PowerPoint演示文稿、Word文档和PDF文件——而不仅仅是描述这些文档可能包含的内容。
|
支持的模型 技能功能支持 Claude Sonnet 4、Claude Sonnet 4.5、Claude Opus 4 及后续版本模型。 要求
|
预构建的Anthropic技能
Spring AI 提供通过 AnthropicSkill 枚举对 Anthropic 的预构建技能进行类型安全的访问:
| 技能 | 描述 | 生成的文件类型 |
|---|---|---|
|
Excel 电子表格的生成与操作 |
|
|
PowerPoint 演示文稿创建 |
|
|
Word 文档生成 |
|
|
PDF文档创建 |
|
基本用法
通过将它们添加到您的 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(Model.CLAUDE_SONNET_4_5)
.maxTokens(4096)
.skill(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(Model.CLAUDE_SONNET_4_5)
.maxTokens(8192)
.skill(AnthropicSkill.XLSX)
.skill(AnthropicSkill.PPTX)
.build()
)
);
使用AnthropicSkillContainer进行高级配置
为了更精细地控制技能类型和版本,请直接使用 AnthropicSkillContainer:
AnthropicSkillContainer container = AnthropicSkillContainer.builder()
.skill(AnthropicSkill.XLSX)
.skill(AnthropicSkill.PPTX, "20251013") // Specific version
.build();
ChatResponse response = chatModel.call(
new Prompt(
"Generate the quarterly report",
AnthropicChatOptions.builder()
.model(Model.CLAUDE_SONNET_4_5)
.maxTokens(4096)
.skillContainer(container)
.build()
)
);
下载生成的文件
当Claude使用技能生成文件时,响应中包含可用于通过Files API下载实际文件的文件ID。
Spring AI提供了AnthropicSkillsResponseHelper工具类来提取文件ID并下载文件。
提取文件ID
import org.springframework.ai.anthropic.AnthropicSkillsResponseHelper;
ChatResponse response = chatModel.call(prompt);
// Extract all file IDs from the response
List<String> fileIds = AnthropicSkillsResponseHelper.extractFileIds(response);
for (String fileId : fileIds) {
System.out.println("Generated file ID: " + fileId);
}
下载所有文件
The AnthropicSkillsResponseHelper 提供了一个便捷方法,可以一次性下载所有生成的文件。
这需要 AnthropicClient 实例(用于创建聊天模型的同一实例):
import com.anthropic.client.AnthropicClient;
@Autowired
private AnthropicClient anthropicClient;
// Download all files to a target directory
Path targetDir = Path.of("generated-files");
Files.createDirectories(targetDir);
List<Path> savedFiles = AnthropicSkillsResponseHelper.downloadAllFiles(
response, anthropicClient, targetDir);
for (Path file : savedFiles) {
System.out.println("Downloaded: " + file.getFileName() +
" (" + Files.size(file) + " bytes)");
}
完整示例
这是一个完整的示例,展示如何在文件下载中使用 Skills:
@Service
public class DocumentGenerationService {
private final AnthropicChatModel chatModel;
private final AnthropicClient anthropicClient;
public DocumentGenerationService(AnthropicChatModel chatModel,
AnthropicClient anthropicClient) {
this.chatModel = chatModel;
this.anthropicClient = anthropicClient;
}
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(Model.CLAUDE_SONNET_4_5)
.maxTokens(4096)
.skill(AnthropicSkill.XLSX)
.build()
)
);
// Extract file IDs from the response
List<String> fileIds = AnthropicSkillsResponseHelper.extractFileIds(response);
if (fileIds.isEmpty()) {
throw new RuntimeException("No file was generated");
}
// Download all generated files
List<Path> savedFiles = AnthropicSkillsResponseHelper.downloadAllFiles(
response, anthropicClient, outputDir);
return savedFiles.get(0);
}
}
最佳实践
-
使用合适的模型:技能在 Claude Sonnet 4 及更高版本的模型上效果最佳。请确保您使用的是受支持的模型。
-
设置足够的最大Tokens数: 文档生成可能需要大量Tokens。对于复杂文档,使用
maxTokens(4096)或更高值。 -
提示要具体: 提供关于文档结构、内容和格式的清晰、详细的说明。
-
及时处理文件下载:生成的文件在24小时后过期。请在生成后尽快下载文件。
-
检查文件ID: 在尝试下载之前,始终验证是否返回了文件ID。某些提示可能会生成纯文本响应而不会创建文件。
-
使用防御性错误处理: 将文件操作包裹在 try-catch 块中,以优雅地处理网络问题或过期文件。
List<String> fileIds = AnthropicSkillsResponseHelper.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 {
List<Path> files = AnthropicSkillsResponseHelper.downloadAllFiles(
response, anthropicClient, targetDir);
// Process files...
} catch (IOException e) {
log.error("Failed to download file: {}", e.getMessage());
}