|
此版本仍在开发中,尚未被视为稳定版。如需最新的快照版本,请使用 Spring AI 1.1.3! |
Anthropic Chat
Spring AI 通过官方的 Anthropic Java SDK 支持 Anthropic 的 Claude 模型,从而可以通过 Anthropic 的 API 访问 Claude。
前置条件
在 Anthropic 控制台 创建一个账户,并在 API 密钥页面 生成一个 API 密钥。
Auto-Configuration
Spring Boot 自动配置可通过 spring-ai-starter-model-anthropic Starters获得。
-
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 连接和聊天选项:
| <property> </property> | <description> </description> | 默认 |
|---|---|---|
|
Anthropic API 密钥 |
- |
|
API 基础 URL |
|
|
模型名称 |
|
|
最大Tokens数 |
|
|
采样温度 |
- |
|
Top-p 采样 |
- |
|
Top-k 采样 |
- |
手动配置
AnthropicChatModel 实现了 ChatModel 接口,并使用官方的 Anthropic Java SDK 连接到 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());
运行时选项
AnthropicChatOptions.java 类提供了模型配置,例如要使用的模型、温度、最大Tokens数等。
在启动时,使用 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()
));
聊天选项
| 选项 | <description> </description> | 默认 |
|---|---|---|
模型 |
要使用的 Claude 模型名称。可用模型包括: |
|
最大Tokens数 |
在响应中生成的最大Tokens数。 |
4096 |
温度 |
控制响应中的随机性。较高的值会使输出更随机,较低的值会使输出更确定。范围:0.0-1.0 |
1.0 |
顶部段落 |
Nucleus 采样参数。模型考虑具有 top_p 概率质量的Tokens。 |
- |
前 K 个 |
仅从每个Tokens的前 K 个选项中进行采样。 |
- |
停止序列 |
将导致模型停止生成的自定义序列。 |
- |
apiKey |
用于身份验证的 API 密钥。如果未设置,则从 |
- |
baseUrl |
Anthropic API 的基础 URL。 |
|
timeout |
请求超时时间。 |
60 秒 |
maxRetries |
失败请求的最大重试次数。 |
2 |
代理 |
HTTP 客户端的代理设置。 |
- |
自定义请求头 |
在所有请求中包含的自定义 HTTP 头(客户端级别)。 |
- |
HTTP 头部 |
每个请求的 HTTP 头。这些通过 |
- |
思考 |
思考配置。使用便捷构建器 |
- |
outputConfig |
输出结构化输出(JSON 模式)和力度控制的配置。使用 |
- |
工具调用选项
| 选项 | <description> </description> | 默认 |
|---|---|---|
工具选择 |
控制模型调用哪个工具(如果有)。使用 |
自动 |
工具回调 |
要注册到模型中的工具回调列表。 |
- |
工具名称 |
需在运行时解析的工具名称集合。 |
- |
内部工具执行已启用 |
如果为 false,工具调用将被代理到客户端进行手动处理。如果为 true,Spring AI 将在内部处理工具调用。 |
true |
禁用并行工具使用 |
当为 true 时,模型每个响应最多使用一个工具。 |
false |
| 除了模型特定的 AnthropicChatOptions 之外,您还可以使用通过 ChatOptions#builder() 创建的可移植 ChatOptions 实例。 |
工具调用
您可以使用 AnthropicChatModel 注册自定义 Java 函数或方法,并让 Claude 智能地选择输出一个 JSON 对象,其中包含调用一个或多个已注册函数/工具的参数。
这是一种将大语言模型能力与外部工具和 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());
}
});
响应结构
当启用思考功能时,响应将包含不同类型的内容:
| 内容类型 | 元数据键 | <description> </description> |
|---|---|---|
思考块 |
|
Claude 的推理文本及其加密签名。在同步模式下,思考文本位于 |
已编辑的思维 |
|
安全脱敏的推理内容。仅包含一个 |
签名(流式) |
|
在流式模式下,签名作为思考块末尾的一个独立增量到达。 |
思考 Delta(流式) |
|
在流式传输过程中增量处理文本块。 |
文本块 |
(none) |
最终答案文本在 |
多模态支持
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 URL(直接传递给 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();
}
引用类型
引用根据文档类型包含不同的位置信息:
字符位置(纯文本)
对于纯文本文档,引用包括字符索引:<br>
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);
}
Page Location (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.");
}
最佳实践
-
使用描述性标题:为引用文档提供有意义的标题,帮助用户识别引文中的来源。
-
检查引用是否为空:并非所有响应都会包含引用,因此在访问引用元数据之前,请始终验证其是否存在。
-
考虑文档大小:较大的文档提供更多的上下文,但会消耗更多的输入标记,并可能影响响应时间。
-
利用多个文档:在回答涉及多个来源的问题时,将所有相关文档在一个请求中提供,而不是多次调用。
-
选择合适的文档类型:对于简单的内容,请选择纯文本;对于现有的文档,请选择PDF;当您需要对引用粒度进行精细控制时,请使用自定义内容块。
引用文档选项
上下文字段
Optionally provide context about the document that won’t be cited but can guide Claude’s understanding:
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 可使用五种缓存策略:
| 策略 | <description> </description> |
|---|---|
|
无缓存(默认)。未添加任何缓存控制头。 |
|
缓存系统消息内容。使用 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();
| 选项 | <description> </description> | 默认 |
|---|---|---|
|
要使用的缓存策略。 |
|
|
每条消息类型的生存时间(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)。
对话历史记录缓存
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 的努力控制功能,用于调节响应质量与速度之间的平衡。
|
模型要求 结构化输出和努力控制需要 架构要求 当使用 JSON Schema 输出时,Anthropic 要求架构中的所有对象类型均为 |
JSON 架构输出
使用 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 在响应上投入的计算量。较低的努力程度意味着更快、更便宜的响应;较高的努力程度意味着更彻底的推理。
| 努力程度 | <description> </description> |
|---|---|
|
快速且简洁的响应,推理过程最少 |
|
速度与全面性之间的平衡权衡 |
|
更透彻的推理和更详细的响应 |
|
最大计算能力,以提供最详尽的可能响应 |
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 的 Skills API 通过专为文档生成设计的预制功能,扩展了 Claude 的能力。 Skills 使 Claude 能够创建实际可下载的文件——如 Excel 电子表格、PowerPoint 演示文稿、Word 文档和 PDF 文件——而不仅仅是描述这些文件可能包含的内容。
|
支持的模型 技能在Claude Sonnet 4、Claude Sonnet 4.5、Claude Opus 4及后续模型中受到支持。 要求
|
预构建的Anthropic技能
Spring AI通过AnthropicSkill枚举提供了类型安全访问Anthropic预构建技能的方式:
| 技能 | <description> </description> | 生成的文件类型 |
|---|---|---|
|
Excel电子表格生成和操作 |
|
|
PowerPoint演示文稿创建 |
|
|
生成字文档 |
|
|
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使用Skills生成文件时,响应中包含可用于通过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);
}
下载所有文件
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());
}