6 月 12 日,Spring AI 2.0.0 GA 正式发布。距离 1.0 不过一年,但这不是小版本迭代——是一次架构级的重写。
一句话概括:1.x 是“能用”,2.0 是“能用来造 Agent”。
先看清底座:Boot 4 + Framework 7
Spring AI 2.0 硬依赖 Spring Boot 4.0/4.1 和 Spring Framework 7.0。没有兼容模式,Boot 3.x 直接加载不了。
Jackson 也从 2 升到了 3,整个代码库引入了 JSpecify 空安全注解。对 AI 应用来说这不是小事——模型返回空、工具结果为空、配置缺失,AI 应用的边界条件比普通业务系统多得多。
结论:新项目直接上,老项目先评估 Boot 4 迁移成本。
核心变化:工具调用从“内嵌”变成“一等公民”
这是 2.0 最根本的架构决策。
1.x 时代,工具调用循环埋在每一个 ChatModel 实现内部。OpenAI 有一套,Anthropic 有一套,行为不一致,你也没法在循环上做任何事——不能拦截、不能观测、不能替换执行策略。
2.0 把这个循环提升到了 ChatClient 的 Advisor 链上,作为 ToolCallingAdvisor 自动注册,完整接管工具执行生命周期。
这意味着什么?
Advisor 链是一条洋葱模型管线。ToolCallingAdvisor 是递归 Advisor,每次工具调用循环都会重新进入下游链。所以你可以:
- 拦截:在工具执行前做权限校验、租户隔离
- 观测:记录每次工具调用的耗时和参数
- 替换:继承 ToolCallingAdvisor,重写 hook 方法
官方测试数据显示,新架构在 OpenAI、Anthropic 和 Gemini 上实现了34%–64% 的 Token 消耗降低。
工具定义本身也简化了。一个 @Tool 注解标注的方法,Spring AI 自动生成 JSON Schema:
@Componentpublic class OrderTools {@Tool(description = "根据订单号查询订单状态")public OrderStatus queryOrder(@ToolParam(description = "订单号") String orderId) {return orderService.query(orderId);}使用时直接注入:
String response = chatClient.prompt().user("帮我查一下订单 12345 的状态").tools(new OrderTools()).call().content();工具执行循环、结果回传、模型二次调用,全部自动处理。
生产级 Agent 的关键拼图
记忆管理
MessageChatMemoryAdvisor 默认在工具循环外部执行——加载一次历史,持久化最终的用户消息和助手回复,中间的 ToolResponseMessage 不会被记忆存储捕获。
要让模型在后续轮次中看到完整的工具调用轨迹,把记忆 Advisor 的 order 调到 ToolCallingAdvisor 之后即可。内置的 Jdbc、Mongo、Cassandra 记忆仓库已经支持工具消息的完整序列化。
MCP 2.0
MCP 支持做了大幅重构。传输层从 MCP Java SDK 移入了 Spring AI 本体,默认传输从 SSE 改为Streamable HTTP,支持无状态变体,可以横向扩展。
暴露 Spring Bean 为 MCP 工具,用 @McpTool 替代 @Tool,加一个 starter 依赖就行。
结构化输出自纠错
新增了 StructuredOutputValidationAdvisor。模型返回的 JSON 不符合 Schema 时,自动把校验错误回传给模型要求重新生成,不需要开发者写任何重试逻辑。
工具搜索(大规模工具场景)
如果注册了 30 个以上工具,每次请求都把全部工具定义塞给模型,会造成上下文膨胀、准确率下降、Token 浪费。ToolSearchToolCallingAdvisor 实现了渐进式工具披露:会话开始时索引全量工具,只注入一个内置的搜索工具,模型按需检索。
一个配置开启:
spring.ai.chat.client.tool-search-advisor.enabled=true动手:一个最小 Java Agent以下是一个完整的 Agent 骨架,具备工具调用和对话记忆能力。
依赖(Maven):
org.springframework.aispring-ai-starter-model-openai2.0.0工具定义:
@Componentpublic class WeatherTools {@Tool(description = "获取指定城市的当前天气")public String getWeather(@ToolParam(description = "城市名称") String city) {return weatherService.fetch(city);}Agent 配置:
@Configurationpublic class AgentConfig {@BeanChatClient chatClient(ChatClient.Builder builder,ChatMemory chatMemory) {return builder.defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build().defaultTools(new WeatherTools()).build();@BeanChatMemory chatMemory(ChatMemoryRepository repository) {return MessageWindowChatMemory.builder().chatMemoryRepository(repository).maxMessages(20).build();}对话入口:
@RestControllerpublic class ChatController {private final ChatClient chatClient;@GetMapping("/chat")public String chat(@RequestParam String message,@RequestParam String sessionId) {return chatClient.prompt().user(message).advisors(a -> a.param(ChatMemory.CONVERSATION_ID, sessionId)).call().content();}这个 Agent 能做的事情:用户问“北京天气怎么样”,模型判断需要调用 getWeather 工具,Spring AI 执行方法、把结果回传给模型、模型生成自然语言回答。整个循环对开发者透明。
值得关注的实际项目
Redis 官方开源的 stock-analysis-agent 是一个多 Agent 协作的真实案例。架构是:协调 Agent 接收用户请求,决定调用哪些专家 Agent(行情、基本面、新闻、技术分析、综合),最后返回合成后的股票分析报告。
项目还演示了 Redis 语义缓存、Agent 工作记忆和长期记忆、语义护栏、限流等生产级关注点。技术栈是 Java 25 + Spring Boot 4.0.6 + Spring AI 2.0。
值得 clone 下来跑一跑,比看文档直观得多。
最后说几句实话
Spring AI 2.0 真正的价值不在于“多接了几个模型”。它做对了一件事:把 Agent 开发从“手写循环 + 手动拼 JSON”变成了 Spring 风格的声明式编程。
工具调用是 Advisor,记忆是 Advisor,RAG 是 Advisor,结构化输出是 Advisor。所有横切关注点收敛到同一条可组合、可观测的链上。
你不需要学 LangChain 的 Python 那一套。你需要的只是 Spring 的那一套——Advisor 链,本质上是 Servlet Filter 的 AI 版本。
对于已经跑着 Spring Cloud、Spring Boot 的 Java 团队来说,AI 能力不是“另起炉灶”,而是在现有工程体系里“加一条链”。
这才是 Spring AI 2.0 真正让人兴奋的地方。
热门跟贴