Spring Ai超简单教程系列 - 07工具调用
在网上找了一些Spring AI相关的教程,因为官方API更新较快的原因,大部分教程的内容都已过时。所以在参照官方参考文档学习的同时,沉淀每篇文章,为同样热爱学习的你,提供参考。
本系列教程参考Spring AI官方1.1.x版本文档:https://docs.spring.io/spring-ai/reference/1.1/index.html
工具调用
Tool Calling(工具调用) 是指让大语言模型在对话过程中,根据用户问题自主决定是否调用应用程序提供的工具或函数,并由应用程序执行工具,最后将执行结果返回给模型生成最终答案。
简单来说:
大模型负责“判断需要做什么”,应用程序负责“真正执行什么”。
例如,用户询问:
“北京今天的天气怎么样?”
大模型本身通常没有实时天气数据,它可以判断需要调用一个天气查询工具:
模型:我要查询北京天气,需要调用 getWeather(city="北京")
应用:执行天气服务接口,返回 25℃,晴
模型:根据工具结果回答用户
在Spring AI中,Tool Calling可以将Java方法、业务服务或外部 API 暴露给大模型使用。
简单示例
大模型无法获取实时信息。任何假设了解当前日期或天气预报等信息的问题,模型都无法回答。我们可以提供一个工具来获取这些信息,并在需要实时信息时让模型调用该工具。
定义获取当前日期的工具方法并添加相应描述。
static class DateTimeTools {
@Tool(description = "获取当前日期和时间")
String getCurrentDateTime() {
log.info("调用了日期和时间工具");
return LocalDateTime.now().atZone(LocaleContextHolder.getTimeZone().toZoneId()).toString();
}
}
接下来,我们将工具提供给模型使用。
@GetMapping("/ai/call/tool/date")
String callDateTimeTools() {
return ChatClient.create(this.chatModel)
.prompt("明天日期是什么?")
.tools(new DateTimeTools())
.call()
.content();
}
大模型输出效果:
明天是 **2026年9月10日**(星期四)。
工具上下文
Spring AI支持通过ToolContext API向工具方法传递额外的上下文信息。可以提供自定义的附加数据。下面已多租户的场景为例,在调用工具方法时,通过ToolContext传递租户信息。
工具方法定义:
static class OrderTools {
@Tool(description = "根据订单Id查询当前订单状态")
String getOrderStats(@ToolParam(description = "订单Id") String orderId, ToolContext toolContext) {
log.info("调用订单查询工具,订单Id:[ {} ]", orderId);
log.info("租户Id:[ {} ]", toolContext.getContext().get("tenantId"));
return new String[]{"下单成功", "运输中", "已收货", "交易成功"}[RandomUtils.secure().randomInt(0, 4)];
}
}
接口定义:
@GetMapping("/ai/call/tool/order")
String callOrderTools() {
return ChatClient.create(this.chatModel)
.prompt("我的订单Id是12345,当前订单状态是什么?")
.tools(new OrderTools())
.toolContext(Map.of("tenantId", "1"))
.call()
.content();
}
调用日志:
2026-09-09T17:25:44.537+08:00 INFO 30764 --- [ai-java-demo] [nio-8080-exec-6] c.c.a.web.ToolCallingController : 调用订单查询工具,订单Id:[ 12345 ]
2026-09-09T17:25:44.540+08:00 INFO 30764 --- [ai-java-demo] [nio-8080-exec-6] c.c.a.web.ToolCallingController : 租户Id:[ 1 ]
大模型输出效果:
您订单Id为12345的订单当前状态是:**已收货**。
实战场景
我们结合SpringBoot中的Bean,开发一个可以查询指定城市天气的接口,向大模型提供天气查询、日期查询两个工具方法,让大模型根据用户输入城市调用工具方法并返回响应内容。
定义天气查询Service类:
@Slf4j
@Service
public class WeatherService {
private final WebClient webClient = WebClient.builder().baseUrl("https://uapis.cn").build();
@Tool(
description = "根据城市名称查询当前天气信息",
resultConverter = WeatherToolCallResultConverter.class
)
public Optional<String> getCurrentWeather(@ToolParam(description = "需要查询的城市名") String city) {
return webClient.get()
.uri(
uriBuilder -> uriBuilder
.path("/api/v1/misc/weather")
.queryParam("city", city)
.build()
)
.retrieve()
.bodyToMono(String.class)
.blockOptional();
}
public static class WeatherToolCallResultConverter implements ToolCallResultConverter {
private final ObjectMapper objectMapper = new ObjectMapper();
@SneakyThrows
@Override
public String convert(@Nullable Object result, @Nullable Type returnType) {
if (result instanceof Optional rop
&& rop.isPresent()
&& rop.get() instanceof String resultStr) {
Map<String, String> json = objectMapper.readValue(resultStr, Map.class);
return String.format(
"根据 %s 的天气信息,%s %s 今天的天气 %s,温度 %s 度, %s %s",
json.get("report_time"),
json.get("province"),
json.get("city"),
json.get("weather"),
json.get("temperature"),
json.get("wind_direction"),
json.get("wind_power")
);
}
return "未查询到天气信息";
}
}
}
定义查询接口:
@GetMapping("/ai/call/tool/weather")
String callWeatherTools(@RequestParam("city") String city) {
return ChatClient.create(this.chatModel)
.prompt()
.user(up -> up.text("查询{city}今天的天气信息").params(Map.of("city", city)))
.system("用户查询天气信息时,按照今天的日期、天气信息发布时间、省份城市、天气、温度、风力返回结果信息,最后根据天气信息添加一句关怀提醒。")
.tools(weatherService, new DateTimeTools())
.call()
.content();
}
大模型输出效果:
为您查询到深圳今天的天气信息如下: 📅 **日期**:2026年9月9日(今天) 🕒 **发布时间**:6分钟前发布 📍 **地点**:广东省 深圳市 🌤 **天气**:多云 🌡 **温度**:30度 💨 **风力**:东北风 3级 --- 💡 **温馨提示**:今天深圳天气多云,气温30度较为闷热,风力不大,体感偏热。建议您外出时注意防晒补水,穿着清凉透气的衣物,及时补充水分哦!午后若在户外活动,不妨携带遮阳伞或帽子。祝您拥有愉快的一天!😊

浙公网安备 33010602011771号