Anthropic モジュールを公式 Java SDK に移行する
2.0.0-M3 では、spring-ai-anthropic が公式の com.anthropic:anthropic-java SDK をベースに書き直され、手作業で実装されていた RestClient / WebClient が置き換えられました。以前の AnthropicApi クラスは、47 個のネストされた DTO レコードを含む 2,300 行のファイルでした。
新しいモジュールは、並列 API ではなく、SDK 上の薄いアダプターです。Spring AI の価値は、ChatModel、ChatClient、アドバイザー、可観測性、自動構成といった独自の抽象化と、プロバイダ間で連携する機能にあります。SDK がすでにカバーしている機能(キャッシュ制御モデリング、ストリーミング、レート制限処理など)については、以前のモジュールのラッパーは引き継がれず削除されたため、アプリケーションは SDK の型を直接使用します。これにより、Anthropic が新しい SDK リリースを提供しても、対象領域が小さく保たれ、互換性の喪失を防ぐことができます。
Maven の座標、spring-ai-starter-model-anthropic の Boot スターター、spring.ai.anthropic.* の設定プロパティはすべて変更されていません。ChatClient の API も変更されていません。ChatModel.call(Prompt) と ChatModel.stream(Prompt) はシグネチャーを維持します。AnthropicChatOptions は既存のフィールドをすべて維持し、スキル、Web 検索、サービスティア、推論ジオ、構造化出力用の新しいフィールドを追加します。
何が変わったのか
| エリア | 変更 |
|---|---|
|
Public constructors removed. Use |
|
Removed. For direct API access, use the SDK’s |
|
|
|
|
| 後継機種なしで撤去されました。 |
デフォルトの |
|
推移的 |
|
ChatClient または ChatModel のみを使用する場合
コードが以下のようになっている場合は、移行は不要です。
@Autowired ChatClient.Builder builder;
String response = builder.build()
.prompt("Tell me a joke")
.call()
.content(); 自動構成により、新しい SDK ベースの実装に接続された AnthropicChatModel Bean が生成されます。呼び出し元のコードには影響はありません。
注意すべき動作変更点は、新しいデフォルト値である maxTokens の値です(Default maxTokens is now 4096 を参照)。
AnthropicChatModel をプログラムで構築する場合
コンストラクターを直接使用する代わりに、ビルダーを使用してください。
// Before
AnthropicApi anthropicApi = new AnthropicApi(apiKey);
AnthropicChatModel chatModel = new AnthropicChatModel(anthropicApi,
AnthropicChatOptions.builder().model("claude-haiku-4-5").maxTokens(2048).build(),
retryTemplate,
toolCallingManager);
// After
AnthropicChatModel chatModel = AnthropicChatModel.builder()
.apiKey(apiKey)
.defaultOptions(AnthropicChatOptions.builder()
.model("claude-haiku-4-5")
.maxTokens(2048)
.build())
.toolCallingManager(toolCallingManager)
.build(); ビルダーは baseUrl、timeout、maxRetries、proxy、customHeaders、observationRegistry、observationConvention も受け入れます。retryTemplate ビルダーメソッドはありません。再試行は SDK によって処理されるようになりました (Retry uses SDK maxRetries, not RetryTemplate を参照)。
キャッシュまたは引用型をインポートした場合
キャッシュおよび引用ヘルパークラスは、api (および api.utils)サブパッケージからルートパッケージである org.springframework.ai.anthropic に移動されました。インポートを以下のように更新してください。
| 古いインポート品 | 新規インポート |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
AnthropicCacheStrategy (NONE、TOOLS_ONLY、SYSTEM_ONLY、SYSTEM_AND_TOOLS、CONVERSATION_HISTORY) および AnthropicCacheTtl (FIVE_MINUTES、ONE_HOUR) の列挙値は変更されていません。plainText(…)、pdf(…)、customContent(…) ファクトリメソッドは、名前が変更された AnthropicCitationDocument にも引き続き存在します。
AnthropicApi を直接使用した場合
AnthropicApi、そのネストされた DTO レコード、AnthropicCacheType は削除されました。SDK クライアントを使用する前に、AnthropicChatModel がこれまで行っていた作業をカバーしているかどうかを検討してください。通常はカバーしており、フレームワークとの統合を維持できます。
AnthropicChatModel は、生の AnthropicClient に以下を追加します。
プロバイダに依存しないリクエストおよびレスポンス型(
Prompt、ChatResponse、Generation、Usage)により、アプリケーションコードはcom.anthropic.*に依存しません。ツール呼び出しは
ToolCallbackおよびToolCallingManagerループと統合されており、自動的な複数ターン実行も含まれています。SDK のコールバックベースの
AsyncStreamResponseではなく、ReactorFlux<ChatResponse>としてストリーミングします。戦略モデリング、TTL 制御、4 ブレークポイント強制による迅速なキャッシング(
AnthropicCacheOptions)。Citationには、ChatResponseMetadataで 4 つの位置変異が発見された。スキル、組み込みの Web 検索、サービス階層、推論地理、構造化出力は、
AnthropicChatOptionsフィールドとして、すべてspring.ai.anthropic.chat.*からバインド可能です。Spring Boot の自動構成と Micrometer の監視。
モデルより上位の
ChatClientパイプライン(アドバイザー、メッセージテンプレート、RAG、会話メモリ、構造化出力コンバーター)。プロバイダーのポータビリティ: 同じ
ChatClientコードは、OpenAI、Bedrock、Google GenAI、およびその他の GenAI に対して実行されます。
For typical chat usage, switch to AnthropicChatModel.builder() (see If You Construct AnthropicChatModel Programmatically ):
// Before
AnthropicApi api = new AnthropicApi(apiKey);
AnthropicApi.ChatCompletionRequest req = new AnthropicApi.ChatCompletionRequest(
AnthropicApi.ChatModel.CLAUDE_HAIKU_4_5.getValue(),
List.of(new AnthropicApi.AnthropicMessage(List.of(new AnthropicApi.ContentBlock("hello")), AnthropicApi.Role.USER)),
null, 1024, null, 0.7, null, null, null, null, false);
ResponseEntity<AnthropicApi.ChatCompletionResponse> resp = api.chatCompletionEntity(req);
// After
AnthropicChatModel chatModel = AnthropicChatModel.builder()
.apiKey(apiKey)
.defaultOptions(AnthropicChatOptions.builder()
.model("claude-haiku-4-5")
.maxTokens(1024)
.temperature(0.7)
.build())
.build();
ChatResponse response = chatModel.call(new Prompt("hello"));If you genuinely need an Anthropic API surface that AnthropicChatModel doesn’t expose (a beta endpoint, files API, custom skills CRUD), drop to the SDK client. You’re outside the framework at that point — no observability, no provider neutrality, no ChatClient pipeline:
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import com.anthropic.models.messages.MessageCreateParams;
import com.anthropic.models.messages.Model;
AnthropicClient client = AnthropicOkHttpClient.builder().apiKey(apiKey).build();
MessageCreateParams params = MessageCreateParams.builder()
.model(Model.CLAUDE_HAIKU_4_5)
.maxTokens(1024)
.temperature(0.7)
.addUserMessage("hello")
.build();
com.anthropic.models.messages.Message message = client.messages().create(params);The hand-rolled record types have direct analogues in the SDK under com.anthropic.models.messages.*:
Removed type (old AnthropicApi.*) | SDK replacement (com.anthropic.models.messages.*) |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ToolUseBlock.input() returns the SDK’s JsonValue, not a JSON string. Calling .toString() on a JsonValue produces Java map syntax ({key=value}) that looks like JSON but is not. Walk it with JsonValue.Visitor<T> or serialize via Jackson to get a real JSON string.
|
Removed in Favor of SDK Equivalents
These types were dropped because the SDK already exposes the same concept. Use the SDK type directly.
| Removed | Use instead |
|---|---|
|
|
|
|
|
|
Behavior Changes
Prompt-level options no longer merge with model defaults
The previous module merged prompt-level AnthropicChatOptions into the model-level defaults via ModelOptionsUtils.copyToTarget(…) and ModelOptionsUtils.merge(…), so model, temperature, and any other unset fields were filled in from the model’s defaults:
// Before: only maxTokens set; model + temperature inherited from defaults.
Prompt prompt = new Prompt(
"Tell me a joke",
AnthropicChatOptions.builder().maxTokens(2048).build());
chatModel.call(prompt);
The new module does not merge. A prompt-level options instance is used as-is; if the prompt has no options, the model defaults are used.
// After: prompt-level options must be "full", or null.
Prompt prompt = new Prompt(
"Tell me a joke",
chatModel.getOptions().mutate().maxTokens(2048).build());
chatModel.call(prompt);
This is a forward port of the broader change in Upgrading to 2.0.0-M5 — ChatOptions Handling, landed early for Anthropic.
ChatClient performs its own merging before reaching the model, so callers using ChatClient are not affected.
Default maxTokens is now 4096
AnthropicChatOptions defaults maxTokens to 4096 instead of 500.
The previous default routinely truncated responses; the new value matches the other Spring AI chat modules.
If you relied on 500 to bound costs, set it explicitly:
spring.ai.anthropic.chat.max-tokens=500
Retry uses SDK maxRetries, not RetryTemplate
The previous module took a Spring Retry RetryTemplate in its constructor.
Retries are now handled by the SDK and configured through maxRetries (default 2):
spring.ai.anthropic.max-retries=5
Any RetryTemplate bean wired specifically for the Anthropic module can be removed.
For finer control, configure the SDK client through a custom AnthropicSetup.
Streaming thinking events
Two changes affect anyone subscribed to the raw Flux<ChatResponse> stream (rather than letting ChatClient or MessageAggregator collapse it).
The previous module bundled the text and signature of a thinking block into a single Generation. The SDK delivers them as separate events, and the new module forwards them that way: a Generation with properties.signature arrives after the thinking-text deltas, before any subsequent text deltas. MessageAggregator and `ChatClient’s built-in aggregation absorb the extra chunk transparently.
Thinking-text deltas now also include properties.thinking = Boolean.TRUE. The previous module emitted them as plain content, leaving callers no reliable way to distinguish thinking text from response text mid-stream.
The full set of streaming metadata keys:
| Block | Generation carries |
|---|---|
Thinking text delta |
|
Thinking signature delta |
empty |
Redacted thinking block | empty |
Sync thinking block (non-streaming) |
|
Prompt caching changes
Three behaviors to know about if you configured AnthropicCacheOptions:
The 4-breakpoint limit is enforced in Spring AI, not at the API。Anthropic permits at most 4 cache breakpoints per request. The previous module passed markers through and let the API reject excess.
CacheBreakpointTrackernow keeps count and silently skips additions past 4, logging a one-timeWARN. The most likely way to hit the cap isSYSTEM_AND_TOOLScombined with multi-block system caching and citation documents. Requests that previously failed with an API error can now succeed with reduced caching, so check your cache hit rate after the upgrade.AnthropicCacheStrategy.NONEmeans "unset", not "disabled"。SettingNONEat the prompt level falls back to the model default. To turn caching off when the model default has it on, build a secondAnthropicChatModelwithAnthropicCacheOptions.disabled()as its default.Tool cache TTL is taken from
MessageType.SYSTEM。resolveToolCacheControllooks upmessageTypeTtl(MessageType.SYSTEM)regardless of strategy. SettingmessageTypeTtl(MessageType.USER, ONE_HOUR)has no effect on tool caching.
Citation document consistency is validated client-side
The Anthropic API requires every DocumentBlockParam with citation configuration in a single request to share the same citations.enabled value. AnthropicChatOptions.validateCitationConsistency() now enforces this and throws IllegalArgumentException before the request is sent. The previous module let the API return an HTTP 400. Tests or call sites that mixed enabled and disabled citation documents will now fail at build time instead of on the network call.
New OkHttp Transitive Dependency
com.anthropic:anthropic-java pulls in com.squareup.okhttp3:okhttp. Most applications will not notice; if you have strict dependency-convergence rules or an existing OkHttp pin, you may need a <dependencyManagement> entry.
New Capabilities
The migration also enables several Anthropic features. See Anthropic チャット for the full reference.
Native skills (
AnthropicSkill,AnthropicSkillContainer).Built-in web search tool (
AnthropicWebSearchTool).Service tier selection (
AnthropicServiceTier).Inference geo for data residency (
us,eu).Native structured output through
JsonOutputFormatandEffort(requiresclaude-sonnet-4-6or newer).Extended-thinking display modes (summarized / omitted).
Citationtype with four location variants (CHAR_LOCATION、PAGE_LOCATION、CONTENT_BLOCK_LOCATION、WEB_SEARCH_RESULT_LOCATION).Per-request HTTP headers on
AnthropicChatOptions#httpHeaders, distinct from client-levelcustomHeadersonAbstractAnthropicOptions.customHeadersis set once on the client and applies to every request;httpHeadersis set perPromptand merged in at request-build time. Useful for request tracing, beta-API toggles, and routing.
Things That Fail Silently
The compile errors are easy. These don’t throw; they produce different output instead.
Partial prompt-level options。
new Prompt(text, AnthropicChatOptions.builder().maxTokens(2048).build())no longer inheritsmodel,temperature, etc. from the model’s defaults (see Prompt-level options no longer merge with model defaults ). No compile error, no exception; the request runs with different values. If output drifts after the upgrade and you callChatModeldirectly, look here first.If costs climb after the upgrade, check whether you were relying on the old
500-tokenmaxTokensdefault to cap responses.Cache breakpoints dropped past four。Stacking multi-block system caching with tools and citation documents can push past Anthropic’s 4-breakpoint cap.
CacheBreakpointTrackerskips the extras and logsWARNonce. Cache hit rate drops without an error.Stale
org.springframework.ai.anthropic.api.AnthropicCache*imports are the source of most compile errors after the upgrade. A project-wide find-and-replace fixes them.