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 の接続とチャットオプションを設定します。

プロパティ 説明 デフォルト

spring.ai.anthropic.api-key

Anthropic API キー

-

spring.ai.anthropic.base-url

API ベース URL

api.anthropic.com (英語)

spring.ai.anthropic.timeout

Anthropic クライアントのリクエストがタイムアウトしました

60s

spring.ai.anthropic.max-retries

失敗したリクエストに対する最大再試行回数

2

spring.ai.anthropic.custom-headers.*

すべての Anthropic クライアントリクエストに追加するカスタム HTTP ヘッダー

-

spring.ai.anthropic.chat.model

モデル名

claude-haiku-4-5

spring.ai.anthropic.chat.max-tokens

最大トークン数

4096

spring.ai.anthropic.chat.temperature

サンプリング温度

-

spring.ai.anthropic.chat.top-p

トップ p サンプリング

-

spring.ai.anthropic.chat.top-k

トップ k サンプリング

-

spring.ai.anthropic.chat.cache-options.strategy

使用するキャッシュ戦略

NONE

spring.ai.anthropic.chat.cache-options.multi-block-system-caching

システムメッセージごとに個別のブロックを使用する

false

spring.ai.anthropic.chat.http-headers

個々の API 呼び出しに渡すリクエストごとの HTTP ヘッダー。

-

spring.ai.anthropic.chat.inference-geo

リクエストが処理される地理的領域 (us または eu)

-

spring.ai.anthropic.chat.web-search-tool.max-uses

Maximum number of web searches per request

-

spring.ai.anthropic.chat.web-search-tool.allowed-domains

Comma-separated list of domains to restrict search results to

-

spring.ai.anthropic.chat.web-search-tool.blocked-domains

Comma-separated list of domains to exclude from search results

-

spring.ai.anthropic.chat.web-search-tool.user-location.city

City for localizing search results

-

spring.ai.anthropic.chat.web-search-tool.user-location.country

ISO 3166-1 alpha-2 country code

-

spring.ai.anthropic.chat.web-search-tool.user-location.region

Region or state

-

spring.ai.anthropic.chat.web-search-tool.user-location.timezone

IANA timezone identifier

-

spring.ai.anthropic.chat.service-tier

Capacity routing: auto (use priority if available) or standard_only (always standard). See Service Tiers (英語) .

-

手動構成

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();

基本的な使い方

ChatResponse response = chatModel.call(
    new Prompt("Generate the names of 5 famous pirates."));

// Or with streaming responses
Flux<ChatResponse> stream = chatModel.stream(
    new Prompt("Generate the names of 5 famous pirates."));

ランタイムオプション

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: claude-sonnet-4-20250514claude-opus-4-20250514claude-3-5-sonnet-20241022claude-3-5-haiku-20241022, etc. See Claude モデル (英語)

claude-sonnet-4-20250514

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. claude-opus-4-7); values < 0.99 are rejected with HTTP 400, and values >= 0.99 are accepted as a backward-compatibility no-op (no actual filtering applied)

-

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. claude-opus-4-7); any value is rejected with HTTP 400 on those models

-

stopSequences

Custom sequences that will cause the model to stop generating.

-

apiKey

The API key for authentication. Auto-detects from ANTHROPIC_API_KEY environment variable if not set.

-

baseUrl

Anthropic API のベース URL。

api.anthropic.com (英語)

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 MessageCreateParams.putAdditionalHeader(). Useful for request-level tracking, beta API headers, or routing.

-

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 thinkingEnabled(budgetTokens)thinkingEnabled(budgetTokens, display)thinkingAdaptive()thinkingAdaptive(display), or thinkingDisabled(), or pass a raw ThinkingConfigParam. The display parameter controls how thinking content appears in the response: SUMMARIZED (condensed summary) or OMITTED (redacted, signature only).

-

outputConfig

Output configuration for structured output (JSON schema) and effort control. Use outputConfig(OutputConfig) for full control, or the convenience methods outputSchema(String) and effort(OutputConfig.Effort). Requires claude-sonnet-4-6 or newer.

-

inferenceGeo

Controls the geographic region where the request is processed. Supported values: useu. Used for data residency compliance. Configurable via spring.ai.anthropic.chat.inference-geo.

-

serviceTier

Controls capacity routing for the request. Use MessageCreateParams.ServiceTier.AUTO to opportunistically use priority capacity, or STANDARD_ONLY to always use standard capacity. See Service Tiers (英語) .

-

Tool Calling Options

オプション 説明 デフォルト

toolChoice

Controls which tool (if any) is called by the model. Use ToolChoiceAutoToolChoiceAnyToolChoiceTool, or ToolChoiceNone.

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 resets

Anthropic 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 DefaultToolCallingManager directly when you need full control over the loop (for example, when combining tool calling with prompt caching).

