Spring AI ToolCalling 扩展模型能力边界
Spring AI ToolCalling
1.什么是ToolCalling
工具调用(也称为函数调用)是AI应用程序中的一种常见模式,允许模型与一组API或工具进行交互,从而增强其功能。
2.ToolCalling作用
-
信息检索
此类工具可用于从外部源(如数据库、web服务、文件系统或web搜索引擎)检索信息。目标是增加模型的知识,使其能够回答本身无法回答的问题。因此,它们可以用于检索增强(RAG)场景。例如,一个工具可以用来检索给定位置的当前天气,检索最新的新闻文章,或者查询数据库中的特定记录。
-
采取行动
此类工具可用于在软件系统中执行操作,例如发送电子邮件、在数据库中创建新记录、提交表单或触发工作流。其目标是将需要人工干预或显式编程的任务自动化。例如,可以使用工具与聊天机器人交互的客户预订航班,在网页上填写表单,或者在代码生成场景中实现基于自动化测试(TDD)的Java类。
尽管我们通常将工具调用称为模型功能,但实际上是由客户端应用程序提供工具调用逻辑。模型只能请求工具调用并提供输入参数,而应用程序负责根据输入参数执行工具调用并返回结果。模型永远无法访问作为工具提供的任何API,这是一个关键的安全考虑因素。
3.ToolCalling 快速入门
接下来介绍如何在Spring AI中开始使用工具调用。将实现两个简单的工具:一个用于信息检索,另一个用于采取行动。信息检索工具将用于获取用户所在时区的当前日期和时间。动作工具将用于设置指定时间的警报。
3.1 信息检索
人工智能模型无法获取实时信息。当前日期或天气预报等信息模型都无法回答。然而,我们可以提供一个可以检索这些信息的工具,并让模型在需要访问实时信息时调用该工具。
定义工具方法:
class DateTimeTools { @Tool(description = "获取当前用户所在时区的日期和时间以及星期几") String getCurrentDateTime() { LocalDate currentDate = LocalDate.now(); DayOfWeek dayOfWeek = currentDate.getDayOfWeek(); return "当前日期和时间是:" + LocalDateTime.now().atZone(LocaleContextHolder.getTimeZone().toZoneId()) + ",星期" + dayOfWeek; } }
工具方法是一个普通的Java方法,然后使用 @Tool 注释,标识方法为工具方法。为了帮助模型理解工具并在合适的场景调用,需要提供工具功能的详细描述。
接下来使该工具对模型可用。在本例中,使用ChatClient与模型进行交互。将通过tools()方法传递DateTimeTools的实例来向模型提供工具。当模型需要知道当前日期和时间时,它将请求调用该工具。在内部,ChatClient将调用工具并将结果返回给模型,然后模型将使用工具调用结果生成对原始问题的最终响应。
@RestController class MyController { private final ChatClient chatClient; public MyController(ChatClient.Builder chatClientBuilder) { this.chatClient = chatClientBuilder.build(); } @GetMapping("/ai") String generation(@RequestParam(required = false,defaultValue = "明天日期和星期是?")String userInput) { return this.chatClient.prompt() .advisors(new SimpleLoggerAdvisor()) // .tools(new DateTimeTools()) .user(userInput) .call() .content(); } }
响应结果类似如下:
当前日期是2025年3月26日,星期三。因此,明天的日期将是2025年3月27日,星期四。
如果不向模型提供工具,注释代码tools(new DateTimeTools()),问同样的问题得到的答案如下:
由于我无法实时获取当前的日期,你可以根据今天的日期自行推算明天的日期和星期。 通常,日历应用或查看设备上的日期设置可以提供准确的信息。
3.2 采取行动
人工智能模型可以用来生成完成特定目标的计划。例如,一个模型可以生成预订某个城市之旅的计划。然而,模型不具备执行计划的能力。这就是工具的用武之地:它们可以用来执行模型生成的计划。在前面的示例中,我们使用了一个工具来确定当前日期和时间。在本例中,我们将定义第二个工具,用于在特定时间设置警报。我们的目标是从现在开始设置10分钟的闹钟,所以我们需要为模型提供这两个工具来完成这个任务。
public class DateTimeTools { @Tool(description = "获取用户所在时区的当前日期和时间以及星期") String getCurrentDateTime() { LocalDate currentDate = LocalDate.now(); DayOfWeek dayOfWeek = currentDate.getDayOfWeek(); return "当前日期和时间是:" + LocalDateTime.now().atZone(LocaleContextHolder.getTimeZone().toZoneId()) + ",星期" + dayOfWeek; } @Tool(description = "为给定的时间设置一个用户闹钟,时间以 ISO-8601 格式提供。") void setAlarm(String time) { LocalDateTime alarmTime = LocalDateTime.parse(time, DateTimeFormatter.ISO_DATE_TIME); System.out.println("已设置闹铃 " + alarmTime); }
@GetMapping("/ai") String generation(@RequestParam(required = false,defaultValue = "设置一个10分钟后的闹铃")String userInput) { return this.chatClient.prompt() .advisors(new SimpleLoggerAdvisor()) .tools(new DateTimeTools()) .user(userInput) .call() .content(); }
输出内容:
已经为您成功设置了10分钟后(2025年03月26日14:07)的闹钟。当时间到达时,闹钟将会响起。
4.ToolCalling 概念和组件
Spring AI通过一组灵活的抽象来支持工具调用,方便使用者使用统一的方式定义、解析和执行工具。
1.当我们想让一个工具对模型可用时,我们将其定义包含在聊天请求中。每个工具定义由名称、描述和输入参数的模式组成。
2.当模型决定调用一个工具时,它会发送一个响应,其中包含工具名称和按照定义的输入参数。
-
应用程序负责使用工具名称来识别并使用提供的输入参数执行工具。
-
工具调用的结果由应用程序处理。
-
应用程序将工具调用结果发送回模型。
-
模型使用工具调用结果作为附加上下文生成最终响应。
Tools是工具调用的基础组成部分,它们由 ToolCallback 接口进行建模。Spring AI 提供了内置支持,可从方法(MethodToolCallback )和函数(FunctionToolCallback )中指定 ToolCallback,可以定义自己的 ToolCallback 实现类,以支持更多的用例。
聊天模型实现会透明地将工具调用请求分派给相应的 ToolCallback 实现类,并会将工具调用结果发送回模型,模型最终会生成最终响应。它们是通过 ToolCallingManager 接口来实现这一点的,该接口负责管理工具的执行生命周期。
ChatClient 和 ChatModel 都接受一个 ToolCallback 对象列表,以使模型和最终将执行这些工具的 ToolCallingManager 能够使用这些工具。
除了直接传递 ToolCallback 对象之外,你还可以传递一个工具名称列表,这些名称将使用 ToolCallbackResolver 接口动态解析。
4.1 方法作为工具
方法作为工具有两种方式:
1.使用注解@Tool
2.通过编程方式,使用低级的MethodToolCallback实现。
注解@Tool
通过使用@Tool注释将方法转换为工具。
class DateTimeTools { @Tool(description = "Get the current date and time in the user's timezone") String getCurrentDateTime() { return LocalDateTime.now().atZone(LocaleContextHolder.getTimeZone().toZoneId()).toString(); } }
注解定义:
public @interface Tool { String name() default ""; String description() default ""; boolean returnDirect() default false; Class
-