Tools
许多 AI 应用程序通过自然语言与用户交互。然而,某些业务场景需要模型使用结构化输入直接与外部系统(如 API、数据库或文件系统)进行交互。
Tools 是 agents 调用来执行操作的组件。它们通过定义良好的输入和输出让模型与外部世界交互,从而扩展模型的能力。Tools 封装了一个可调用的函数及其输入模式。我们可以把工具定义传递给兼容的 models,允许模型决定是否调用工具以及使用什么参数。在这些场景中,工具调用使模型能够生成符合指定输入模式的请求。
注意:服务器端工具使用
某些聊天模型(例如 OpenAI、Anthropic 和 Gemini)具有在服务器端执行的内置工具,如 Web 搜索和代码解释器。请参阅提供商概述以了解如何使用特定聊天模型访问这些工具。
TIP: 迁移与更多 Tool Calling 说明请参考:Tool Calling 使用指南。
Tool calling(也称为 function calling)是 AI 应用程序中的常见模式,允许 model 与一组 API 或 tools 交互,增强其能力。
Tools 主要用于:
- 信息检索。此类别中的 tools 可用于从外部源检索信息,例如数据库、Web 服务、文件系统或 Web 搜索引擎。目标是增强 model 的知识,使其能够回答原本无法回答的问题。因此,它们可以在 Retrieval Augmented Generation (RAG) 场景中使用。例如,可以使用 tool 检索给定位置的当前天气、检索最新新闻文章或查询数据库中的特定记录。
- 执行操作。此类别中的 tools 可用于在软件系统中执行操作,例如发送电子邮件、在数据库中创建新记录、提交表单或触发工作流。目标是自动化原本需要人工干预或显式编程的任务。例如,可以使用 tool 为与聊天机器人交互的客户预订航班、填写网页上的表单,或在代码生成场景中基于自动化测试(TDD)实现 Java 类。
尽管我们通常将 tool calling 称为 model 能力,但实际上由客户端应用程序提供 tool calling 逻辑。Model 只能请求 tool call 并提供输入参数,而应用程序负责从输入参数执行 tool call 并返回结果。Model 永远无法访问作为 tools 提供的任何 API,这是一个关键的安全考虑。
Spring AI 提供了便捷的 API 来定义 tools、解析来自 model 的 tool call 请求并执行 tool calls。
快速开始
让我们看看如何在 Spring AI 中开始使用 tool calling。我们将实现两个简单的 tools:一个用于信息检索,一个用于执行操作。
信息检索
AI model 无法访问实时信息。任何假设了解当前日期或天气预报等信息的问题都无法由 model 回答。但是,我们可以提供一个可以检索此信息的 tool,并让 model 在需要访问实时信息时调用此 tool。
让我们在 DateTimeTools 类中实现一个 tool 来获取用户时区的 当前日期和时间。该 tool 不接受任何参数。来自 Spring Framework 的 LocaleContextHolder 可以提供用户的时区。该 tool 将定义为用 @Tool 注解的方法。为了帮助 model 理解是否以及何时调用此 tool,我们将提供该 tool 功能的详细描述。
import java.time.LocalDateTime;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.context.i18n.LocaleContextHolder;
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();
}
}
接下来,让我们使 tool 可用于 model。在此示例中,我们将使用 ChatClient 与 model 交互。我们将通过 tools() 方法传递 DateTimeTools 的实例来向 model 提供 tool。当 model 需要知道当前日期和时间时,它将请求调用 tool。在内部,ChatClient 将调用 tool 并将结果返回给 model,然后 model 将使用 tool call 结果生成对原始问题的最终响应。
ChatModel chatModel = ...;
String response = ChatClient.create(chatModel)
.prompt("What day is tomorrow?")
.tools(new DateTimeTools())
.call()
.content();
System.out.println(response);
// 输出:Tomorrow is 2015-10-21.
执行操作
AI model 可用于生成完成某些目标的计划。例如,model 可以生成预订前往丹麦旅行的计划。但是,model 无法执行计划。这就是 tools 的用武之地:它们可用于执行 model 生成的计划。
在前面的示例中,我们使用 tool 来确定当前日期和时间。在此示例中,我们将定义一个用于在特定时间设置闹钟的第二个 tool。目标是从现在起 10 分钟设置闹钟,因此我们需要向 model 提供两个 tools 来完成此任务。
我们将新 tool 添加到与之前相同的 DateTimeTools 类中。新 tool 将接受单个参数,即 ISO-8601 格式的时间。然后,tool 将向控制台打印一条消息,指示已为给定时间设置闹钟。与之前一样,tool 定义为用 @Tool 注解的方法,我们还使用它提供详细描述以帮助 model 理解何时以及如何使用 tool。
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.context.i18n.LocaleContextHolder;
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();
}
@Tool(description = "Set a user alarm for the given time, provided in ISO-8601 format")
void setAlarm(String time) {
LocalDateTime alarmTime = LocalDateTime.parse(time, DateTimeFormatter.ISO_DATE_TIME);
System.out.println("Alarm set for " + alarmTime);
}
}
接下来,让我们使两个 tools 都可用于 model。当我们要求从现在起 10 分钟设置闹钟时,model 首先需要知道当前日期和时间。然后,它将使用当前日期和时间来计算闹钟时间。最后,它将使用闹钟 tool 来设置闹钟。
ChatModel chatModel = ...;
String response = ChatClient.create(chatModel)
.prompt("Can you set an alarm 10 minutes from now?")
.tools(new DateTimeTools())
.call()
.content();
System.out.println(response);
// 在应用程序日志中,您可以检查闹钟是否已在正确时间设置。
概述
Spring AI 通过一组灵活的抽象支持 tool calling,允许您以一致的方式定义、解析和执行 tools。本节概述了 Spring AI 中 tool calling 的主要概念和组件。

