升级 Spring Boot 4.1 时顺手把 Spring AI 提到了 2.0。结果发现工具调用变化不小——toolNames() 删了,ChatClient 成了唯一入口,配置前缀也改了。分享下实际集成步骤和踩坑记录。
项目搭建
Spring AI 2.0 要求 Java 17+、Spring Boot 4.0/4.1、Spring Framework 7.0。我的项目直接用 Boot 4.1 + Java 21。
Maven 依赖就加一个 starter,版本用 2.0.0:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
如果要集成 DeepSeek、Ollama 等其他模型,换对应的 starter 就行,API 用法完全一样。
然后是 application.yml,注意 2.0 去掉了 .options 前缀:
spring:
ai:
openai:
api-key:${OPENAI_API_KEY}
chat:
model:gpt-4o
chat:
client:
tool-search-advisor:
enabled:true
tool-index-type:vector
tool-search-advisor 是 2.0 新增的渐进式工具披露机制——工具多了以后,不会一股脑把所有工具定义塞进 prompt,而是按需查询,能省不少 token。
工具定义
Spring AI 2.0 注册工具的方式有三种:@Tool 注解、java.util.Function 实现、ToolCallback bean。最简洁的是 @Tool 注解,直接在 @Component 类的方法上加注解就行。
下面这个工具类提供天气查询和汇率转换两个功能:
@Component
publicclass AiTools{
@Tool(description="查询指定城市的实时天气")
publicStringgetWeather(Stringcity){
// 生产环境调真实 API,这里模拟返回
return"{\"city\":\""+city+"\",\"temperature\":\"28°C\",\"humidity\":\"65%\"}";
}
@Tool(description="货币汇率转换,支持 USD/CNY/EUR/JPY")
publicStringconvertCurrency(Stringfrom,Stringto,doubleamount){
Map<String,Map<String,Double>>rates=Map.of(
"USD",Map.of("CNY",7.24,"EUR",0.92),
"CNY",Map.of("USD",0.14,"JPY",20.15)
);
doublerate=rates.getOrDefault(from,Map.of())
.getOrDefault(to,1.0);
doubleresult=amount*rate;
returnString.format("%.2f %s = %.2f %s",amount,from,result,to);
}
}
几点实践心得:
@Tool的description字段很关键,模型靠它判断什么时候该调用哪个工具。描述写清楚参数含义和返回值格式。- 返回 JSON 字符串比返回对象更可控,模型解析起来也干脆。
@Tool注解方法默认注入为ToolCallbackbean,Spring AI 启动时自动收集,不需要额外注册。
如果你有现成的 java.util.Function 实现,也可以用 toolCallbacks(Function.class) 注册。但我更推荐 @Tool —— 少写样板代码,语义也更清晰。
ChatClient 调用
工具定义好了,接下来配置 ChatClient。Spring AI 2.0 的 ChatClient 是 Builder 模式,核心是挂载 ToolCallingAdvisor:
@Service
publicclass AiChatService{
privatefinalChatClientchatClient;
publicAiChatService(ChatClient.Builderbuilder,List<ToolCallback>toolCallbacks){
this.chatClient=builder
.defaultAdvisors(
newToolCallingAdvisor(toolCallbacks,
ToolCallingAdvisorConfig.builder()
.maxToolExecuteCount(5)
.build())
)
.build();
}
publicStringchat(StringuserMessage){
returnchatClient.prompt()
.user(userMessage)
.call()
.content();
}
}
ToolCallingAdvisor 把工具定义塞进 system prompt 发给大模型,模型返回的响应里有工具调用请求就去执行对应方法,然后把结果喂回模型让它继续推理。这个过程反复进行,直到模型给出纯文本响应或达到 maxToolExecuteCount 上限。
maxToolExecuteCount 默认是 5,我一般设 5-10 之间。设太小模型来不及完成多步推理,设太大如果工具有 BUG 可能陷入死循环。
如果项目里工具数量很多(10 个以上),可以换成 ToolSearchToolCallingAdvisor,它的渐进式披露策略只会在 prompt 里放当前轮次需要的工具,而不是全量注入。
运行验证
启动项目,用 Postman 或 curl 测一下:
// 请求:查询天气并发邮件
{"message":"北京今天天气怎么样?顺便把100美元换成人民币"}
// 响应
北京当前气温28°C,湿度65%,体感舒适。
100.00USD=724.00CNY
模型自动识别出需要调用两个工具,先调 getWeather 拿到天气信息,再调 convertCurrency 算汇率,最后把结果整合成自然语言返回。整个过程对一个工具调用就完成了两轮。
如果用一个需要多步推理的问题测试,比如「我有 1000 美元换成人民币能买多少股腾讯股票」,模型会依次调汇率转换工具和股票查询工具(如果有),形成多步调用链。
踩坑提醒
从 Spring AI 1.x 升到 2.0,有几个变化需要特别留意。
toolNames() 没了。 1.x 用 toolNames("weather", "currency") 指定工具,2.0 彻底删了,改用 ToolCallback bean 统一注册。要按名字过滤,要么拆 ChatClient,要么在方法里自己判断。
.options 前缀删除。 1.x 的 spring.ai.openai.chat.options.model 在 2.0 里变成 spring.ai.openai.chat.model。升级后配置文件不改,模型加载会失败。
Jackson 3 兼容。 Spring AI 2.0 依赖 Jackson 3,如果你项目里还有 Jackson 2 的序列化配置,升级后 JSON 解析行为可能有差异。工具方法返回的字符串是纯文本,不受影响,但如果你用 @Tool 返回对象(2.0 支持自动序列化),要确认序列化结果符合预期。
Streamable HTTP 是默认传输。 2.0 不再依赖 SSE(Server-Sent Events),默认用 Streamable HTTP。绝大多数情况下对开发者透明,但如果你的网络环境对 HTTP 流有特殊限制,需要留意。
总结
Spring AI 2.0 的工具调用比 1.x 成熟不少。@Tool 注解 + ToolCallingAdvisor,写个 Component、加几个注解、配个 Advisor,工具调用循环就跑起来了。
工具方法保持单一职责——模型调用工具是原子操作,粒度越细组合越灵活。description 写详细些,模型靠描述匹配用户意图。工具超过 10 个就上 ToolSearchToolCallingAdvisor,首轮响应速度提升很明显。
还在 1.x 的升级不算大工程:删 toolNames(),改配置前缀,测一遍工具调用链,基本稳了。
夜雨聆风