乐于分享
好东西不私藏

SpringBoot41 AI2.0工具实战

SpringBoot41 AI2.0工具实战

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

几点实践心得:

  • @Tooldescription 字段很关键,模型靠它判断什么时候该调用哪个工具。描述写清楚参数含义和返回值格式。
  • 返回 JSON 字符串比返回对象更可控,模型解析起来也干脆。
  • @Tool 注解方法默认注入为 ToolCallback bean,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(),改配置前缀,测一遍工具调用链,基本稳了。