主题
把 Anthropic 模块迁移到官方 Java SDK
在 2.0.0-M3 中,spring-ai-anthropic 被重写于官方 com.anthropic:anthropic-java SDK 之上,取代了手写的 RestClient / WebClient 实现。先前的 AnthropicApi 类是一个 2,300 行的文件,含 47 个嵌套 DTO record。
新模块是 SDK 之上的一层薄适配器,而非并行的 API。Spring AI 的价值在于它自己的抽象——ChatModel、ChatClient、顾问、可观测性、自动配置——以及跨服务商可用的特性。凡 SDK 已涵盖的关切(cache-control 建模、流式、速率限制处理),我们丢弃了先前模块的包装而非沿袭,因此应用直接使用 SDK 的类型。这让接口面保持小巧,并避免随 Anthropic 发布新 SDK 版本而产生漂移。
Maven 坐标、spring-ai-starter-model-anthropic Boot starter 和 spring.ai.anthropic.* 配置属性都未改变。ChatClient API 未变。ChatModel.call(Prompt) 与 ChatModel.stream(Prompt) 保持其签名。AnthropicChatOptions 保留所有既有字段,并为 skills、web search、service tier、inference geo 和 structured output 新增字段。
发生了什么变化
| 领域 | 变更 |
|---|---|
AnthropicChatModel 构造 | 公共构造器移除。使用 AnthropicChatModel.builder()。 |
org.springframework.ai.anthropic.api.AnthropicApi 及其嵌套 DTO | 移除。对于直接 API 访问,使用 SDK 的 com.anthropic.client.AnthropicClient。 |
AnthropicCacheOptions、AnthropicCacheStrategy、AnthropicCacheTtl、CacheBreakpointTracker、CacheEligibilityResolver | 从 org.springframework.ai.anthropic.api(及 api.utils)移出到根包 org.springframework.ai.anthropic。枚举值不变。 |
CitationDocument | 重命名为 AnthropicCitationDocument。 |
AnthropicCacheType、StreamHelper、metadata.AnthropicRateLimit | 移除,无直接替代。 |
默认 maxTokens | 从 500 改为 4096。 |
传递依赖 com.squareup.okhttp3:okhttp | 新增,由 com.anthropic:anthropic-java 引入。 |
若你只用 ChatClient 或 ChatModel
若你的代码看起来像这样,无需迁移:
java
@Autowired ChatClient.Builder builder;
String response = builder.build()
.prompt("Tell me a joke")
.call()
.content();自动配置会产出一个接线到新的、基于 SDK 实现的 AnthropicChatModel bean。调用代码不受影响。
需要留意的一处行为变更是新的默认 maxTokens 值(见 default-max-tokens)。
若你以编程方式构造 AnthropicChatModel
用构建器替换直接的构造器使用:
java
// 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-behavior)。
若你导入了缓存或引用类型
缓存与引用辅助类从 api(及 api.utils)子包移出到根包 org.springframework.ai.anthropic。请如下更新导入:
| 旧导入 | 新导入 |
|---|---|
org.springframework.ai.anthropic.api.AnthropicCacheOptions | org.springframework.ai.anthropic.AnthropicCacheOptions |
org.springframework.ai.anthropic.api.AnthropicCacheStrategy | org.springframework.ai.anthropic.AnthropicCacheStrategy |
org.springframework.ai.anthropic.api.AnthropicCacheTtl | org.springframework.ai.anthropic.AnthropicCacheTtl |
org.springframework.ai.anthropic.api.utils.CacheBreakpointTracker | org.springframework.ai.anthropic.CacheBreakpointTracker |
org.springframework.ai.anthropic.api.utils.CacheEligibilityResolver | org.springframework.ai.anthropic.CacheEligibilityResolver |
org.springframework.ai.anthropic.api.CitationDocument | org.springframework.ai.anthropic.AnthropicCitationDocument |
AnthropicCacheStrategy(NONE、TOOLS_ONLY、SYSTEM_ONLY、SYSTEM_AND_TOOLS、CONVERSATION_HISTORY)和 AnthropicCacheTtl(FIVE_MINUTES、ONE_HOUR)的枚举值不变。plainText(...)、pdf(...) 和 customContent(...) 工厂方法在重命名后的 AnthropicCitationDocument 上仍然存在。
若你直接使用了 AnthropicApi
AnthropicApi、它的嵌套 DTO record 和 AnthropicCacheType 都没了。在动用 SDK 客户端之前,先考虑 AnthropicChatModel 是否涵盖了你在做的事。它通常可以,且你保留了框架集成。
AnthropicChatModel 在原始 AnthropicClient 之上额外提供:
- 服务商中立的请求与响应类型(
Prompt、ChatResponse、Generation、Usage),使应用代码不依赖com.anthropic.*。 - 与
ToolCallback和ToolCallingManager循环集成的工具调用,包括自动多轮执行。 - 以 Reactor
Flux<ChatResponse>而非 SDK 基于回调的AsyncStreamResponse提供的流式。 - 带策略建模、TTL 控制和 4 断点强制的提示词缓存(
AnthropicCacheOptions)。 - 在
ChatResponseMetadata下呈现的、带四种位置变体的Citation。 - 作为
AnthropicChatOptions字段的 Skills、内建 web search、service tier、inference geo 和 structured output,都可从spring.ai.anthropic.chat.*绑定。 - Spring Boot 自动配置与 Micrometer 观测。
- 模型之上的
ChatClient流水线(顾问、消息模板、RAG、对话记忆、结构化输出转换器)。 - 服务商可移植性:同样的
ChatClient代码可对接 OpenAI、Bedrock、Google GenAI 等运行。
对于典型的聊天用法,改用 AnthropicChatModel.builder()(见 programmatic-construction):
java
// 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"));如果你确实需要一个 AnthropicChatModel 未暴露的 Anthropic API 面(一个 beta 端点、files API、自定义 skills CRUD),则下沉到 SDK 客户端。那时你在框架之外——没有可观测性、没有服务商中立、没有 ChatClient 流水线:
java
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);手写的 record 类型在 SDK 的 com.anthropic.models.messages.* 下有直接对应物:
移除的类型(旧 AnthropicApi.*) | SDK 替代(com.anthropic.models.messages.*) |
|---|---|
ChatCompletionRequest | MessageCreateParams |
ChatCompletionResponse | Message |
AnthropicMessage | MessageParam |
ContentBlock | ContentBlock(sealed union:TextBlock、ToolUseBlock、ThinkingBlock、RedactedThinkingBlock、ServerToolUseBlock、WebSearchToolResultBlock、ContainerUploadBlock,……) |
Tool | Tool |
ToolChoiceAuto / ToolChoiceAny / ToolChoiceTool / ToolChoiceNone | ToolChoice(带 ToolChoiceAuto、ToolChoiceAny、ToolChoiceTool、ToolChoiceNone 变体的 sealed union) |
Source(图像 / PDF 媒体) | Base64ImageSource、UrlImageSource、Base64PdfSource、UrlPdfSource |
MessageStartEvent、ContentBlockStartEvent、ContentBlockDeltaEvent、MessageDeltaEvent,…… | RawMessageStreamEvent(sealed union) |
NOTE
ToolUseBlock.input() 返回 SDK 的 JsonValue,而非一个 JSON 字符串。对 JsonValue 调用 .toString() 会产生 Java 的 map 语法({key=value}),看着像 JSON 但其实不是。用 JsonValue.Visitor<T> 遍历它,或通过 Jackson 序列化以得到真正的 JSON 字符串。
转而使用 SDK 等价物而移除
这些类型被丢弃,因为 SDK 已暴露同样的概念。直接使用 SDK 类型。
| 移除 | 改用 |
|---|---|
org.springframework.ai.anthropic.api.AnthropicCacheType | com.anthropic.models.messages.CacheControlEphemeral。旧的枚举只携带字面值 ephemeral,而这也是 API 唯一支持的 cache-control 类型。 |
org.springframework.ai.anthropic.api.StreamHelper | com.anthropic.core.http.AsyncStreamResponse<RawMessageStreamEvent>。StreamHelper 是旧 WebClient 流式路径的内部 SSE-合并辅助;SDK 现在原生交付流事件,Spring AI 在内部把它们桥接到一个 Reactor Flux。调用 ChatModel.stream(Prompt) 的代码不受影响。 |
org.springframework.ai.anthropic.metadata.AnthropicRateLimit | com.anthropic.errors.RateLimitException(在重试耗尽后由 SDK 抛出),加上 SDK 响应对象上的头部。把原先从 ChatResponseMetadata 读取速率限制元数据的代码替换为异常处理。 |
行为变更
提示词级选项不再与模型默认值合并
先前模块会通过 ModelOptionsUtils.copyToTarget(...) 和 ModelOptionsUtils.merge(...) 把提示词级 AnthropicChatOptions 合并进模型级默认,于是 model、temperature 及任何未设置的字段都会从模型默认补齐:
java
// 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);新模块不做合并。提示词级的选项实例原样使用;若提示词没有选项,则使用模型默认。
java
// 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);这是对 升级到 2.0.0-M5 — ChatOptions 处理 中更广泛变更的一次前向移植,为 Anthropic 提前落地。ChatClient 在到达模型之前会做自己的合并,因此使用 ChatClient 的调用者不受影响。
默认 maxTokens 现为 4096
AnthropicChatOptions 把 maxTokens 默认设为 4096 而非 500。先前的默认时常会截断响应;新值与其他 Spring AI 聊天模块一致。
若你依赖 500 来约束成本,请显式设置它:
properties
spring.ai.anthropic.chat.max-tokens=500重试使用 SDK maxRetries,而非 RetryTemplate
先前模块在其构造器中接受一个 Spring Retry 的 RetryTemplate。重试现在由 SDK 处理,并通过 maxRetries(默认 2)配置:
properties
spring.ai.anthropic.max-retries=5任何专为 Anthropic 模块接线的 RetryTemplate bean 都可移除。若要更细的控制,通过一个自定义的 AnthropicSetup 配置 SDK 客户端。
流式思考事件
有两处变更影响任何订阅了原始 Flux<ChatResponse> 流(而非让 ChatClient 或 MessageAggregator 折叠它)的人。
先前模块把思考块的文本与签名捆绑进单个 Generation。SDK 把它们作为独立事件交付,新模块照此转发:一个带 properties.signature 的 Generation 会在思考文本增量之后、任何后续文本增量之前到达。MessageAggregator 和 ChatClient 的内建聚合会透明地吸收这个额外的块。
思考文本增量现在还包含 properties.thinking = Boolean.TRUE。先前模块把它们作为纯内容发出,使调用者在流中途没有办法可靠地区分思考文本与响应文本。
完整的流式元数据键集合:
| 块 | Generation 携带 |
|---|---|
| 思考文本增量 | content = <thinking text>,properties.thinking = true(M3 新增) |
| 思考签名增量 | 空的 content,properties.signature = <signature> |
| 涂黑的思考块 | 空的 content,properties.data = <data> |
| 同步思考块(非流式) | content = <thinking text>,properties.signature = <signature>(单个 Generation) |
提示词缓存变更
若你配置了 AnthropicCacheOptions,有三项行为需要知道:
- 4 断点限制在 Spring AI 内强制,而非在 API。 Anthropic 每请求至多允许 4 个缓存断点。先前模块会把标记透传并让 API 拒绝超额。
CacheBreakpointTracker现在会计数并静默跳过第 4 个之后的新增,记录一次性的WARN。最可能触顶的方式是把SYSTEM_AND_TOOLS与多块系统缓存和引用文档结合。那些此前以 API 错误失败的请求现在可能以减少的缓存成功,因此升级后请检查你的缓存命中率。 AnthropicCacheStrategy.NONE意为“未设置”,而非“禁用”。 在提示词层级设置NONE会回退到模型默认。若模型默认开启了缓存而你想关闭它,请以AnthropicCacheOptions.disabled()作为默认构建第二个AnthropicChatModel。- 工具的缓存 TTL 取自
MessageType.SYSTEM。resolveToolCacheControl无论策略如何都查messageTypeTtl(MessageType.SYSTEM)。设置messageTypeTtl(MessageType.USER, ONE_HOUR)对工具缓存没有影响。
引用文档一致性在客户端校验
Anthropic API 要求单个请求中每个带引用配置的 DocumentBlockParam 都共享相同的 citations.enabled 值。AnthropicChatOptions.validateCitationConsistency() 现在强制这一点,并在请求发出前抛出 IllegalArgumentException。先前模块让 API 返回一个 HTTP 400。混用启用与禁用引用文档的测试或调用点,现在会在构建期失败而非在网络调用时。
新的 OkHttp 传递依赖
com.anthropic:anthropic-java 会引入 com.squareup.okhttp3:okhttp。多数应用不会察觉;若你有严格的依赖收敛规则或一个已有的 OkHttp 版本钉,你可能需要一个 <dependencyManagement> 条目。
新能力
此次迁移还启用了若干 Anthropic 特性。完整参考见 Anthropic 聊天。
- 原生 skills(
AnthropicSkill、AnthropicSkillContainer)。 - 内建 web search 工具(
AnthropicWebSearchTool)。 - 服务层选择(
AnthropicServiceTier)。 - 用于数据驻留的 inference geo(
us、eu)。 - 通过
JsonOutputFormat和Effort的原生结构化输出(需要claude-sonnet-4-6或更新)。 - 扩展思考的显示模式(summarized / omitted)。
- 带四种位置变体的
Citation类型(CHAR_LOCATION、PAGE_LOCATION、CONTENT_BLOCK_LOCATION、WEB_SEARCH_RESULT_LOCATION)。 AnthropicChatOptions#httpHeaders上的逐请求 HTTP 头,不同于AbstractAnthropicOptions上客户端级的customHeaders。customHeaders在客户端上设置一次并适用于每个请求;httpHeaders逐Prompt设置并在请求构建时合并。对请求跟踪、beta-API 开关和路由很有用。
会静默失败之处
编译错误很容易发现。而下面这些不抛异常,而是产出不同的输出。
- 部分提示词级选项。
new Prompt(text, AnthropicChatOptions.builder().maxTokens(2048).build())不再从模型默认继承model、temperature等(见 prompt-option-merging)。没有编译错误、没有异常;请求会以不同的值运行。若升级后输出漂移且你直接调用ChatModel,先到此处排查。 - 若升级后成本攀升,检查你是否依赖旧的
500令牌maxTokens默认来约束响应。 - 第 4 个之后的缓存断点被丢弃。 把多块系统缓存与工具和引用文档叠加,可能超过 Anthropic 的 4 断点上限。
CacheBreakpointTracker会跳过多余的并一次性记录WARN。缓存命中率下降但没有错误。 - 陈旧的
org.springframework.ai.anthropic.api.AnthropicCache*导入是升级后多数编译错误的根源。一次全项目的查找替换即可修复。