Spring AI(4)——工具调用
工具调用也成为函数工具调用,是人工智能应用中的一个常见模式,通过函数调用允许模型与一组API或工具进行交互,从而增强其能力。
主要作用:
信息检索。此类别中的工具可用于从外部源检索信息,例如数据库、Web服务、文件系统或Web搜索引擎。其目的是增强模型的知识,使其能够回答其他方式无法回答的问题。例如,可以使用某个工具检索给定地点的当前天气、获取最新的新闻文章或查询数据库中的特定记录。
执行操作。本类工具可用于在软件系统中执行操作,例如发送电子邮件、在数据库中创建新记录、提交表单或触发工作流程。目标是自动化否则需要人工干预或明确编程的任务。例如,可以使用某个工具为与聊天机器人互动的客户预订航班,填写网页上的表单等。
执行流程:
1.当我们想让工具对大模型可用时,我们会将它的定义包含在聊天请求中。每个工具定义包括名称、描述和输入参数的模式。
2.当大模型决定调用工具时(注意:是否调用函数工具,由大模型做决定),它会发送一个响应,其中包含工具名称以及根据定义模式建模的输入参数。
3.我们的应用负责使用工具名称来识别并执行带有提供输入参数的工具。
4.工具调用的结果由应用处理。
5.应用将工具调用结果发送回大模型。
6.大模型根据工具调用的结果,结合上下文信息生成最终响应。
工具调用的是实现方式:
- 方法作为工具
Spring AI 提供内置支持,可以通过两种方式从方法中指定工具(即 ToolCallback):
声明式方式,使用 @Tool 注解
编程式方式,使用低级 MethodToolCallback 实现。
- 函数作为工具
Spring AI 提供内置支持,可以从函数中指定工具,既可以通过编程式使用低级 FunctionToolCallback 将函数式类型(Function
、Supplier
、Consumer
或 BiFunction
)转换为工具实现
还可以将工具定义为 Spring bean,并让 Spring AI 在运行时使用 ToolCallbackResolver
接口(通过 SpringBeanToolCallbackResolver
实现)动态解析它们
注意:本例主要演示@Tool声明式方式
定义工具类
public class MyTools {@Tool(name="getTodayDate", description = "获取今天日期信息")String getTodayDate() {System.out.println("获取当前日期时间");SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");return sdf.format(new Date());}}
大模型调用的工具通过@Tool
修饰,其包含如下4个属性:
-
name
:工具的名称。如果未提供,将使用方法名称。AI 模型在调用工具时使用此名称进行识别。因此,同一类中不允许存在名称相同的两个工具。对于特定聊天请求,该名称在模型可用的所有工具中必须是唯一的。 -
description
:工具的描述,模型可以使用此描述来理解何时以及如何调用该工具。如果未提供,将使用方法名称作为工具描述。但是,强烈建议提供详细描述,这对于模型理解工具目的及使用方法至关重要。未提供良好的描述可能导致模型在应该使用时未使用工具,或者使用错误。 -
returnDirect
:工具结果是直接返回给客户端,还是传回给模型。默认值false,表示执行工具的结果需要再发送给大模型,大模型根据上下文返回组织后的信息。 -
resultConverter
:用于将工具调用结果转换为自定义的String类型的数据,需要开发人员 重写ToolCallResultConverter
接口的方法实现,默认返回JSON格式的字符串。
通过ChatModel对象调用
@GetMapping("/chat")public String chat(String message) {// 将指定类中@Tool修饰的方法转为ToolCallback对象ToolCallback[] dateTimeTools = ToolCallbacks.from(new MyTools());// 设置ChatOptions, 指定工具ChatOptions chatOptions = ToolCallingChatOptions.builder().toolCallbacks(dateTimeTools).build();// 提示词对象中指定工具Prompt prompt = new Prompt(message, chatOptions);// Prompt prompt = new Prompt(message);ChatResponse response = chatModel.call(prompt);System.out.println(response.getResult().getOutput().getText());return "success";}
提问内容:今天的日期
返回结果:
从日志信息可以看到,内部调用了getTodayDate的方法。
提问内容:明天的日期
返回结果:
从输出看到,返回了正确的结果。比如当前日期是5月13号,明天的日期是5月14号。
在这里我们再次强调下执行流程:
- 大模型根据用户提出的问题确定是否需要调用工具
- 如果需要调用,大模型返回需要调用哪个工具
- 我们的应用执行对应的工具(默认spring ai内部封装的代码,帮助我们直接调用工具)
- 将工具执行结果再次发给大模型
- 大模型给出最终结果
通过ChatClient对象调用
@GetMapping("/chat2")public String chat2(String message) {String answer = this.client.prompt(message)// 指定工具.tools(new MyTools()).call().content();System.out.println(answer);return "success";}
上面的例子,我们是每次发送消息时都指定工具,这样子做更灵活。如果不想每次指定,我们可以通过defaultTools()方法为ChatClient指定默认工具:
@Beanpublic ChatClient chatClient(ZhiPuAiChatModel chatModel) {return ChatClient.builder(chatModel)// 设置系统消息.defaultSystem("你是一个java架构师")// 指定默认工具.defaultTools(new MyTools())//配置日志相关的Advisor,需要开启日志级别以及配置 默认debug级别.defaultAdvisors(simpleLoggerAdvisor(),MessageChatMemoryAdvisor.builder(chatMemory()).build()).build();}
如果即指定了默认工具,发送消息时也指定了工具,发送消息指定的工具生效。
returnDirect属性
工具结果是直接返回给客户端,还是传回给模型
在工具定义中,我们将returnDirect设置为true
public class MyTools {@Tool(name="getTodayDate", description = "获取今天日期信息", returnDirect = true)String getTodayDate() {System.out.println("获取当前日期时间");SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");return sdf.format(new Date());}}
提出的问题:明天的日期
返回结果:
通过返回内容,我们发现,设置 returnDirect=true后,只是返回了工具的调用结果,并没有将结果再次交给大模型。
大模型自动调用多次函数工具
public class MyTools {@Tool(description = "获取今天日期信息")String getTodayDate() {System.out.println("获取当前日期时间");SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");return sdf.format(new Date());}@Tool(description = "根据指定日期获取天气情况")String getWeather(@ToolParam(description = "日期信息,格式为yyyy-MM-dd") String date) {System.out.println("获取指定日期的天气:" + date);return "晴天 35度";}}
本例,我们设置了两个工具,一个用户获取当前日志,一个用于根据日期获取天气情况(给出模型的数据)。
提出问题:今天的天气
返回结果:
通过结果可以看出,大模型根据我们的提问,进行自动分析,内部调用了两次工具,返回了我们想要的结果。
提出问题:明天的天气
返回结果:
我们询问明天的天气,本意是想大模型得到明天的日期,然后返回天气,但是大模型只调用了获取天气的方法,日期随便给出。
修改提出的问题:查询天气时,需要先获取日期,然后根据日期查询。问题:明天的天气
返回结果:
返回结果符合我们的预期。
通过上面的例子,我们看到,是否调用函数工具由大模型决定,提示词描述的越准确,返回的结果也越精确。