- 当我们想要使 tool 可用于 model 时,我们在聊天请求中包含其定义。每个 tool 定义包括名称、描述和输入参数的 schema。
- 当 model 决定调用 tool 时,它发送带有 tool 名称和根据定义 schema 建模的输入参数的响应。
- 应用程序负责使用 tool 名称来识别并使用提供的输入参数执行 tool。
- Tool call 的结果由应用程序处理。
- 应用程序将 tool call 结果发送回 model。
- Model 使用 tool call 结果作为附加上下文生成最终响应。
Tools 是 tool calling 的构建块,它们由 ToolCallback 接口建模。Spring AI 提供了从方法和函数指定 ToolCallback(s) 的内置支持,但您始终可以定义自己的 ToolCallback 实现以支持更多用例。
ChatModel 实现透明地将 tool call 请求分派到相应的 ToolCallback 实现,并将 tool call 结果发送回 model,最终生成最终响应。它们使用 ToolCallingManager 接口来执行此操作,该接口负责管理 tool 执行生命周期。
ChatClient 和 ChatModel 都接受 ToolCallback 对象列表,以使 tools 可用于 model 和最终执行它们的 ToolCallingManager。
除了直接传递 ToolCallback 对象外,您还可以传递 tool 名称列表,这些名称将使用 ToolCallbackResolver 接口动态解析。
创建工具
Spring AI 提供了两种从方法指定 tools(即 ToolCallback(s))的内置支持:
- 声明式,使用
@Tool注解 - 编程式,使用低级
MethodToolCallback实现。
方法作为 Tools
声明式规范:@Tool
您可以通过用 @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();
}
}
@Tool 注解允许您提供有关 tool 的关键信息:
name:tool 的名称。如果未提供,将使用方法名称。AI model 使用此名称在调用时识别 tool。因此,不允许在同一类中有两个同名 tools。名称必须在特定聊天请求中提供给 model 的所有 tools 中唯一。description:tool 的描述,model 可以使用它来理解何时以及如何调用 tool。如果未提供,方法名称将用作 tool 描述。但是,强烈建议提供详细描述,因为这对于 model 理解 tool 的用途以及如何使用它至关重要。未能提供良好的描述可能导致 model 在应该使用时不使用 tool 或错误使用它 。returnDirect:tool 结果是否应直接返回给客户端或传递回 model。有关更多详细信息,请参阅 返回直接。resultConverter:用于将 tool call 的结果转换为String object以发送回 AI model 的ToolCallResultConverter实现。有关更多详细信息,请参阅 结果转换。
方法可以是静态的或实例的,并且可以具有任何可见性(public、protected、package-private 或 private)。包含方法的类可以是顶级类或嵌套类,也可以具有任何可见性(只要在您计划实例化它的地方可以访问)。
NOTE: Spring AI 为
@Tool注解方法的 AOT 编译提供内置支持,只要包含方法的类是 Spring bean(例如@Component)。否则,您需要向 GraalVM 编译器提供必要的配置。例如,通过用@RegisterReflection(memberCategories = MemberCategory.INVOKE_DECLARED_METHODS)注解类。
您可以为方法定义任意数量的参数(包括无参数),支持大多数类型(基元、POJO、枚举、列表、数组、映射等)。同样,方法可以返回大多数类型,包括 void。如果方法返回值,返回类型必须是可序列化类型,因为结果将被序列化并发送回 model。
NOTE: 某些类型不受支持。有关更多详细信息,请参阅 方法 Tool 限制。
Spring AI 将自动为 @Tool 注解方法的输入参数生成 JSON schema。Schema 由 model 用于理解如何调用 tool 并准备 tool 请求。@ToolParam 注解可用于提供有关输入参数的附加信息,例如描述或参数是必需还是可选的。默认情况下,所有输入参数都被视为必需。
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
class DateTimeTools {
@Tool(description = "Set a user alarm for the given time")
void setAlarm(@ToolParam(description = "Time in ISO-8601 format") String time) {
LocalDateTime alarmTime = LocalDateTime.parse(time, DateTimeFormatter.ISO_DATE_TIME);
System.out.println("Alarm set for " + alarmTime);
}
}
@ToolParam 注解允许您提供有关 tool 参数的关键信息:
description:参数的描述,model 可以使用它来更好地理解如何使用它。例如,参数应该是什么格式,允许什么值等。required:参数是必需还是可选的。默认情况下,所有参数都被视为必需。
如果参数用 @Nullable 注解,除非使用 @ToolParam 注解明确标记为必需,否则将被视为可选。
除了 @ToolParam 注解外,您还可以使用来自 Swagger 的 @Schema 注解或来自 Jackson 的 @JsonProperty。有关更多详细信息,请参阅 JSON Schema。
编程式规范:MethodToolCallback
您可以通过编程方式构建 MethodToolCallback 来将方法转换为 tool。
import org.springframework.util.ReflectionUtils;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.tool.method.MethodToolCallback;
import org.springframework.ai.tool.ToolDefinitions;
class DateTimeTools {
String getCurrentDateTime() {
return LocalDateTime.now().atZone(LocaleContextHolder.getTimeZone().toZoneId()).toString();
}
}
Method method = ReflectionUtils.findMethod(DateTimeTools.class, "getCurrentDateTime");
ToolCallback toolCallback = MethodToolCallback.builder()
.toolDefinition(ToolDefinitions.builder(method)
.description("Get the current date and time in the user's timezone")
.build())
.toolMethod(method)
.toolObject(new DateTimeTools())
.build();
MethodToolCallback.Builder 允许您构建 MethodToolCallback 实例并提供有关 tool 的关键信息:
toolDefinition:定义 tool 名称、描述和输入 schema 的ToolDefinition实例。您可以使用ToolDefinition.Builder类构建它。必需。toolMetadata:定义附加设置的ToolMetadata实例,例如结果是否应直接返回给客户端,以及要使用的结果转换器。您可以使用ToolMetadata.Builder类构建它。toolMethod:表示 tool 方法的Method实例。必需。toolObject:包含 tool 方法的对象实例。如果方法是静态的,您可以省略此参数。toolCallResultConverter:用于将 tool call 的结果转换为String对象以发送回 AI model 的ToolCallResultConverter实例。如果未提供,将使用默认转换器(DefaultToolCallResultConverter)。
如果方法是静态的,您可以省略 toolObject() 方法,因为不需要它。
class DateTimeTools {
static String getCurrentDateTime() {
return LocalDateTime.now().atZone(LocaleContextHolder.getTimeZone().toZoneId()).toString();
}
}
Method method = ReflectionUtils.findMethod(DateTimeTools.class, "getCurrentDateTime");
ToolCallback toolCallback = MethodToolCallback.builder()
.toolDefinition(ToolDefinitions.builder(method)
.description("Get the current date and time in the user's timezone")
.build())
.toolMethod(method)
.build();