三、使用Spring AI实现工具调用(Tool Calling)

三、使用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 {
    @Autowired
    private ChatModel chatModel;

    @GetMapping("/tools/setAlarm")
    public String setAlarm(String userInput) {
        return ChatClient.create(chatModel)
                .prompt()
                // 设置可用的工具给大模型,让大模型知道有哪些工具可以使用
                .tools(new LocalDateTimeTool()) // 注册整个类中的 @Tool 方法
                .user(userInput)
                .call()
                .content();
    }
}

c9c3e5ea-94c6-459a-85d3-a9babaf739f5

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 }

    @Override
    public 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 {
    @Autowired
    private 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();
    }

}

92054e4e-f86a-46a8-a956-765ab2c8a838

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 {
    @Autowired
    private 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();
    }
}

d11588b3-3cf3-4656-9a1a-e9b76762a49f

3、最佳实践建议

  • 优先使用 @Bean + Function 模式:便于依赖注入、单元测试和统一管理;
  • 务必填写 description:高质量的描述能显著提升模型选择工具的准确性;
  • 合理命名工具和参数:语义清晰有助于模型理解和推理;
  • 避免副作用过重的操作:工具应尽量保持幂等性和安全性;
  • 记录日志便于调试:观察模型是否准确识别了工具调用时机;
  • 考虑错误处理机制:工具执行失败时应有兜底策略。
posted @ 2025-11-24 10:21  老羅  阅读(80)  评论(0)    收藏  举报