同期ツール実行とストリーミングツール実行の両方で、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:

  • ToolChoice.auto() can be used instead of ToolChoice.ofAuto(…​).

  • ToolChoice.any() can be used instead of ToolChoice.ofAny(…​).

  • ToolChoice.none() can be used instead of ToolChoice.ofNone(…​).

ストリーミング

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 モデルでサポートされています。

  • Claude 4 モデル (claude-opus-4-20250514claude-sonnet-4-20250514)

  • Claude 3.7 ソネット (claude-3-7-sonnet-20250219)

モデルの機能:

  • Claude 3.7 ソネット : Returns full thinking output.

  • Claude 4 モデル : Support summarized thinking and enhanced tool integration.

API リクエスト構造はサポートされているすべてのモデルで同じですが、出力動作は異なります。

思考構成

To enable thinking, configure the following:

  1. Set a thinking budget : The budgetTokens must be >= 1024 and less than maxTokens.

  2. 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

signature

Claude’s reasoning text with a cryptographic signature. In sync mode, the thinking text is in getText() and the signature is in getMetadata().get("signature").

Redacted Thinking

data

Safety-redacted reasoning. Contains only a data marker, no visible text.

シグネチャー (streaming)

signature

In streaming mode, the signature arrives as a separate delta at the end of a thinking block.

Thinking Delta (streaming)

thinking

Incremental thinking text chunks during streaming. The thinking metadata key is set to true.

テキストブロック

(なし)

The final answer text in getText().

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)));

Multiple Media Items

You can include multiple images or documents in a single message:

var userMessage = UserMessage.builder()
    .text("Compare these two images.")
    .media(List.of(
        new Media(MimeTypeUtils.IMAGE_PNG, image1Resource),
        new Media(MimeTypeUtils.IMAGE_PNG, image2Resource)))
    .build();

引用

Anthropic の引用 API (英語) により、Claude はレスポンスを生成する際に、提供されたドキュメントの特定の部分を参照できます。プロンプトに引用ドキュメントが含まれている場合、Claude はソース資料を引用することができ、引用メタデータ(文字範囲、ページ番号、コンテンツブロック)がレスポンスメタデータに返されます。

引用は次のような改善に役立ちます:

  • 精度検証 : ユーザーは Claude の回答をソース資料と照らし合わせて検証できる

  • 透過性 : 回答の根拠となったドキュメントの正確な部分を確認する

  • 準拠 : 規制対象産業における情報源帰属の要件を満たす

  • 信頼 : 情報の出所を示すことで信頼を築く

対応モデル

引用は、Claude 3.7 Sonnet および Claude 4 モデル (Opus および Sonnet) でサポートされています。

ドキュメントタイプ

3 種類の引用ドキュメントがサポートされています。

  • プレーンテキスト : 文字レベルの引用を含むテキストコンテンツ

  • PDF : ページレベルの引用を含む PDF ドキュメント

  • カスタムコンテンツ : ブロックレベルの引用を含むユーザー定義のコンテンツブロック

引用ドキュメントの作成

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.");
}

ベストプラクティス

  1. 説明的なタイトルを使用する : ユーザーが引用内のソースを識別できるように、引用ドキュメントに意味のあるタイトルを付けます。

  2. null 引用をチェックする : すべてのレスポンスに引用が含まれるわけではないため、アクセスする前に引用のメタデータが存在することを必ず検証してください。

  3. ドキュメントサイズを考慮する : ドキュメントが大きいほどコンテキストは多くなりますが、消費される入力トークンも多くなり、レスポンス時間に影響する可能性があります。

  4. 複数のドキュメントを活用する : 複数のソースにまたがる質問に答える場合は、複数回呼び出すのではなく、1 回のリクエストですべての関連ドキュメントを提供してください。

  5. 適切なドキュメント型を使用する : シンプルなコンテンツにはプレーンテキスト、既存のドキュメントには 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:

戦略 説明

NONE

No caching (default). No cache control headers are added.

SYSTEM_ONLY

Cache system message content. Uses 1 cache breakpoint.

TOOLS_ONLY

Cache tool definitions only. Uses 1 cache breakpoint.

SYSTEM_AND_TOOLS

Cache both system messages and tool definitions. Uses 2 cache breakpoints.

CONVERSATION_HISTORY

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();
オプション 説明 デフォルト

strategy

The caching strategy to use.

NONE

messageTypeTtl

TTL per message type. Available values: FIVE_MINUTESONE_HOUR.

FIVE_MINUTES for all types

messageTypeMinContentLength

Minimum content length required before caching a message type.

1

contentLengthFunction

Custom function to compute content length (e.g., token counting).

String::length

multiBlockSystemCaching

When true, each system message becomes a separate cacheable block; cache control is applied to the second-to-last block (static prefix pattern). When false, all system messages are joined into one block.

