Anthropic チャット
Spring AI は、公式の Anthropic Java SDK [GitHub] (英語) を通じて Anthropic の Claude モデルをサポートし、Anthropic の API を通じて Claude へのアクセスを提供します。
前提条件
Anthropic Console (英語) でアカウントを作成し、API キーページ (英語) で API キーを生成してください。
リポジトリと BOM の追加
Spring AI アーティファクトは、Maven Central リポジトリと Spring スナップショットリポジトリに公開されています。これらのリポジトリをビルドシステムに追加するには、アーティファクトリポジトリセクションを参照してください。
依存関係の管理を支援するために、Spring AI は BOM (部品表) を提供し、一貫したバージョンの Spring AI がプロジェクト全体で使用されるようにします。Spring AI BOM をビルドシステムに追加するには、"依存関係管理" セクションを参照してください。
自動構成
Spring Boot の自動構成は、spring-ai-starter-model-anthropic スターターを介して利用可能です。
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 | |
| Anthropic クライアントのリクエストがタイムアウトしました |
|
| 失敗したリクエストに対する最大再試行回数 |
|
| すべての Anthropic クライアントリクエストに追加するカスタム HTTP ヘッダー | - |
| モデル名 |
|
| 最大トークン数 |
|
| サンプリング温度 | - |
| トップ p サンプリング | - |
| トップ k サンプリング | - |
| 使用するキャッシュ戦略 |
|
| システムメッセージごとに個別のブロックを使用する |
|
| 個々の API 呼び出しに渡すリクエストごとの HTTP ヘッダー。 | - |
| リクエストが処理される地理的領域 ( | - |
| Maximum number of web searches per request | - |
| Comma-separated list of domains to restrict search results to | - |
| Comma-separated list of domains to exclude from search results | - |
| City for localizing search results | - |
| ISO 3166-1 alpha-2 country code | - |
| Region or state | - |
| IANA timezone identifier | - |
| Capacity routing: | - |
手動構成
The AnthropicChatModel [GitHub] (英語) 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 をビルドファイルに追加するには、"依存関係管理" セクションを参照してください。 |
認証
Configure your API key either programmatically or via environment variable:
var chatOptions = AnthropicChatOptions.builder()
.model("claude-sonnet-4-20250514")
.maxTokens(1024)
.apiKey(System.getenv("ANTHROPIC_API_KEY"))
.build();
var chatModel = AnthropicChatModel.builder()
.options(chatOptions)
.build();Or set the environment variable and let the SDK auto-detect it:
export ANTHROPIC_API_KEY=<your-api-key>// API key will be detected from ANTHROPIC_API_KEY environment variable
var chatModel = AnthropicChatModel.builder()
.options(AnthropicChatOptions.builder()
.model("claude-sonnet-4-20250514")
.maxTokens(1024)
.build())
.build();ランタイムオプション
The AnthropicChatOptions.java [GitHub] (英語) class provides model configurations such as the model to use, temperature, max tokens, etc.
On start-up, configure default options with the AnthropicChatModel.builder().defaultOptions(options).build() constructor.
実行時に、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()
));チャットオプション
| オプション | 説明 | デフォルト |
|---|---|---|
model | Name of the Claude model to use. Models include: |
|
maxTokens | The maximum number of tokens to generate in the response. | 4096 |
temperature | Controls randomness in the response. Higher values make output more random, lower values make it more deterministic. Range: 0.0-1.0 | 1.0 |
topP | Nucleus sampling parameter. The model considers tokens with top_p probability mass. Supported on Claude Opus 4.6 and earlier models only. Deprecated by Anthropic for models released after Claude Opus 4.6 (e.g. | - |
topK | Only sample from the top K options for each token. Supported on Claude Opus 4.6 and earlier models only. Deprecated by Anthropic for models released after Claude Opus 4.6 (e.g. | - |
stopSequences | Custom sequences that will cause the model to stop generating. | - |
apiKey | The API key for authentication. Auto-detects from | - |
baseUrl | Anthropic API のベース URL。 | |
timeout | リクエストのタイムアウト期間。 | 60 秒 |
maxRetries | 失敗したリクエストの最大再試行回数。 | 2 |
proxy | Proxy settings for the HTTP client. | - |
customHeaders | Custom HTTP headers to include on all requests (client-level). | - |
httpHeaders | Per-request HTTP headers. These are added to individual API calls via | - |
cacheOptions | Options for configuring prompt caching behavior. Includes caching strategy, multi-block caching, TTL, and content length requirements. | - |
thinking | Thinking configuration. Use the convenience builders | - |
outputConfig | Output configuration for structured output (JSON schema) and effort control. Use | - |
inferenceGeo | Controls the geographic region where the request is processed. Supported values: | - |
serviceTier | Controls capacity routing for the request. Use | - |
Tool Calling Options
| オプション | 説明 | デフォルト |
|---|---|---|
toolChoice | Controls which tool (if any) is called by the model. Use | AUTO |
toolCallbacks | List of tool callbacks to register with the model. | - |
disableParallelToolUse | When true, the model will use at most one tool per response. | false |
| In addition to the model-specific AnthropicChatOptions [GitHub] (英語) , you can use a portable ChatOptions [GitHub] (英語) instance, created with ChatOptions#builder() [GitHub] (英語) 。 |
レート制限メタデータ
The Anthropic API includes rate limit headers (英語) on every response that report your current request and token budgets along with when each window resets. Spring AI exposes this data through the standard ChatResponseMetadata#getRateLimit() accessor, which returns an AnthropicRateLimit populated from the response headers.
The portable RateLimit interface exposes the request and token families:
ChatResponse response = chatModel.call(prompt);
RateLimit rateLimit = response.getMetadata().getRateLimit();
rateLimit.getRequestsLimit(); // overall request ceiling
rateLimit.getRequestsRemaining(); // requests left in the current window
rateLimit.getRequestsReset(); // time until the request window resets
rateLimit.getTokensLimit(); // overall token ceiling
rateLimit.getTokensRemaining(); // tokens left in the current window
rateLimit.getTokensReset(); // time until the token window resetsAnthropic also reports separate buckets for input tokens and output tokens. These are exposed by the AnthropicRateLimit concrete type:
if (response.getMetadata().getRateLimit() instanceof AnthropicRateLimit anthropic) {
anthropic.getInputTokensLimit();
anthropic.getInputTokensRemaining();
anthropic.getInputTokensReset();
anthropic.getOutputTokensLimit();
anthropic.getOutputTokensRemaining();
anthropic.getOutputTokensReset();
}If Anthropic does not return any rate-limit headers on a given response, getRateLimit() returns an EmptyRateLimit instance instead, matching the convention used by other Spring AI providers.
Rate-limit metadata is populated on both the synchronous call(Prompt) and the streaming stream(Prompt) paths. For streaming responses the headers are read once at stream start and attached to the response chunk that carries the final usage, so getRateLimit() resolves to a populated AnthropicRateLimit there.
To read the response headers, the streaming path consumes the Anthropic SDK’s blocking stream on a Schedulers.boundedElastic() worker, so a streaming call holds a worker thread for the duration of the stream. |
ツール呼び出し
You can register custom Java functions or methods with the AnthropicChatModel and have Claude intelligently choose to output a JSON object containing arguments to call one or many of the registered functions/tools. This is a powerful technique to connect the LLM capabilities with external tools and API. Read more about ツール呼び出し。
AnthropicChatModel はツール呼び出しを内部的に実行しません。ツール実行は、サポートされている 2 つの方法のいずれかを使用して外部で処理する必要があります。
ChatClient と ToolCallingAdvisor — ほとんどのユースケースにおいて推奨されるアプローチです。
ToolCallingAdvisorは自動的に登録され、ツール呼び出しループを透過的に管理します。ユーザー制御によるツール実行 — use
DefaultToolCallingManagerdirectly when you need full control over the loop (for example, when combining tool calling with prompt caching).
ChatClient 経由のツール呼び出し (推奨)
同期ツール実行とストリーミングツール実行の両方で、ChatClient と ToolCallingAdvisor を併用してください。ツールが存在する場合、ToolCallingAdvisor は自動的に登録されるため、アドバイザーの明示的な設定は不要です。
ToolCallback weatherCallback = FunctionToolCallback.builder("getCurrentWeather", new WeatherService())
.description("Get the weather in location")
.inputType(WeatherService.Request.class)
.build();
// Synchronous
String response = ChatClient.create(chatModel)
.prompt()
.user("What's the weather in Paris, Tokyo, and New York?")
.tools(weatherCallback)
.call()
.content();
// Streaming
Flux<String> stream = ChatClient.create(chatModel)
.prompt()
.user("What's the weather in Paris, Tokyo, and New York?")
.tools(weatherCallback)
.stream()
.content();ユーザー制御ツールの実行
Use this pattern when you need direct access to the ChatModel API — for example, when combining tool calling with prompt caching. Invoke ChatModel directly without ToolCallingAdvisor; check for tool calls yourself and drive the loop using ToolCallingManager.
ToolCallingManager toolCallingManager = ToolCallingManager.builder().build();
AnthropicChatOptions options = AnthropicChatOptions.builder()
.toolCallbacks(ToolCallbacks.from(new WeatherService()))
.build();
Prompt prompt = new Prompt("What's the weather in Paris, Tokyo, and New York?", options);
ChatResponse response = chatModel.call(prompt);
while (response.hasToolCalls()) {
ToolExecutionResult result = toolCallingManager.executeToolCalls(prompt, response);
prompt = new Prompt(result.conversationHistory(), options);
response = chatModel.call(prompt);
} ストリーミングの場合、各ストリーミングレスポンスを MessageAggregator で集約して、チャンクをまたいだツール呼び出しを検出します。
AtomicReference<ChatResponse> aggregatedRef = new AtomicReference<>();
new MessageAggregator()
.aggregate(chatModel.stream(prompt), aggregatedRef::set)
.collectList().block();
while (aggregatedRef.get().hasToolCalls()) {
ToolExecutionResult result = toolCallingManager.executeToolCalls(prompt, aggregatedRef.get());
prompt = new Prompt(result.conversationHistory(), options);
aggregatedRef.set(null);
new MessageAggregator()
.aggregate(chatModel.stream(prompt), aggregatedRef::set)
.collectList().block();
}
String content = aggregatedRef.get().getResult().getOutput().getText();ツール選択オプション
Control which tool Claude selects with the toolChoice option:
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();The Anthropic Java SDK provides convenient static factory methods for common tool choices, which can make your code more concise:
|
ストリーミング
The Anthropic SDK module supports both synchronous and streaming responses. Streaming allows Claude to return responses incrementally as they’re generated.
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);
}
});Extended Thinking
Anthropic Claude models support a "thinking" feature that allows the model to show its reasoning process before providing a final answer. This is especially useful for complex questions that require step-by-step reasoning, such as math, logic, and analysis tasks.
対応モデル 思考機能は、次の Claude モデルでサポートされています。
モデルの機能:
API リクエスト構造はサポートされているすべてのモデルで同じですが、出力動作は異なります。 |
思考構成
To enable thinking, configure the following:
Set a thinking budget : The
budgetTokensmust be >= 1024 and less thanmaxTokens.Set temperature to 1.0 : Required when thinking is enabled.
Convenience Builder Methods
AnthropicChatOptions.Builder provides convenience methods for thinking configuration:
// 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();You can also use the raw SDK ThinkingConfigParam directly:
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();Thinking Display Setting
By default, full thinking output is returned in the response. You can control this with the display parameter to reduce token costs:
SUMMARIZED— Claude still thinks fully, but returns a condensed summary instead of the raw chain-of-thought. Reduces output tokens while still providing insight into the reasoning.OMITTED— Thinking is performed but completely redacted from the response. Only a cryptographic signature is returned (needed for multi-turn continuity). Lowest output token cost.
import com.anthropic.models.messages.ThinkingConfigEnabled;
import com.anthropic.models.messages.ThinkingConfigAdaptive;
// Enabled thinking with summarized display
var options = AnthropicChatOptions.builder()
.model("claude-sonnet-4-20250514")
.temperature(1.0)
.maxTokens(16000)
.thinkingEnabled(10000L, ThinkingConfigEnabled.Display.SUMMARIZED)
.build();
// Enabled thinking with omitted display
var options = AnthropicChatOptions.builder()
.model("claude-sonnet-4-20250514")
.temperature(1.0)
.maxTokens(16000)
.thinkingEnabled(10000L, ThinkingConfigEnabled.Display.OMITTED)
.build();
// Adaptive thinking with summarized display
var options = AnthropicChatOptions.builder()
.model("claude-sonnet-4-20250514")
.temperature(1.0)
.maxTokens(16000)
.thinkingAdaptive(ThinkingConfigAdaptive.Display.SUMMARIZED)
.build();| The display setting does not affect the quality of the final answer — Claude performs the same amount of thinking regardless. It only controls what thinking content is returned in the response. |
非ストリーミングの例
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());
}
}ストリーミングの例
Thinking is fully supported in streaming mode. Thinking deltas and signature deltas are emitted as they arrive:
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());
}
});レスポンス構造
When thinking is enabled, the response contains different types of content:
| コンテンツタイプ | Metadata Key | 説明 |
|---|---|---|
Thinking Block |
| Claude’s reasoning text with a cryptographic signature. In sync mode, the thinking text is in |
Redacted Thinking |
| Safety-redacted reasoning. Contains only a |
シグネチャー (streaming) |
| In streaming mode, the signature arrives as a separate delta at the end of a thinking block. |
Thinking Delta (streaming) |
| Incremental thinking text chunks during streaming. The |
テキストブロック | (なし) | The final answer text in |
Multi-Modal Support
The Anthropic SDK module supports multi-modal inputs, allowing you to send images and PDF documents alongside text in your prompts.
Image Input
Send images to Claude for analysis using the Media class:
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)));Supported image formats: PNG, JPEG, GIF, WebP. Images can be provided as:
Byte 配列 (automatically base64-encoded)
HTTPS URLs (passed directly to the API)
PDF Document Input
Send PDF documents for Claude to analyze:
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) でサポートされています。 ドキュメントタイプ 3 種類の引用ドキュメントがサポートされています。
|
引用ドキュメントの作成
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.");
}ベストプラクティス
説明的なタイトルを使用する : ユーザーが引用内のソースを識別できるように、引用ドキュメントに意味のあるタイトルを付けます。
null 引用をチェックする : すべてのレスポンスに引用が含まれるわけではないため、アクセスする前に引用のメタデータが存在することを必ず検証してください。
ドキュメントサイズを考慮する : ドキュメントが大きいほどコンテキストは多くなりますが、消費される入力トークンも多くなり、レスポンス時間に影響する可能性があります。
複数のドキュメントを活用する : 複数のソースにまたがる質問に答える場合は、複数回呼び出すのではなく、1 回のリクエストですべての関連ドキュメントを提供してください。
適切なドキュメント型を使用する : シンプルなコンテンツにはプレーンテキスト、既存のドキュメントには 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’s プロンプトキャッシュ (英語) reduces costs and latency by caching repeated context across API calls. The Anthropic SDK module supports prompt caching with configurable strategies, TTL, and per-message-type settings.
Caching Strategies
Five caching strategies are available via AnthropicCacheStrategy:
| 戦略 | 説明 |
|---|---|
| No caching (default). No cache control headers are added. |
| Cache system message content. Uses 1 cache breakpoint. |
| Cache tool definitions only. Uses 1 cache breakpoint. |
| Cache both system messages and tool definitions. Uses 2 cache breakpoints. |
| Cache system messages, tool definitions, and conversation messages. Uses up to 4 cache breakpoints. |
| Anthropic allows a maximum of 4 cache breakpoints per request. The implementation tracks breakpoint usage and stops adding cache control once the limit is reached. |
基本的な使い方
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));Cache Configuration Options
AnthropicCacheOptions provides fine-grained control over caching behavior:
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();| オプション | 説明 | デフォルト |
|---|---|---|
| The caching strategy to use. |
|
| TTL per message type. Available values: |
|
| Minimum content length required before caching a message type. |
|
| Custom function to compute content length (e.g., token counting). |
|
| When |
|
| When |
|
Caching Tool Results
When a turn triggers tool calls, each round appends an assistant tool_use message and a user tool_result message, and the growing history is sent back to Anthropic on every round. By default the CONVERSATION_HISTORY breakpoint lands on the last user message, so tool results — which are appended after it — fall outside every breakpoint and are billed as uncached input on each round. This is wasteful when tool outputs are large.
Enable cacheToolResults to place a breakpoint on the last tool result block of the request. On the next round Anthropic reads the previous round’s tool results from cache instead of reprocessing them:
var cacheOptions = AnthropicCacheOptions.builder()
.strategy(AnthropicCacheStrategy.CONVERSATION_HISTORY)
.cacheToolResults(true)
.build();The breakpoint moves to the latest tool result on each round, so the previous rounds' tool outputs are read from cache while the newest is written. This consumes one additional cache breakpoint (Anthropic allows up to four per request), leaving room for the tool, system, and last-user breakpoints under CONVERSATION_HISTORY. The TTL and minimum content length are configurable via messageTypeTtl(MessageType.TOOL, …) and messageTypeMinContentLength(MessageType.TOOL, …).
Multi-Block System Caching
When you have both a static system prompt and dynamic instructions, use multi-block system caching to cache only the static portion:
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()));Spring Boot の設定
Configure prompt caching via application.properties or application.yml:
spring.ai.anthropic.chat.cache-options.strategy=SYSTEM_AND_TOOLS
spring.ai.anthropic.chat.cache-options.multi-block-system-caching=trueAccessing Cache Token Usage
Cache token metrics are available through the native SDK Usage object:
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);On the first request, cacheCreationInputTokens will be non-zero (tokens written to cache). On subsequent requests with the same cached prefix, cacheReadInputTokens will be non-zero (tokens read from cache at reduced cost).
会話履歴のキャッシュ
The CONVERSATION_HISTORY strategy caches the entire conversation context, including system messages, tool definitions, and the last user message. This is useful for multi-turn conversations where the growing context would otherwise be re-processed on every request:
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));構造化された出力
Structured output constrains Claude to produce responses conforming to a JSON schema. The Anthropic SDK module also supports Anthropic’s effort control for tuning response quality vs speed.
Model Requirement Structured output and effort control require Schema Requirements When using JSON schema output, Anthropic requires |
JSON Schema Output
Constrain Claude’s responses to a specific JSON schema using the outputSchema convenience method:
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 schemaEffort Control
Control how much compute Claude spends on its response. Lower effort means faster, cheaper responses; higher effort means more thorough reasoning.
| Effort Level | 説明 |
|---|---|
| Fast and concise responses with minimal reasoning |
| Balanced trade-off between speed and thoroughness |
| More thorough reasoning and detailed responses |
| Maximum compute for the most thorough possible responses |
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));Combined Schema and Effort
You can combine JSON schema output with effort control:
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));Direct OutputConfig
For full control, use the SDK’s OutputConfig directly:
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));Service Tier
Anthropic offers different service tiers (英語) that control capacity routing for API requests. You can use AnthropicServiceTier.AUTO to opportunistically use priority capacity (lower latency) when available, or STANDARD_ONLY to always use standard capacity.
Via Spring Boot properties:
spring.ai.anthropic.chat.service-tier=autoOr programmatically per-request:
var options = AnthropicChatOptions.builder()
.serviceTier(AnthropicServiceTier.AUTO)
.build();
ChatResponse response = chatModel.call(new Prompt("Hello", options));Per-Request HTTP Headers
The Anthropic SDK module supports per-request HTTP headers, which are injected into individual API calls. This is distinct from customHeaders (which are set at the client level for all requests).
Per-request headers are useful for:
Request tracking : Adding correlation IDs or trace headers per request
Beta API access : Including beta feature headers for specific requests
ルーティング : Adding routing or priority headers for load balancing
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 are per-request and set via MessageCreateParams.putAdditionalHeader(). They do not affect other requests. For headers that should apply to all requests, use customHeaders instead. |
サンプルコントローラー
Here is an example of a simple @RestController class that uses the chat model for text generations:
@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 = AnthropicChatModel.builder()
.options(options)
.build();
}
@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);
}
}Accessing the Raw Response
The full Anthropic SDK Message object is available in the response metadata under the "anthropic-response" key. This provides access to any fields not explicitly mapped by Spring AI’s abstraction:
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| The raw response is available for synchronous calls only. Streaming responses do not include it. |
スキル
Anthropic’s スキル API (英語) extends Claude’s capabilities with specialized, pre-packaged abilities for document generation. Skills enable Claude to create actual downloadable files — Excel spreadsheets, PowerPoint presentations, Word documents, and PDFs — rather than just describing what these documents might contain.
対応モデル スキルは、Claude Sonnet 4、Claude Sonnet 4.5、Claude Opus 4 以降のモデルでサポートされています。 要件
|
プリビルド Anthropic スキル
Spring AI は、AnthropicSkill 列挙型を通じて Anthropic の事前構築されたスキルへの型安全なアクセスを提供します。
| スキル | 説明 | 生成されたファイルの種類 |
|---|---|---|
| Excel スプレッドシートの生成と操作 |
|
| PowerPoint プレゼンテーション作成 |
|
| Word ドキュメントの生成 |
|
| PDF ドキュメントの作成 |
|
基本的な使い方
Enable skills by adding them to your 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..."Multiple Skills
You can enable multiple skills in a single request (up to 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()
)
);Using AnthropicSkillContainer for Advanced Configuration
For more control over skill types and versions, use AnthropicSkillContainer directly:
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()
)
);Downloading Generated Files
When Claude generates files using Skills, the response contains file IDs that can be used to download the actual files via the Files API. Spring AI provides the AnthropicSkillsResponseHelper utility class for extracting file IDs and downloading files.
Extracting File IDs
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);
}Downloading All Files
The AnthropicSkillsResponseHelper provides a convenience method to download all generated files at once. This requires the AnthropicClient instance (the same one used to create the chat model):
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)");
}完全な例
Here’s a complete example showing Skills usage with file download:
@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 以降のモデルで最も効果的に機能します。サポートされているモデルを使用していることをご確認ください。
十分な最大トークンを設定する : ドキュメント生成には大量のトークンが必要になる場合があります。複雑なドキュメントの場合は、
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());
}Web 検索
Anthropic’s Web 検索 (英語) tool allows Claude to search the web during a conversation and use the results to generate cited responses.
|
基本的な使い方
Enable web search by adding an AnthropicWebSearchTool to your chat options:
var webSearch = AnthropicWebSearchTool.builder().build();
ChatResponse response = chatModel.call(
new Prompt("What is the latest released version of Spring AI?",
AnthropicChatOptions.builder()
.webSearchTool(webSearch)
.build()));
String answer = response.getResult().getOutput().getText();構成オプション
| オプション | 説明 | デフォルト |
|---|---|---|
maxUses | Maximum number of web searches Claude can perform per request | - |
allowedDomains | Restrict search results to these domains only | - |
blockedDomains | Exclude these domains from search results | - |
userLocation | Approximate user location for localizing results (city, country, region, timezone) | - |
Domain Filtering
Restrict or exclude specific domains from search results:
var webSearch = AnthropicWebSearchTool.builder()
.allowedDomains(List.of("docs.spring.io", "github.com"))
.blockedDomains(List.of("example.com"))
.maxUses(5)
.build();User Location
Provide approximate location to localize search results:
var webSearch = AnthropicWebSearchTool.builder()
.userLocation("San Francisco", "US", "California", "America/Los_Angeles")
.build();Accessing Web Search Results
Web search results and citations are available in the response metadata:
ChatResponse response = chatModel.call(
new Prompt("What happened in tech news today?",
AnthropicChatOptions.builder()
.webSearchTool(AnthropicWebSearchTool.builder().build())
.build()));
// Get web search results
List<AnthropicWebSearchResult> results =
(List<AnthropicWebSearchResult>) response.getMetadata().get("web-search-results");
if (results != null) {
for (AnthropicWebSearchResult result : results) {
System.out.println("Title: " + result.title());
System.out.println("URL: " + result.url());
System.out.println("Page age: " + result.pageAge());
}
}
// Get web search citations
List<Citation> citations =
(List<Citation>) response.getMetadata().get("citations");
if (citations != null) {
for (Citation citation : citations) {
if (citation.getType() == Citation.LocationType.WEB_SEARCH_RESULT_LOCATION) {
System.out.println("Source: " + citation.getUrl());
System.out.println("Title: " + citation.getDocumentTitle());
System.out.println("Cited text: " + citation.getCitedText());
}
}
}Spring Boot の設定
Configure web search via application.properties or application.yml:
spring.ai.anthropic.chat.web-search-tool.max-uses=5
spring.ai.anthropic.chat.web-search-tool.allowed-domains=docs.spring.io,github.com
spring.ai.anthropic.chat.web-search-tool.user-location.city=San Francisco
spring.ai.anthropic.chat.web-search-tool.user-location.country=US可観測性
Spring AI は、Anthropic 呼び出しごとに 2 つのレイヤーで Micrometer 観測値を出力します。
Chat-model layer。A
gen_ai.client.operationobservation wraps every call toAnthropicChatModel.call(…)or.stream(…), carrying request parameters, response metadata, and token usage. See the observability reference for the full set of tags and metrics.HTTP layer。
okhttp.requests観測は、HTTP メソッド、URI、ステータスコード、例外タグを伴って、Anthropic API へのすべての HTTP リクエストをラップします。各リクエストは、traceparentをネットワーク経由で下流のサービス(AI ゲートウェイ、OpenAI 互換推論サーバー、プロキシ)にも伝播します。
spring-boot-starter-actuator とトレーシングブリッジ(micrometer-tracing-bridge-otel または micrometer-tracing-bridge-brave)を備えた Spring Boot アプリケーションでは、両方のレイヤーが自動的にワイヤリングされます。オプトインは不要です。Boot 以外のアプリケーションの場合は、ObservationRegistry を AnthropicChatModel.builder() に渡します。
AnthropicChatModel chatModel = AnthropicChatModel.builder()
.options(...)
.observationRegistry(observationRegistry)
.build();接続プールのメトリクス (opt-in)
Spring AI は、OkHttp 接続プールゲージ(okhttp.pool.connection.count と state=active|idle および client.kind=sync|async)をアプリケーションの MeterRegistry にバインドすることもできます。これらは二次的なテレメトリであり、容量調整には役立ちますが、トレースやリクエストごとのレイテンシには必要ないため、デフォルトでは無効になっています。
Spring Boot アプリケーションで有効にするには:
spring.ai.anthropic.chat.connection-pool-metrics-enabled=trueBoot アプリケーション以外の場合は、MeterRegistry を直接ビルダーに渡してください。レジストリが指定されると、ゲージがバインドされます。
AnthropicChatModel chatModel = AnthropicChatModel.builder()
.options(...)
.observationRegistry(observationRegistry)
.meterRegistry(meterRegistry)
.build();HTTP ディスパッチャー実行者 (advanced)
デフォルトでは、Spring AI は OkHttp ディスパッチャーのエグゼキュータ (SDK の標準動作を再現する無制限のプラットフォームスレッドプール) を管理します。HTTP の同時実行性が高いワークロード、または Java 21+ 仮想スレッドを活用するには、独自の ExecutorService を指定できます。
AnthropicChatModel chatModel = AnthropicChatModel.builder()
.options(...)
.dispatcherExecutor(Executors.newVirtualThreadPerTaskExecutor())
.build(); 同期クライアントと非同期(ストリーミング)クライアントの両方で同じエグゼキュータが使用されます。独自のエグゼキュータを指定すると、そのライフサイクルはユーザーが管理します。Spring AI は、そのエグゼキュータに対して shutdown() を呼び出すことはありません。省略した場合、内部エグゼキュータは自動的に作成およびクリーンアップされます。
ストリーミング HTTP 観測は、チャットモデルの観測には配置されません。 For synchronous calls, the For streaming calls, the If you need to correlate streaming HTTP spans with their parent chat-model span in your tracing UI, filter by span name |
制限
The following features are not yet supported:
Amazon Bedrock backend
Google Vertex AI backend
These features are planned for future releases.
HTTP クライアントのカスタマイズ
Spring AI uses the official anthropic-java SDK under the hood and configures its HTTP transport with a custom OkHttp client built by SpringAiAnthropicHttpClient.Builder. You can intercept that builder before the underlying OkHttpClient is created by exposing one or more AnthropicHttpClientBuilderCustomizer beans.
@FunctionalInterface
public interface AnthropicHttpClientBuilderCustomizer {
void customize(SpringAiAnthropicHttpClient.Builder builder);
}典型的な使用例としては、以下のようなものがあります。
OkHttp および
Interceptorインスタンスの登録(認証、伝播ヘッダー、カスタムログ記録)ディスパッチャー
ExecutorServiceを交換します(たとえば、非同期 I/O を仮想スレッド経由でルーティングするため)。プロキシ、SSL、ホスト名の検証、ビルダーによって公開される接続プールのサイズ設定。
When several customizer beans are present in the application context, they are applied in @Order / @Priority order, after Spring AI’s own defaults, so user code wins.
Each customizer is called twice per AnthropicChatModel — once for the synchronous client and once for the asynchronous client that back the model. Implementations should be idempotent (i.e. safe to call more than once). |
The same hook is available when wiring the model manually via AnthropicChatModel.Builder. In that case the customizers are applied in registration order; @Order annotations have no effect outside of Spring’s bean container:
var chatModel = AnthropicChatModel.builder()
.options(AnthropicChatOptions.builder().model("claude-sonnet-4-20250514").build())
.httpClientBuilderCustomizer(myCustomizer)
.build();