AI05——工具调用
基于 Methods 方法(容易编写、更容易理解、支持的参数和返回类型更多)Methods 模式:通过@Tool注解定义工具,通过tools方法绑定工具// 使用方式").call();基于functions 函数式编程(不推荐,不好用)通过@Bean注解定义工具,通过functions方法绑定工具Spring AI 提供了两种定义工具的方法 ——注解式和编程式。Spring AI 支持大多数常
可以理解为让 AI 大模型 借用外部工具 来完成它自己做不到的事情。 工具调用是一种让大型语言模型(LLM)能够与外部工具和服务进行交互的功能机制
工具调用的工作原理——并不是 AI 服务器自己调用这些工具、,它只提出要求,而真正执行工具的是我们自己的应用程序,执行后再把结果告诉 AI,让它继续工作。
AI 模型永远无法直接接触你的 API 或系统资源,所有操作都必须通过你的程序来执行,这样你可以完全控制 AI 做什么——安全性。
工具的本质就是一种插件(能不自己写,就不写),可以直接在网上找现成的工具实现,例如在 GitHub 社区找到官方提供的 工具源码
通过Spring AI 实现工具调用
定义工具
工具定义模式
基于 Methods 方法(容易编写、更容易理解、支持的参数和返回类型更多)
Methods 模式:通过 @Tool
注解定义工具,通过 tools
方法绑定工具
class WeatherTools { @Tool(description = "Get current weather for a location") public String getWeather(@ToolParam(description = "The city name") String city) { return "Current weather in " + city + ": Sunny, 25°C"; } } // 使用方式 ChatClient.create(chatModel) .prompt("What's the weather in Beijing?") .tools(new WeatherTools()) .call();
基于functions 函数式编程(不推荐,不好用)
通过 @Bean
注解定义工具,通过 functions
方法绑定工具
定义工具
Spring AI 提供了两种定义工具的方法 —— 注解式 和 编程式。
Spring AI 支持大多数常见的 Java 类型作为参数和返回值
1)注解式:
@Tool
注解标记普通 Java 方法,就可以定义工具了,简单直观。
@ToolParam注解描述参数含义
每个工具最好都添加详细清晰的描述,帮助 AI 理解何时应该调用这个工具。
示例代码:
class WeatherTools { @Tool(description = "获取指定城市的当前天气情况") String getWeather(@ToolParam(description = "城市名称") String city) { // 获取天气的实现逻辑 return "北京今天晴朗,气温25°C"; } }
2)编程式:如果想在运行时动态创建工具,可以选择编程式来定义工具,更灵活。
编程式就是把注解式的那些参数,改成通过调用方法来设置了而已
先定义工具类:
class WeatherTools { String getWeather(String city) { // 获取天气的实现逻辑 return "北京今天晴朗,气温25°C"; } }
然后将工具类转换为 ToolCallback 工具定义类,之后就可以把这个类绑定给 ChatClient,从而让 AI 使用工具了。
Method method = ReflectionUtils.findMethod(WeatherTools.class, "getWeather", String.class); //ReflectionUtils.findMethod():这是 Spring 框架提供的反射工具类方法,用于查找指定类中的方法 ToolCallback toolCallback = MethodToolCallback.builder()//创建工具回调的建造者对象 .toolDefinition(ToolDefinition.builder(method)//设置工具定义信息 .description("获取指定城市的当前天气情况") .build()) .toolMethod(method) .toolObject(new WeatherTools()) .build();
集成工具注册
开发了很多工具类后,结合自己的需求,我们可以创建 工具注册类,可以给 AI 一次性提供所有工具,让它自己决定何时调用。方便统一管理和绑定所有工具
@Configuration // @Configuration注解:标记当前类是一个配置类,Spring会扫描并加载其中的配置 /** * 集中的工具注册类 */ //有了这个注册类,如果需要添加或移除工具,只需修改这一个类即可,更利于维护。 public class ToolRegistration { @Value("${search-api.api-key}") private String searchApiKey; // @Bean注解:将该方法的返回值注册为Spring容器中的Bean,其他组件可通过依赖注入使用 // 方法返回值为ToolCallback[]:Spring AI要求的工具数组类型 @Bean public ToolCallback[] allTools() { // 创建文件操作工具实例 FileOperationTool fileOperationTool = new FileOperationTool(); // 创建网络搜索工具实例,传入从配置注入的API密钥 WebSearchTool webSearchTool = new WebSearchTool(searchApiKey); // 创建网页内容提取工具实例 WebScrapingTool webScrapingTool = new WebScrapingTool(); // 创建资源下载工具实例 ResourceDownloadTool resourceDownloadTool = new ResourceDownloadTool(); // 创建终端操作工具实例 TerminalOperationTool terminalOperationTool = new TerminalOperationTool(); // 创建PDF生成工具实例 PDFGenerationTool pdfGenerationTool = new PDFGenerationTool(); // 创建终止工具实例(可能用于终止AI流程) TerminateTool terminateTool = new TerminateTool(); //把自己写的方法转换为可以给springAi用的工具 // 使用ToolCallbacks.from()方法:将多个工具实例转换为统一的ToolCallback数组 // 该方法起到适配器作用,使不同工具类能以统一格式被Spring AI框架处理 return ToolCallbacks.from( fileOperationTool, webSearchTool, webScrapingTool, resourceDownloadTool, terminalOperationTool, pdfGenerationTool, terminateTool ); } }
该代码包含的设计模式:
-
工厂模式:allTools() 方法作为一个工厂方法,负责创建和配置多个工具实例,然后将它们包装成统一的数组返回。这符合工厂模式的核心思想 - 集中创建对象并隐藏创建细节。
-
依赖注入模式:通过
@Value
注解注入配置值,以及将创建好的工具通过 Spring 容器注入到需要它们的组件中。 -
注册模式:该类作为一个中央注册点,集中管理和注册所有可用的工具,使它们能够被系统其他部分统一访问。
-
适配器模式的应用:ToolCallbacks.from 方法可以看作是一种适配器,它将各种不同的工具类转换为统一的 ToolCallback 数组,使系统能够以一致的方式处理它们。
Spring AI要求的工具数组类型——ToolCallback[] ToolCallbacks.from()方法:将多个工具实例转换为统一的ToolCallback数组
使用工具
定义好工具后,Spring AI 提供了多种灵活的方式将工具提供给 ChatClient,让 AI 能够在需要时调用这些工具。
1)按需使用:这是最简单的方式,直接在构建 ChatClient 请求时通过 tools()
方法附加工具。
String response = ChatClient.create(chatModel) .prompt("北京今天天气怎么样?") .tools(new WeatherTools()) // 在这次对话中提供天气工具 .call() .content();
2)全局使用:如果某些工具需要在所有对话中都可用,可以在构建 ChatClient 时注册默认工具。
ChatClient chatClient = ChatClient.builder(chatModel) .defaultTools(new WeatherTools(), new TimeTools()) // 注册默认工具 .build();
3)更底层的使用方式:除了给 ChatClient 绑定工具外,也可以给更底层的 ChatModel 绑定工具(毕竟工具调用是 AI 大模型支持的能力)
// 先得到工具对象 ToolCallback[] weatherTools = ToolCallbacks.from(new WeatherTools()); // 绑定工具到对话 ChatOptions chatOptions = ToolCallingChatOptions.builder() .toolCallbacks(weatherTools) .build(); // 构造 Prompt 时指定对话选项 Prompt prompt = new Prompt("北京今天天气怎么样?", chatOptions); chatModel.call(prompt);
4)批量绑定(复用集成的所有工具)
过 tools 方法绑定所有已注册的工具:
@Resource private ToolCallback[] allTools; public String doChatWithTools(String message, String chatId) { ChatResponse response = chatClient .prompt() .user(message) .tools(allTools) .call() .chatResponse(); String content = response.getResult().getOutput().getText(); return content; }
在使用工具时,Spring AI 会自动处理工具调用的全过程:
AI 模型决定调用工具 => 执行工具方法 => 将结果返回给模型 => 模型基于工具生成回答。
工具进阶知识
工具底层数据结构
AI 怎么知道要如何调用工具?
Spring AI 工具调用的核心在于 ToolCallback
接口,它是所有工具实现的基础
为什么我们刚刚定义工具时,直接通过注解就能把方法变成工具呢?
当使用注解定义工具时,Spring AI 会做大量幕后工作:
-
JsonSchemaGenerator
会解析方法签名和注解,自动生成符合 JSON Schema 规范的参数定义,作为 ToolDefinition 的一部分提供给 AI 大模型 -
ToolCallResultConverter
负责将各种类型的方法返回值统一转换为字符串,便于传递给 AI 大模型处理 -
MethodToolCallback
实现了对注解方法的封装,使其符合ToolCallback
接口规范
工具上下文
假如做了一个用户自助退款功能,如果已登录用户跟 AI 说:”我要退款“,AI 就不需要再问用户 “你是谁?”,让用户自己输入退款信息了;而是直接从系统中读取到 userId,在工具调用时根据 userId 操作退款即可。
工具执行可能需要额外的上下文信息,比如登录用户信息、会话 ID 或者其他环境参数。Spring AI 通过 ToolContext
提供了这一能力。
我们在调用 AI 大模型时,用.toolContext(Map.of())传递上下文参数。
// 从已登录用户中获取用户名称 String loginUserName = getLoginUserName(); String response = chatClient .prompt("帮我查询用户信息") .tools(new CustomerTools()) .toolContext(Map.of("userName", "我是我")) .call() .content(); System.out.println(response);
class CustomerTools { @Tool(description = "Retrieve customer information") Customer getCustomerInfo(Long id, ToolContext toolContext) { return customerRepository.findById(id, toolContext.getContext().get("userName")); } }
ToolContext
本质上就是一个 Map: 它可以携带任何与当前请求相关的信息,但这些信息 不会传递给 AI 模型,只在应用程序内部使用。这样做既增强了工具的安全性
可观测性
工具调用所有主要操作的记录日志(可观测性的功能之一)
logging: level: org.springframework.ai: DEBUG
可以在配置文件中设置 org.springframework.ai
包的日志级别为 DEBUG
:
立即返回
工具执行的结果不需要再经过 AI 模型处理,而是希望直接返回给用户(比如生成pdf文档)
使用注解式:
@Tool(description = "Retrieve customer information", returnDirect = true)
定义工具时,将 returnDirect
属性设为 true
使用编程式:
手动构造 ToolMetadata 对象:设置 .returnDirect(true)
ToolMetadata toolMetadata = ToolMetadata.builder() .returnDirect(true) .build(); Method method = ReflectionUtils.findMethod(CustomerTools.class, "getCustomerInfo", Long.class); ToolCallback toolCallback = MethodToolCallback.builder() .toolDefinition(ToolDefinition.builder(method) .description("Retrieve customer information") .build()) .toolMethod(method) .toolObject(new CustomerTools()) .toolMetadata(toolMetadata) .build();
更多推荐
所有评论(0)