false

cacheToolResults

When true, a cache breakpoint is placed on the last tool result message of a request so tool outputs are cached across tool-calling rounds. Takes effect with CONVERSATION_HISTORY.

false

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=true

Accessing 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 claude-sonnet-4-6 or newer. Older models like claude-sonnet-4-20250514 do not support these features.

Schema Requirements

When using JSON schema output, Anthropic requires "additionalProperties": false for all object types in the schema.

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 schema

Effort Control

Control how much compute Claude spends on its response. Lower effort means faster, cheaper responses; higher effort means more thorough reasoning.

Effort Level 説明

LOW

Fast and concise responses with minimal reasoning

MEDIUM

Balanced trade-off between speed and thoroughness

HIGH

More thorough reasoning and detailed responses

MAX

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));

StructuredOutputChatOptions インターフェース

AnthropicChatOptions implements the StructuredOutputChatOptions interface, which provides portable getOutputSchema() method. This allows structured output to work with Spring AI’s generic structured output infrastructure.

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=auto

Or 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 以降のモデルでサポートされています。

要件

  • スキルにはコード実行機能が必要 (automatically enabled by Spring AI when skills are configured)

  • リクエストごとに最大 8 つのスキル

  • 生成されたファイルは、ファイル API 経由で 24 時間ダウンロードできます。

プリビルド Anthropic スキル

Spring AI は、AnthropicSkill 列挙型を通じて Anthropic の事前構築されたスキルへの型安全なアクセスを提供します。

スキル 説明 生成されたファイルの種類

XLSX

Excel スプレッドシートの生成と操作

.xlsx (Microsoft Excel)

PPTX

PowerPoint プレゼンテーション作成

.pptx (Microsoft PowerPoint)

DOCX

Word ドキュメントの生成

.docx (Microsoft Word)

PDF

PDF ドキュメントの作成

.pdf (Portable Document Format)

基本的な使い方

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)");
}

コンテナー ID の抽出

For multi-turn conversations with Skills, you can extract the container ID for reuse:

String containerId = AnthropicSkillsResponseHelper.extractContainerId(response);

if (containerId != null) {
    System.out.println("Container ID for reuse: " + containerId);
}

完全な例

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);
    }
}

ベストプラクティス

  1. 適切なモデルを使用する : スキルは Claude Sonnet 4 以降のモデルで最も効果的に機能します。サポートされているモデルを使用していることをご確認ください。

  2. 十分な最大トークンを設定する : ドキュメント生成には大量のトークンが必要になる場合があります。複雑なドキュメントの場合は、maxTokens(4096) 以上のトークンを使用してください。

  3. プロンプトは具体的に: ドキュメントの構造、コンテンツ、書式設定に関する明確で詳細な指示を提供します。

  4. ファイルのダウンロードを迅速に処理する : 生成されたファイルは 24 時間後に期限切れになります。生成後すぐにファイルをダウンロードしてください。

  5. ファイル ID を確認する : ダウンロードを試みる前に、必ずファイル ID が返されていることを確認してください。一部のプロンプトでは、ファイルが生成されずにテキストレスポンスが表示される場合があります。

  6. 守備的なエラー処理を使用する : ネットワークの課題や期限切れのファイルを適切に処理するには、ファイル操作を 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());
}

Anthropic’s Web 検索 (英語) tool allows Claude to search the web during a conversation and use the results to generate cited responses.

  • Web search is a built-in server-side tool — no external tool callbacks are needed

  • Web search results automatically include citations with URLs

  • You can combine web search with other tools (function calling, code execution, skills) in the same request

基本的な使い方

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.operation observation wraps every call to AnthropicChatModel.call(…​) or .stream(…​), carrying request parameters, response metadata, and token usage. See the observability reference for the full set of tags and metrics.

  • HTTP layerokhttp.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=true

Boot アプリケーション以外の場合は、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 okhttp.requests span is correctly nested under the gen_ai.client.operation span — the full trace tree forms as expected.

For streaming calls, the okhttp.requests span fires and is recorded in the registry, the timer is recorded in MeterRegistry, and traceparent is still propagated on the outbound request — but the span is not nested under the chat-model span. This is because the Anthropic SDK’s async streaming implementation hops onto ForkJoinPool.commonPool() before invoking Spring AI’s HTTP client, dropping the calling thread’s observation context at that boundary. Spring AI cannot recover the context from within its HTTP-client wrapper.

If you need to correlate streaming HTTP spans with their parent chat-model span in your tracing UI, filter by span name okhttp.requests with host api.anthropic.com and join to the corresponding gen_ai.client.operation span by trace ID or timestamp range. Built-in parent linkage for streaming is being pursued upstream in the Anthropic Java SDK.

ログ

Enable SDK logging by setting the environment variable:

export ANTHROPIC_LOG=debug

制限

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();