三、使用Spring AI实现工具调用(Tool Calling)
==================================================================================
==================================================================================
参考资料:
==================================================================================
Spring AI 框架在升级,Function Calling 废弃,被 Tool Calling 取代本文对 Spr - 掘金 (juejin.cn)
Spring AI 工具调用(Tool Calling)实战_java ai 提示词里分析结果后调用tool接口获取具体的值 这个原理是什么-CSDN博客
004-Spring AI 实现 Java Function Callback 功能完整案例-CSDN博客
Migrating from FunctionCallback to ToolCallback API :: Spring AI Reference
==================================================================================
在大模型应用开发中,一个核心挑战是如何让语言模型“走出”自身的知识边界,与外部系统进行交互。这就是 工具调用(Tool Calling) 的意义所在。
工具调用 是指语言模型根据用户输入判断是否需要调用某个外部 API 或执行某项操作,并生成结构化的调用请求(包含工具名和参数),由客户端应用程序负责执行该调用并将结果返回给模型,最终由模型整合信息并生成自然语言响应。
它主要应用于两大场景:
信息检索:从数据库、网络服务、文件系统等外部来源获取实时或私有数据,扩展模型的知识能力。
采取行动:触发系统内的具体操作,如发送邮件、设置提醒、创建任务等,实现自动化流程。
1、使用 @Tool 注解定义工具
Spring AI 支持通过注解的方式将普通 Java 方法转化为可被模型识别的“工具”。
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.context.i18n.LocaleContextHolder;import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;public class LocalDateTimeTool {// 定义一个工具,和普通方法没有什么区别,仅多了一个 @Tool 注解// 注意,description 是工具的描述信息,要编写准确清晰,便于AI准确发现@Tool(description = "获取用户所在时区的当前日期和时间")public String getCurrentDateTime() {System.out.println("->> getCurrentDateTime() 获取用户所在时区的当前日期和时间");return LocalDateTime.now().atZone(LocaleContextHolder.getTimeZone().toZoneId()).toString();}@Tool(description = "根据 ISO-8601 格式设置用户在指定时间的闹钟")LocalDateTime setAlarm(String time) {System.out.println("->> setAlarm() 根据 ISO-8601 格式设置用户在指定时间的闹钟, time=" + time);LocalDateTime alarmTime = LocalDateTime.parse(time, DateTimeFormatter.ISO_DATE_TIME);System.out.println("->> 闹钟设置为 " + alarmTime);return alarmTime;}
}
模型会自动提取时间并格式化为 ISO-8601 字符串传入 setAlarm() 方法。
@RestController
public class ToolsController {@Autowiredprivate ChatModel chatModel;@GetMapping("/tools/setAlarm")public String setAlarm(String userInput) {return ChatClient.create(chatModel).prompt()// 设置可用的工具给大模型,让大模型知道有哪些工具可以使用.tools(new LocalDateTimeTool()) // 注册整个类中的 @Tool 方法.user(userInput).call().content();}
}

2、使用函数式编程定义工具
示例:天气查询服务
public class WeatherService implements Function<WeatherService.WeatherRequest, WeatherService.WeatherResponse> {public record WeatherRequest(String location, Unit unit) {}public record WeatherResponse(double temp, Unit unit) {}public enum Unit { C, F }@Overridepublic WeatherResponse apply(WeatherRequest request) {System.out.println("获取{" + request.location + "}的天气");double temperature = Math.random() * 50; // 模拟随机温度return new WeatherResponse(temperature, Unit.C);}
}
2.1、手动构建 FunctionToolCallback
适用于灵活注册、动态管理工具场景。
@RestController
public class ToolsController {@Autowiredprivate ChatModel chatModel;@GetMapping("/functions/getWeather")public String getWeather(String userInput) {FunctionToolCallback<WeatherService.WeatherRequest, WeatherService.WeatherResponse> callback =FunctionToolCallback.builder("currentWeather", new WeatherService()).description("Get the weather in location").inputType(WeatherService.WeatherRequest.class).build();return ChatClient.create(chatModel).prompt().toolCallbacks(callback).user(userInput).call().content();}}

2.2、通过 @Bean 注册工具
利用 Spring IoC 容器统一管理工具实例,更符合我们的开发习惯。
@Configuration(proxyBeanMethods = false)
public class WeatherTools {public static final String CURRENT_WEATHER_TOOL = "currentWeather";@Bean(CURRENT_WEATHER_TOOL)@Description("Get the weather in location")public Function<WeatherService.WeatherRequest, WeatherService.WeatherResponse> currentWeather() {return new WeatherService();}
}
@RestController
public class ToolsController {@Autowiredprivate ChatModel chatModel;@GetMapping("/beans/getWeather")public String getWeatherBySpring(String userInput) {return ChatClient.create(chatModel).prompt().toolNames(WeatherTools.CURRENT_WEATHER_TOOL) // 仅传入 Bean 名称.user(userInput).call().content();}
}

3、最佳实践建议
- 优先使用 @Bean + Function 模式:便于依赖注入、单元测试和统一管理;
- 务必填写 description:高质量的描述能显著提升模型选择工具的准确性;
- 合理命名工具和参数:语义清晰有助于模型理解和推理;
- 避免副作用过重的操作:工具应尽量保持幂等性和安全性;
- 记录日志便于调试:观察模型是否准确识别了工具调用时机;
- 考虑错误处理机制:工具执行失败时应有兜底策略。