跳到主要内容

工具调用

DeepSeek V3 中英对照 Tool Calling

工具调用(也称为函数调用)是 AI 应用中的一种常见模式,它允许模型与一组 API 或工具进行交互,从而增强其能力。

工具主要用于:

  • 信息检索。此类工具可用于从外部来源(如数据库、Web 服务、文件系统或 Web 搜索引擎)检索信息。其目标是增强模型的知识,使其能够回答原本无法回答的问题。因此,它们可以用于检索增强生成(Retrieval Augmented Generation, RAG)场景。例如,可以使用工具检索给定位置的当前天气、检索最新的新闻文章或查询数据库中的特定记录。

  • 执行操作。此类工具可用于在软件系统中执行操作,例如发送电子邮件、在数据库中创建新记录、提交表单或触发工作流。其目标是自动化那些原本需要人工干预或显式编程的任务。例如,可以使用工具为与聊天机器人互动的客户预订航班、在网页上填写表单,或在代码生成场景中基于自动化测试(TDD)实现 Java 类。

尽管我们通常将 tool calling 称为模型的能力,但实际上,提供工具调用逻辑的是客户端应用程序。模型只能请求工具调用并提供输入参数,而应用程序负责根据输入参数执行工具调用并返回结果。模型永远不会获得对任何作为工具提供的 API 的访问权限,这是一个关键的安全考虑因素。

Spring AI 提供了便捷的 API 来定义工具、解析来自模型的工具调用请求并执行工具调用。以下部分概述了 Spring AI 中的工具调用功能。

备注

查看 聊天模型比较 以了解哪些 AI 模型支持工具调用功能。

快速开始

让我们看看如何在 Spring AI 中开始使用工具调用。我们将实现两个简单的工具:一个用于信息检索,另一个用于执行操作。信息检索工具将用于获取用户时区的当前日期和时间。操作工具将用于为指定时间设置闹钟。

信息检索

AI 模型无法访问实时信息。任何假设了解当前日期或天气预报等信息的问题,模型都无法回答。不过,我们可以提供一个能够检索这些信息的工具,并在需要访问实时信息时让模型调用该工具。

让我们实现一个工具,在 DateTimeTools 类中获取用户所在时区的当前日期和时间。该工具不需要任何参数。Spring Framework 中的 LocaleContextHolder 可以提供用户的时区。该工具将定义为一个带有 @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();
}

}
java

接下来,我们将使工具可供模型使用。在这个示例中,我们将使用 ChatClient 与模型进行交互。我们将通过 tools() 方法传递 DateTimeTools 的实例,将工具提供给模型。当模型需要知道当前日期和时间时,它将请求调用该工具。在内部,ChatClient 将调用工具并将结果返回给模型,模型随后将使用工具调用的结果生成对原始问题的最终响应。

ChatModel chatModel = ...

String response = ChatClient.create(chatModel)
.prompt("What day is tomorrow?")
.tools(new DateTimeTools())
.call()
.content();

System.out.println(response);
java

输出将会类似于:

Tomorrow is 2015-10-21.
none

你可以再次尝试询问相同的问题。这次,不要向模型提供工具。输出将会类似于:

I am an AI and do not have access to real-time information. Please provide the current date so I can accurately determine what day tomorrow will be.
none

如果没有这个工具,模型无法回答这个问题,因为它不具备确定当前日期和时间的能力。

采取行动

AI 模型可以用来生成实现某些目标的计划。例如,一个模型可以生成预订去丹麦旅行的计划。然而,模型本身没有执行计划的能力。这就是工具的作用:它们可以用来执行模型生成的计划。

在前面的示例中,我们使用了一个工具来确定当前的日期和时间。在本示例中,我们将定义第二个工具,用于在特定时间设置闹钟。目标是设置一个从现在起 10 分钟的闹钟,因此我们需要向模型提供这两个工具来完成此任务。

我们将把新工具添加到之前相同的 DateTimeTools 类中。新工具将接受一个参数,即 ISO-8601 格式的时间。然后,工具将向控制台打印一条消息,指示闹钟已设置为给定的时间。和之前一样,工具被定义为一个带有 @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);
}

}
java

接下来,我们将使这两个工具对模型可用。我们将使用 ChatClient 与模型进行交互。我们将通过 tools() 方法传递 DateTimeTools 的实例,向模型提供这些工具。当我们要求从现在开始设置一个 10 分钟的闹钟时,模型首先需要知道当前的日期和时间。然后,它将使用当前的日期和时间来计算闹钟时间。最后,它将使用闹钟工具来设置闹钟。在内部,ChatClient 将处理来自模型的任何工具调用请求,并将工具调用的执行结果发送回模型,以便模型可以生成最终的响应。

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);
java

在应用程序日志中,您可以检查告警是否已在正确的时间设置。

概述

Spring AI 通过一组灵活的抽象支持工具调用,这些抽象允许你以一致的方式定义、解析和执行工具。本节概述了 Spring AI 中工具调用的主要概念和组件。

工具调用的主要操作序列

  1. 当我们希望模型能够使用某个工具时,我们会在聊天请求中包含该工具的定义。每个工具定义由名称、描述和输入参数的 schema 组成。

  2. 当模型决定调用某个工具时,它会发送一个包含工具名称和根据定义的 schema 建模的输入参数的响应。

  3. 应用程序负责使用工具名称来识别并执行该工具,并使用提供的输入参数。

  4. 工具调用的结果由应用程序处理。

  5. 应用程序将工具调用的结果发送回模型。

  6. 模型使用工具调用的结果作为额外的上下文生成最终的响应。

工具是工具调用的构建模块,它们通过 ToolCallback 接口进行建模。Spring AI 提供了内置支持,可以从方法和函数中指定 ToolCallback,但你也可以随时定义自己的 ToolCallback 实现,以支持更多的用例。

ChatModel 实现会透明地将工具调用请求分派给相应的 ToolCallback 实现,并将工具调用结果发送回模型,最终生成最终响应。它们通过 ToolCallingManager 接口来实现这一功能,该接口负责管理工具执行的生命周期。

ChatClientChatModel 都接受一个 ToolCallback 对象列表,以便将这些工具提供给模型和最终执行它们的 ToolCallingManager

除了直接传递 ToolCallback 对象外,您还可以传递一个工具名称列表,这些名称将使用 ToolCallbackResolver 接口动态解析。

以下部分将详细介绍所有这些概念和 API,包括如何定制和扩展它们以支持更多用例。

方法作为工具

Spring AI 提供了内置支持,可以通过两种方式从方法中指定工具(即 ToolCallback):

  • 声明式地,使用 @Tool 注解

  • 编程式地,使用底层的 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();
}

}
java

@Tool 注解允许你提供关于工具的关键信息:

  • name: 工具的名称。如果未提供,将使用方法名称。AI 模型在调用工具时使用此名称来识别工具。因此,不允许在同一类中存在两个同名的工具。该名称在模型可用于特定聊天请求的所有工具中必须是唯一的。

  • description: 工具的描述,模型可以使用该描述来理解何时以及如何调用该工具。如果未提供,将使用方法名称作为工具描述。但是,强烈建议提供详细的描述,因为这对于模型理解工具的用途以及如何使用它至关重要。未能提供良好的描述可能导致模型在该使用工具时未使用它,或错误地使用它。

  • returnDirect: 工具结果是否应直接返回给客户端或传递回模型。有关更多详细信息,请参阅直接返回

  • resultConverter: 用于将工具调用的结果转换为 String 对象 以发送回 AI 模型的 ToolCallResultConverter 实现。有关更多详细信息,请参阅结果转换

该方法可以是静态的或实例的,并且可以具有任何可见性(publicprotected、包私有或private)。包含该方法的类可以是顶级类或嵌套类,并且也可以具有任何可见性(只要在您计划实例化它的地方可以访问)。

备注

Spring AI 提供了对 @Tool 注解方法的 AOT 编译的内置支持,只要包含这些方法的类是 Spring bean(例如 @Component)。否则,你需要为 GraalVM 编译器提供必要的配置。例如,通过在类上添加 @RegisterReflection(memberCategories = MemberCategory.INVOKE_DECLARED_METHODS) 注解。

你可以为方法定义任意数量的参数(包括无参数),并且支持大多数类型(基本类型、POJO、枚举、列表、数组、映射等)。同样,方法可以返回大多数类型,包括 void。如果方法返回值,则返回类型必须是可序列化的类型,因为结果将被序列化并发送回模型。

备注

某些类型不受支持。更多详情请参阅方法工具限制

Spring AI 会自动为 @Tool 注解的方法的输入参数生成 JSON 模式。该模式用于让模型理解如何调用工具并准备工具请求。@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);
}

}
java

@ToolParam 注解允许你提供关于工具参数的关键信息:

  • description: 参数的描述,模型可以使用它来更好地理解如何使用该参数。例如,参数应该采用什么格式,允许哪些值等。

  • required: 参数是必需的还是可选的。默认情况下,所有参数都被视为必需的。

如果一个参数被注解为 @Nullable,它将被视为可选的,除非使用 @ToolParam 注解明确标记为必需的。

除了 @ToolParam 注解,您还可以使用 Swagger 的 @Schema 注解或 Jackson 的 @JsonProperty 注解。更多详情请参阅 JSON Schema

ChatClient 添加工具

在使用声明式规范方法时,你可以在调用 ChatClient 时将工具类实例传递给 tools() 方法。这些工具仅在添加它们的特定聊天请求中可用。

ChatClient.create(chatModel)
.prompt("What day is tomorrow?")
.tools(new DateTimeTools())
.call()
.content();
java

在底层,ChatClient 会从工具类实例中每个带有 @Tool 注解的方法生成一个 ToolCallback,并将它们传递给模型。如果你更倾向于自己生成 ToolCallback,你可以使用 ToolCallbacks 工具类。

ToolCallback[] dateTimeTools = ToolCallbacks.from(new DateTimeTools());
java

ChatClient 添加默认工具

在使用声明式规范方法时,您可以通过将工具类实例传递给 defaultTools() 方法,将默认工具添加到 ChatClient.Builder 中。如果同时提供了默认工具和运行时工具,运行时工具将完全覆盖默认工具。

注意

默认的工具在所有由同一个 ChatClient.Builder 构建的 ChatClient 实例执行的所有聊天请求之间共享。这些工具对于在不同聊天请求中常用的工具非常有用,但如果不小心使用,也可能带来风险,可能导致它们在不应该可用的情况下被使用。

ChatModel chatModel = ...
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultTools(new DateTimeTools())
.build();
java

ChatModel 添加工具

在使用声明式规范方法时,你可以将工具类实例传递给 ToolCallingChatOptionstoolCallbacks() 方法,该方法用于调用 ChatModel。这些工具只会对它们被添加到的特定聊天请求可用。

ChatModel chatModel = ...
ToolCallback[] dateTimeTools = ToolCallbacks.from(new DateTimeTools());
ChatOptions chatOptions = ToolCallingChatOptions.builder()
.toolCallbacks(dateTimeTools)
.build():
Prompt prompt = new Prompt("What day is tomorrow?", chatOptions);
chatModel.call(prompt);
java

ChatModel 添加默认工具

在使用声明式规范方法时,你可以在构造时将默认工具添加到 ChatModel 中,方法是将工具类实例传递给用于创建 ChatModelToolCallingChatOptions 实例的 toolCallbacks() 方法。如果同时提供了默认工具和运行时工具,运行时工具将完全覆盖默认工具。

注意

默认工具会在该 ChatModel 实例执行的所有聊天请求之间共享。这些工具对于在不同聊天请求中常用的工具非常有用,但如果不小心使用,也可能会带来风险,可能会导致在不应该使用它们的情况下使其可用。

ToolCallback[] dateTimeTools = ToolCallbacks.from(new DateTimeTools());
ChatModel chatModel = OllamaChatModel.builder()
.ollamaApi(new OllamaApi())
.defaultOptions(ToolCallingChatOptions.builder()
.toolCallbacks(dateTimeTools)
.build())
.build();
java

编程规范:MethodToolCallback

你可以通过编程方式构建一个 MethodToolCallback 来将一个方法转换为工具。

class DateTimeTools {

String getCurrentDateTime() {
return LocalDateTime.now().atZone(LocaleContextHolder.getTimeZone().toZoneId()).toString();
}

}
java

MethodToolCallback.Builder 允许你构建一个 MethodToolCallback 实例,并提供关于该工具的关键信息:

  • toolDefinition: 定义工具名称、描述和输入模式的 ToolDefinition 实例。你可以使用 ToolDefinition.Builder 类来构建它。必需。

  • toolMetadata: 定义附加设置的 ToolMetadata 实例,例如是否应将结果直接返回给客户端,以及要使用的结果转换器。你可以使用 ToolMetadata.Builder 类来构建它。

  • toolMethod: 表示工具方法的 Method 实例。必需。

  • toolObject: 包含工具方法的对象实例。如果方法是静态的,可以省略此参数。

  • toolCallResultConverter: 用于将工具调用结果转换为 String 对象以发送回 AI 模型的 ToolCallResultConverter 实例。如果未提供,将使用默认转换器(DefaultToolCallResultConverter)。

ToolDefinition.Builder 允许你构建一个 ToolDefinition 实例,并定义工具名称、描述和输入模式:

  • name: 工具的名称。如果未提供,将使用方法名称。AI 模型在调用工具时使用此名称来识别工具。因此,不允许在同一类中存在两个具有相同名称的工具。该名称在模型用于特定聊天请求的所有可用工具中必须是唯一的。

  • description: 工具的描述,模型可以使用此描述来理解何时以及如何调用该工具。如果未提供,将使用方法名称作为工具描述。然而,强烈建议提供详细的描述,因为这对于模型理解工具的用途以及如何使用它至关重要。未能提供良好的描述可能导致模型在该使用时未使用工具,或者错误地使用它。

  • inputSchema: 工具输入参数的 JSON 模式。如果未提供,将根据方法参数自动生成模式。你可以使用 @ToolParam 注解来提供有关输入参数的附加信息,例如描述或参数是必需的还是可选的。默认情况下,所有输入参数都被视为必需的。有关更多详细信息,请参阅 JSON Schema

ToolMetadata.Builder 允许你构建一个 ToolMetadata 实例,并为工具定义额外的设置:

  • returnDirect: 工具结果是否应直接返回给客户端或传递回模型。有关更多详细信息,请参阅直接返回
Method method = ReflectionUtils.findMethod(DateTimeTools.class, "getCurrentDateTime");
ToolCallback toolCallback = MethodToolCallback.builder()
.toolDefinition(ToolDefinition.builder(method)
.description("Get the current date and time in the user's timezone")
.build())
.toolMethod(method)
.toolObject(new DateTimeTools())
.build();
java

该方法可以是静态的或实例的,并且可以具有任何可见性(publicprotected、包私有或 private)。包含该方法的类可以是顶级类或嵌套类,并且也可以具有任何可见性(只要在您计划实例化它的地方可以访问)。

备注

Spring AI 提供了对工具方法的 AOT 编译的内置支持,只要包含这些方法的类是一个 Spring bean(例如 @Component)。否则,您需要为 GraalVM 编译器提供必要的配置。例如,通过在类上添加 @RegisterReflection(memberCategories = MemberCategory.INVOKE_DECLARED_METHODS) 注解。

你可以为该方法定义任意数量的参数(包括没有参数),并且这些参数可以是大多数类型(基本类型、POJOs、枚举、列表、数组、映射等)。同样,该方法可以返回大多数类型,包括 void。如果该方法返回一个值,返回类型必须是可序列化的类型,因为结果将被序列化并发送回模型。

备注

部分类型不受支持。更多详情请参见方法工具限制

如果该方法是静态的,你可以省略 toolObject() 方法,因为它不是必需的。

class DateTimeTools {

static String getCurrentDateTime() {
return LocalDateTime.now().atZone(LocaleContextHolder.getTimeZone().toZoneId()).toString();
}

}
java
Method method = ReflectionUtils.findMethod(DateTimeTools.class, "getCurrentDateTime");
ToolCallback toolCallback = MethodToolCallback.builder()
.toolDefinition(ToolDefinition.builder(method)
.description("Get the current date and time in the user's timezone")
.build())
.toolMethod(method)
.build();
java

Spring AI 会自动生成方法输入参数的 JSON 模式。该模式用于让模型理解如何调用工具并准备工具请求。@ToolParam 注解可用于提供有关输入参数的额外信息,例如描述或参数是必需还是可选的。默认情况下,所有输入参数都被视为必需的。

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import org.springframework.ai.tool.annotation.ToolParam;

class DateTimeTools {

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);
}

}
java

@ToolParam 注解允许你提供关于工具参数的关键信息:

  • description: 参数的描述,模型可以使用它来更好地理解如何使用该参数。例如,参数应采用什么格式,允许的值是什么,等等。

  • required: 参数是必需的还是可选的。默认情况下,所有参数都被视为必需的。

如果一个参数被注解为 @Nullable,它将被视为可选参数,除非使用 @ToolParam 注解明确标记为必需。

除了 @ToolParam 注解之外,你还可以使用 Swagger 的 @Schema 注解或 Jackson 的 @JsonProperty 注解。更多详情请参见 JSON Schema

ChatClientChatModel 添加工具

在使用编程式规范方法时,您可以将 MethodToolCallback 实例传递给 ChatClienttools() 方法。该工具仅对添加它的特定聊天请求可用。

ToolCallback toolCallback = ...
ChatClient.create(chatModel)
.prompt("What day is tomorrow?")
.tools(toolCallback)
.call()
.content();
java

ChatClient 添加默认工具

在使用编程规范方法时,你可以通过将 MethodToolCallback 实例传递给 defaultTools() 方法来向 ChatClient.Builder 添加默认工具。如果同时提供了默认工具和运行时工具,运行时工具将完全覆盖默认工具。

注意

默认工具在所有通过相同 ChatClient.Builder 构建的 ChatClient 实例执行的所有聊天请求之间共享。这些工具对于在不同聊天请求之间常用的工具非常有用,但如果使用不当,也可能带来风险,可能会在不应该的情况下使这些工具可用。

ChatModel chatModel = ...
ToolCallback toolCallback = ...
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultTools(toolCallback)
.build();
java

ChatModel 添加工具

在使用编程式规范方法时,您可以将 MethodToolCallback 实例传递给用于调用 ChatModelToolCallingChatOptionstoolCallbacks() 方法。该工具仅对添加它的特定聊天请求可用。

ChatModel chatModel = ...
ToolCallback toolCallback = ...
ChatOptions chatOptions = ToolCallingChatOptions.builder()
.toolCallbacks(toolCallback)
.build():
Prompt prompt = new Prompt("What day is tomorrow?", chatOptions);
chatModel.call(prompt);
java

ChatModel 添加默认工具

在使用编程规范方法时,你可以在构建 ChatModel 时通过将 MethodToolCallback 实例传递给用于创建 ChatModelToolCallingChatOptions 实例的 toolCallbacks() 方法来添加默认工具。如果同时提供了默认工具和运行时工具,运行时工具将完全覆盖默认工具。

注意

默认工具在该 ChatModel 实例执行的所有聊天请求之间共享。这些工具对于在不同聊天请求中常用的工具非常有用,但如果不小心使用,也可能带来风险,可能会导致在不应该使用它们时使其可用。

ToolCallback toolCallback = ...
ChatModel chatModel = OllamaChatModel.builder()
.ollamaApi(new OllamaApi())
.defaultOptions(ToolCallingChatOptions.builder()
.toolCallbacks(toolCallback)
.build())
.build();
java

方法工具限制

以下类型目前不支持作为工具方法的参数或返回类型:

  • Optional

  • 异步类型(例如 CompletableFutureFuture

  • 响应式类型(例如 FlowMonoFlux

  • 函数式类型(例如 FunctionSupplierConsumer

功能类型通过基于函数的工具规范方法得到支持。更多详情请参见函数作为工具

函数作为工具

Spring AI 提供了内置支持,用于从函数中指定工具,既可以通过编程方式使用低级别的 FunctionToolCallback 实现,也可以在运行时动态解析为 @Bean

编程规范:FunctionToolCallback

你可以通过编程方式构建一个 FunctionToolCallback,将一个函数式类型(FunctionSupplierConsumerBiFunction)转换为工具。

public class WeatherService implements Function<WeatherRequest, WeatherResponse> {
public WeatherResponse apply(WeatherRequest request) {
return new WeatherResponse(30.0, Unit.C);
}
}

public enum Unit { C, F }
public record WeatherRequest(String location, Unit unit) {}
public record WeatherResponse(double temp, Unit unit) {}
java

FunctionToolCallback.Builder 允许你构建一个 FunctionToolCallback 实例,并提供关于工具的关键信息:

  • name: 工具的名称。AI 模型在调用工具时使用此名称来识别工具。因此,在同一上下文中不允许有两个同名工具。对于特定聊天请求,模型可用的所有工具中名称必须是唯一的。必填项。

  • toolFunction: 表示工具方法的功能对象(FunctionSupplierConsumerBiFunction)。必填项。

  • description: 工具的描述,模型可以使用此描述来理解何时以及如何调用工具。如果未提供,则将使用方法名称作为工具描述。然而,强烈建议提供详细的描述,因为这对于模型理解工具的用途以及如何使用它至关重要。如果未能提供良好的描述,可能会导致模型在该使用时未使用工具,或者错误地使用工具。

  • inputType: 函数输入的类型。必填项。

  • inputSchema: 工具输入参数的 JSON 模式。如果未提供,将根据 inputType 自动生成模式。你可以使用 @ToolParam 注解来提供有关输入参数的附加信息,例如描述或参数是必需还是可选的。默认情况下,所有输入参数都被视为必需的。有关更多详细信息,请参阅 JSON Schema

  • toolMetadata: 定义附加设置的 ToolMetadata 实例,例如是否应将结果直接返回给客户端,以及要使用的结果转换器。你可以使用 ToolMetadata.Builder 类来构建它。

  • toolCallResultConverter: 用于将工具调用的结果转换为 String 对象以发送回 AI 模型的 ToolCallResultConverter 实例。如果未提供,将使用默认转换器(DefaultToolCallResultConverter)。

ToolMetadata.Builder 允许你构建一个 ToolMetadata 实例,并为工具定义额外的设置:

  • returnDirect: 工具结果是否应直接返回给客户端或传递回模型。更多详情请参阅Return Direct
ToolCallback toolCallback = FunctionToolCallback
.builder("currentWeather", new WeatherService())
.description("Get the weather in location")
.inputType(WeatherRequest.class)
.build();
java

函数的输入和输出可以是 Void 或 POJO。输入和输出的 POJO 必须是可序列化的,因为结果将被序列化并发送回模型。函数及其输入和输出类型必须是公共的。

备注

某些类型不受支持。更多详情请参见 函数工具限制

ChatClient 添加工具

在使用编程式规范方法时,您可以将 FunctionToolCallback 实例传递给 ChatClienttools() 方法。该工具仅在添加到特定聊天请求时可用。

ToolCallback toolCallback = ...
ChatClient.create(chatModel)
.prompt("What's the weather like in Copenhagen?")
.tools(toolCallback)
.call()
.content();
java

ChatClient 添加默认工具

在使用编程式规范方法时,你可以通过将 FunctionToolCallback 实例传递给 defaultTools() 方法来为 ChatClient.Builder 添加默认工具。如果同时提供了默认工具和运行时工具,运行时工具将完全覆盖默认工具。

注意

默认工具在所有由同一个 ChatClient.Builder 构建的 ChatClient 实例执行的所有聊天请求之间共享。它们对于在不同聊天请求中常用的工具非常有用,但如果不小心使用,也可能带来风险,可能会导致在不应该使用它们的情况下使其可用。

ChatModel chatModel = ...
ToolCallback toolCallback = ...
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultTools(toolCallback)
.build();
java

ChatModel 添加工具

在使用编程规范方法时,你可以将 FunctionToolCallback 实例传递给 ToolCallingChatOptionstoolCallbacks() 方法。该工具仅在添加到特定的聊天请求时可用。

ChatModel chatModel = ...
ToolCallback toolCallback = ...
ChatOptions chatOptions = ToolCallingChatOptions.builder()
.toolCallbacks(toolCallback)
.build():
Prompt prompt = new Prompt("What's the weather like in Copenhagen?", chatOptions);
chatModel.call(prompt);
java

ChatModel 添加默认工具

在使用编程规范方法时,您可以在构建 ChatModel 时通过将 FunctionToolCallback 实例传递给用于创建 ChatModelToolCallingChatOptions 实例的 toolCallbacks() 方法,来添加默认工具。如果同时提供了默认工具和运行时工具,运行时工具将完全覆盖默认工具。

注意

默认工具会在同一个 ChatModel 实例的所有聊天请求中共享。这些工具对于在不同聊天请求中常用的工具非常有用,但如果不小心使用,也可能会带来风险,因为它们可能会在不该出现的时候被调用。

ToolCallback toolCallback = ...
ChatModel chatModel = OllamaChatModel.builder()
.ollamaApi(new OllamaApi())
.defaultOptions(ToolCallingChatOptions.builder()
.toolCallbacks(toolCallback)
.build())
.build();
java

动态规范:@Bean

与通过编程方式指定工具不同,你可以将工具定义为 Spring bean,并让 Spring AI 在运行时通过 ToolCallbackResolver 接口(通过 SpringBeanToolCallbackResolver 实现)动态解析这些工具。这种方式允许你将任何 FunctionSupplierConsumerBiFunction bean 作为工具使用。bean 的名称将作为工具的名称,并且可以使用 Spring Framework 的 @Description 注解为工具提供描述,模型会利用这些描述来理解何时以及如何调用该工具。如果你没有提供描述,方法名称将作为工具的描述。然而,强烈建议提供详细的描述,因为这对于模型理解工具的用途以及如何使用它至关重要。如果未能提供良好的描述,可能会导致模型在该使用工具时未使用,或者错误地使用它。

@Configuration(proxyBeanMethods = false)
class WeatherTools {

WeatherService weatherService = new WeatherService();

@Bean
@Description("Get the weather in location")
Function<WeatherRequest, WeatherResponse> currentWeather() {
return weatherService;
}

}
java
备注

某些类型不受支持。更多详情请参阅函数工具限制

工具输入参数的 JSON 模式将自动生成。你可以使用 @ToolParam 注解来提供有关输入参数的额外信息,例如描述或参数是否为必需或可选。默认情况下,所有输入参数都被视为必需。有关更多详细信息,请参阅 JSON 模式

record WeatherRequest(@ToolParam(description = "The name of a city or a country") String location, Unit unit) {}
java

这种工具规范方法的缺点是无法保证类型安全,因为工具解析是在运行时完成的。为了缓解这个问题,你可以使用 @Bean 注解显式指定工具名称,并将该值存储在常量中,这样你就可以在聊天请求中使用它,而不是硬编码工具名称。

@Configuration(proxyBeanMethods = false)
class WeatherTools {

public static final String CURRENT_WEATHER_TOOL = "currentWeather";

@Bean(CURRENT_WEATHER_TOOL)
@Description("Get the weather in location")
Function<WeatherRequest, WeatherResponse> currentWeather() {
...
}

}
java

ChatClient 添加工具

在使用动态规范方法时,你可以将工具名称(即函数 bean 名称)传递给 ChatClienttools() 方法。该工具仅对添加它的特定聊天请求可用。

ChatClient.create(chatModel)
.prompt("What's the weather like in Copenhagen?")
.tools("currentWeather")
.call()
.content();
java

ChatClient 添加默认工具

在使用动态规范方法时,你可以通过将工具名称传递给 defaultTools() 方法来向 ChatClient.Builder 添加默认工具。如果同时提供了默认工具和运行时工具,运行时工具将完全覆盖默认工具。

注意

默认工具在所有由同一个 ChatClient.Builder 构建的 ChatClient 实例执行的所有聊天请求之间共享。这些工具对于在不同聊天请求中常用的工具非常有用,但如果使用不当,也可能会带来风险,尤其是在不应该使用它们的情况下使它们可用时。

ChatModel chatModel = ...
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultTools("currentWeather")
.build();
java

ChatModel 添加工具

在使用动态规范方法时,你可以将工具名称传递给 ToolCallingChatOptionstoolNames() 方法,该方法用于调用 ChatModel。该工具仅在添加到特定的聊天请求时才可用。

ChatModel chatModel = ...
ChatOptions chatOptions = ToolCallingChatOptions.builder()
.toolNames("currentWeather")
.build():
Prompt prompt = new Prompt("What's the weather like in Copenhagen?", chatOptions);
chatModel.call(prompt);
java

ChatModel 添加默认工具

在使用动态规范方法时,你可以在构建时通过将工具名称传递给用于创建 ChatModelToolCallingChatOptions 实例的 toolNames() 方法来向 ChatModel 添加默认工具。如果同时提供了默认工具和运行时工具,运行时工具将完全覆盖默认工具。

注意

默认工具由 ChatModel 实例执行的所有聊天请求共享。这些工具对于在不同聊天请求中常用的工具非常有用,但如果不小心使用,也可能带来风险,可能会导致在不应该使用它们的情况下使其可用。

ChatModel chatModel = OllamaChatModel.builder()
.ollamaApi(new OllamaApi())
.defaultOptions(ToolCallingChatOptions.builder()
.toolNames("currentWeather")
.build())
.build();
java

函数工具限制

以下类型目前不支持作为工具函数的输入或输出类型:

  • 基本类型

  • Optional

  • 集合类型(例如 ListMapArraySet

  • 异步类型(例如 CompletableFutureFuture

  • 响应式类型(例如 FlowMonoFlux

原始类型和集合通过基于方法的工具规范方法得到支持。有关更多详细信息,请参见方法作为工具

工具规范

在 Spring AI 中,工具通过 ToolCallback 接口进行建模。在前面的章节中,我们已经了解了如何使用 Spring AI 提供的内置支持从方法和函数定义工具(参见 方法作为工具函数作为工具)。本节将深入探讨工具规范,以及如何自定义和扩展它以支持更多的用例。

工具回调

ToolCallback 接口提供了一种定义工具的方式,该工具可以被 AI 模型调用,包括定义和执行逻辑。当你想要从头开始定义一个工具时,这是需要实现的主要接口。例如,你可以从 MCP 客户端(使用 Model Context Protocol)或 ChatClient(用于构建模块化代理应用程序)定义一个 ToolCallback

该接口提供了以下方法:

public interface ToolCallback {

/**
* Definition used by the AI model to determine when and how to call the tool.
*/
ToolDefinition getToolDefinition();

/**
* Metadata providing additional information on how to handle the tool.
*/
ToolMetadata getToolMetadata();

/**
* Execute tool with the given input and return the result to send back to the AI model.
*/
String call(String toolInput);

/**
* Execute tool with the given input and context, and return the result to send back to the AI model.
*/
String call(String toolInput, ToolContext tooContext);

}
java

Spring AI 提供了工具方法(MethodToolCallback)和工具函数(FunctionToolCallback)的内置实现。

工具定义

ToolDefinition 接口为 AI 模型提供了了解工具可用性所需的必要信息,包括工具名称、描述和输入模式。每个 ToolCallback 实现都必须提供一个 ToolDefinition 实例来定义工具。

该接口提供了以下方法:

public interface ToolDefinition {

/**
* The tool name. Unique within the tool set provided to a model.
*/
String name();

/**
* The tool description, used by the AI model to determine what the tool does.
*/
String description();

/**
* The schema of the parameters used to call the tool.
*/
String inputSchema();

}
java
备注

有关输入模式的更多详细信息,请参见 JSON Schema

ToolDefinition.Builder 允许你使用默认实现(DefaultToolDefinition)来构建一个 ToolDefinition 实例。

ToolDefinition toolDefinition = ToolDefinition.builder()
.name("currentWeather")
.description("Get the weather in location")
.inputSchema("""
{
"type": "object",
"properties": {
"location": {
"type": "string"
},
"unit": {
"type": "string",
"enum": ["C", "F"]
}
},
"required": ["location", "unit"]
}
""")
.build();
java

方法工具定义

在从方法构建工具时,ToolDefinition 会自动为你生成。如果你更倾向于自己生成 ToolDefinition,可以使用这个便捷的构建器。

Method method = ReflectionUtils.findMethod(DateTimeTools.class, "getCurrentDateTime");
ToolDefinition toolDefinition = ToolDefinition.from(method);
java

从方法生成的 ToolDefinition 包括方法名称作为工具名称、方法名称作为工具描述以及方法输入参数的 JSON 模式。如果方法使用 @Tool 注解,工具名称和描述将从注解中获取(如果已设置)。

备注

更多详情请参见方法作为工具

如果你更愿意显式地提供部分或全部属性,你可以使用 ToolDefinition.Builder 来构建一个自定义的 ToolDefinition 实例。

Method method = ReflectionUtils.findMethod(DateTimeTools.class, "getCurrentDateTime");
ToolDefinition toolDefinition = ToolDefinition.builder(method)
.name("currentDateTime")
.description("Get the current date and time in the user's timezone")
.inputSchema(JsonSchemaGenerator.generateForMethodInput(method))
.build();
java

函数工具定义

在从函数构建工具时,ToolDefinition 会自动生成。当你使用 FunctionToolCallback.Builder 构建 FunctionToolCallback 实例时,你可以提供工具名称、描述和输入模式,这些信息将用于生成 ToolDefinition。更多详细信息,请参见 Functions as Tools

JSON Schema

当向 AI 模型提供工具时,模型需要知道调用工具时输入类型的模式(schema)。该模式用于理解如何调用工具以及准备工具请求。Spring AI 通过 JsonSchemaGenerator 类提供了内置支持,用于生成工具输入类型的 JSON Schema。该模式作为 ToolDefinition 的一部分提供。

备注

有关 ToolDefinition 的更多详细信息以及如何将输入模式传递给它,请参阅 工具定义

JsonSchemaGenerator 类在底层用于生成方法或函数输入参数的 JSON 模式,使用 Methods as ToolsFunctions as Tools 中描述的任何策略。JSON 模式生成逻辑支持一系列注解,你可以在方法和函数的输入参数上使用这些注解来自定义生成的模式。

本节介绍了在为工具的输入参数生成 JSON schema 时可以自定义的两个主要选项:描述和必填状态。

描述

除了为工具本身提供描述外,您还可以为工具的输入参数提供描述。这些描述可用于提供有关输入参数的关键信息,例如参数应采用什么格式、允许哪些值等。这有助于帮助模型理解输入模式以及如何使用它。Spring AI 提供了内置支持,可以使用以下注解之一生成输入参数的描述:

  • @ToolParam(description = "…​") 来自 Spring AI

  • @JsonClassDescription(description = "…​") 来自 Jackson

  • @JsonPropertyDescription(description = "…​") 来自 Jackson

  • @Schema(description = "…​") 来自 Swagger

这种方法适用于方法和函数,并且你可以递归地将其用于嵌套类型。

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.context.i18n.LocaleContextHolder;

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);
}

}
java

必选/可选

默认情况下,每个输入参数都被认为是必需的,这迫使 AI 模型在调用工具时为其提供一个值。但是,您可以通过使用以下注释之一(按优先级顺序)使输入参数变为可选:

  • @ToolParam(required = false) 来自 Spring AI

  • @JsonProperty(required = false) 来自 Jackson

  • @Schema(required = false) 来自 Swagger

  • @Nullable 来自 Spring Framework

这种方法适用于方法和函数,并且你可以递归地将其用于嵌套类型。

class CustomerTools {

@Tool(description = "Update customer information")
void updateCustomerInfo(Long id, String name, @ToolParam(required = false) String email) {
System.out.println("Updated info for customer with id: " + id);
}

}
java
注意

为输入参数定义正确的必填状态对于减少幻觉风险并确保模型在调用工具时提供正确的输入至关重要。在上一个示例中,email 参数是可选的,这意味着模型可以在调用工具时不提供该参数的值。如果该参数是必填的,模型在调用工具时必须为其提供一个值。如果没有值存在,模型可能会编造一个值,从而导致幻觉。

结果转换

工具调用的结果通过 ToolCallResultConverter 进行序列化,然后发送回 AI 模型。ToolCallResultConverter 接口提供了一种将工具调用结果转换为 String 对象的方式。

接口提供以下方法:

@FunctionalInterface
public interface ToolCallResultConverter {

/**
* Given an Object returned by a tool, convert it to a String compatible with the
* given class type.
*/
String convert(@Nullable Object result, @Nullable Type returnType);

}
java

结果必须是一个可序列化的类型。默认情况下,结果会使用 Jackson (DefaultToolCallResultConverter) 序列化为 JSON,但你可以通过提供自己的 ToolCallResultConverter 实现来自定义序列化过程。

Spring AI 在方法和函数工具中都依赖于 ToolCallResultConverter

方法工具调用结果转换

在使用声明式方法从方法构建工具时,可以通过设置 @Tool 注解的 resultConverter() 属性来为该工具提供自定义的 ToolCallResultConverter

class CustomerTools {

@Tool(description = "Retrieve customer information", resultConverter = CustomToolCallResultConverter.class)
Customer getCustomerInfo(Long id) {
return customerRepository.findById(id);
}

}
java

如果使用编程方法,你可以通过设置 MethodToolCallback.BuilderresultConverter() 属性来为工具提供一个自定义的 ToolCallResultConverter

更多详情请参见方法作为工具

函数工具调用结果转换

在使用编程方法从函数构建工具时,你可以通过设置 FunctionToolCallback.BuilderresultConverter() 属性来提供一个自定义的 ToolCallResultConverter 以用于该工具。

有关更多详细信息,请参阅函数作为工具

工具上下文

Spring AI 支持通过 ToolContext API 传递额外的上下文信息到工具中。此功能允许您提供用户提供的额外数据,这些数据可以与 AI 模型传递的工具参数一起在工具执行过程中使用。

向工具提供额外的上下文信息

class CustomerTools {

@Tool(description = "Retrieve customer information")
Customer getCustomerInfo(Long id, ToolContext toolContext) {
return customerRepository.findById(id, toolContext.get("tenantId"));
}

}
java

ToolContext 由用户在调用 ChatClient 时提供的数据填充。

ChatModel chatModel = ...

String response = ChatClient.create(chatModel)
.prompt("Tell me more about the customer with ID 42")
.tools(new CustomerTools())
.toolContext(Map.of("tenantId", "acme"))
.call()
.content();

System.out.println(response);
java
备注

ToolContext 中提供的任何数据都不会发送给 AI 模型。

同样地,在直接调用 ChatModel 时,你也可以定义工具上下文数据。

ChatModel chatModel = ...
ToolCallback[] customerTools = ToolCallbacks.from(new CustomerTools());
ChatOptions chatOptions = ToolCallingChatOptions.builder()
.toolCallbacks(customerTools)
.toolContext(Map.of("tenantId", "acme"))
.build():
Prompt prompt = new Prompt("Tell me more about the customer with ID 42", chatOptions);
chatModel.call(prompt);
java

如果 toolContext 选项同时在默认选项和运行时选项中设置,最终的 ToolContext 将是两者的合并结果,其中运行时选项优先于默认选项。

返回直接

默认情况下,工具调用的结果会作为响应发送回模型。然后,模型可以使用该结果继续对话。

在某些情况下,您可能更希望直接将结果返回给调用者,而不是将其发送回模型。例如,如果您构建了一个依赖于 RAG 工具的代理,您可能希望直接将结果返回给调用者,而不是将其发送回模型进行不必要的后处理。或者,您可能有一些工具应该结束代理的推理循环。

每个 ToolCallback 实现都可以定义工具调用的结果是直接返回给调用者还是发送回模型。默认情况下,结果会发送回模型。但你可以为每个工具更改此行为。

ToolCallingManager 负责管理工具执行的生命周期,并处理与工具关联的 returnDirect 属性。如果该属性设置为 true,则工具调用的结果将直接返回给调用者。否则,结果将返回给模型。

备注

如果同时请求多个工具调用,必须将 returnDirect 属性设置为 true,以便所有工具将结果直接返回给调用者。否则,结果将发送回模型。

将工具调用结果直接返回给调用者

  1. 当我们希望将工具提供给模型使用时,我们需要在聊天请求中包含该工具的定义。如果我们希望工具执行的结果直接返回给调用者,我们需要将 returnDirect 属性设置为 true

  2. 当模型决定调用某个工具时,它会发送一个响应,其中包含工具名称以及根据定义的模式建模的输入参数。

  3. 应用程序负责使用工具名称来识别工具,并使用提供的输入参数执行该工具。

  4. 工具调用的结果由应用程序处理。

  5. 应用程序将工具调用的结果直接发送给调用者,而不是将其发送回模型。

方法直接返回

在使用声明式方法构建工具时,您可以通过将 @Tool 注解的 returnDirect 属性设置为 true,来标记一个工具直接将结果返回给调用者。

class CustomerTools {

@Tool(description = "Retrieve customer information", returnDirect = true)
Customer getCustomerInfo(Long id) {
return customerRepository.findById(id);
}

}
java

如果使用编程方法,你可以通过 ToolMetadata 接口设置 returnDirect 属性,并将其传递给 MethodToolCallback.Builder

ToolMetadata toolMetadata = ToolMetadata.builder()
.returnDirect(true)
.build();
java

更多详情请参见作为工具的方法

函数直接返回

在使用编程方法从函数构建工具时,可以通过 ToolMetadata 接口设置 returnDirect 属性,并将其传递给 FunctionToolCallback.Builder

ToolMetadata toolMetadata = ToolMetadata.builder()
.returnDirect(true)
.build();
java

更多详情请参阅函数作为工具

工具执行

工具执行是通过提供的输入参数调用工具并返回结果的过程。工具执行由 ToolCallingManager 接口处理,该接口负责管理工具执行的生命周期。

public interface ToolCallingManager {

/**
* Resolve the tool definitions from the model's tool calling options.
*/
List<ToolDefinition> resolveToolDefinitions(ToolCallingChatOptions chatOptions);

/**
* Execute the tool calls requested by the model.
*/
ToolExecutionResult executeToolCalls(Prompt prompt, ChatResponse chatResponse);

}
java

如果你正在使用任何 Spring AI Spring Boot Starters,DefaultToolCallingManagerToolCallingManager 接口的自动配置实现。你可以通过提供自己的 ToolCallingManager bean 来自定义工具执行行为。

@Bean
ToolCallingManager toolCallingManager() {
return ToolCallingManager.builder().build();
}
java

默认情况下,Spring AI 在每个 ChatModel 实现中透明地为你管理工具执行的生命周期。但你也可以选择退出这种行为,自己控制工具的执行。本节将描述这两种场景。

框架控制的工具执行

在使用默认行为时,Spring AI 会自动拦截来自模型的任何工具调用请求,调用工具并将结果返回给模型。所有这些操作都是由每个 ChatModel 实现通过 ToolCallingManager 透明地为你完成的。

框架控制的工具执行生命周期

  1. 当我们希望让模型能够使用某个工具时,我们会在聊天请求(Prompt)中包含其定义,并调用 ChatModel API,该 API 将请求发送给 AI 模型。

  2. 当模型决定调用某个工具时,它会发送一个响应(ChatResponse),其中包含工具名称和根据定义的 schema 建模的输入参数。

  3. ChatModel 将工具调用请求发送给 ToolCallingManager API。

  4. ToolCallingManager 负责识别要调用的工具并使用提供的输入参数执行它。

  5. 工具调用的结果返回给 ToolCallingManager

  6. ToolCallingManager 将工具执行结果返回给 ChatModel

  7. ChatModel 将工具执行结果发送回 AI 模型(ToolResponseMessage)。

  8. AI 模型使用工具调用结果作为额外的上下文生成最终响应,并通过 ChatClient 将其发送回调用者(ChatResponse)。


注意: 以上内容中,PromptChatModelToolCallingManagerChatResponseToolResponseMessageChatClient 等专有名词未翻译,保留了英文原文。

注意

目前,与模型之间关于工具执行的内部消息不会暴露给用户。如果您需要访问这些消息,您应该使用用户控制的工具执行方法。

用户控制的工具执行

在某些情况下,您可能更希望自己控制工具执行的生命周期。您可以通过将 ToolCallingChatOptionsinternalToolExecutionEnabled 属性设置为 false 来实现这一点。当您使用此选项调用 ChatModel 时,工具执行将被委托给调用者,从而使您能够完全控制工具执行的生命周期。您需要负责检查 ChatResponse 中的工具调用,并使用 ToolCallingManager 来执行它们。

以下示例演示了用户控制工具执行方法的最小实现:

ChatModel chatModel = ...
ToolCallingManager toolCallingManager = ToolCallingManager.builder().build();

ChatOptions chatOptions = ToolCallingChatOptions.builder()
.toolCallbacks(new CustomerTools())
.internalToolExecutionEnabled(false)
.build();
Prompt prompt = new Prompt("Tell me more about the customer with ID 42", chatOptions);

ChatResponse chatResponse = chatModel.call(prompt);

while (chatResponse.hasToolCalls()) {
ToolExecutionResult toolExecutionResult = toolCallingManager.executeToolCalls(prompt, chatResponse);

prompt = new Prompt(toolExecutionResult.conversationHistory(), chatOptions);

chatResponse = chatModel.call(prompt);
}

System.out.println(chatResponse.getResult().getOutput().getText());
java
备注

在选择用户控制的工具执行方法时,我们建议使用 ToolCallingManager 来管理工具调用操作。这样,你可以受益于 Spring AI 提供的对工具执行的内置支持。然而,这并不妨碍你实现自己的工具执行逻辑。

异常处理

当一个工具调用失败时,异常会以 ToolExecutionException 的形式传播,可以通过捕获该异常来处理错误。可以使用 ToolExecutionExceptionProcessor 来处理 ToolExecutionException,处理结果有两种:要么生成一个错误消息返回给 AI 模型,要么抛出一个异常由调用者处理。

@FunctionalInterface
public interface ToolExecutionExceptionProcessor {

/**
* Convert an exception thrown by a tool to a String that can be sent back to the AI
* model or throw an exception to be handled by the caller.
*/
String process(ToolExecutionException exception);

}
java

如果你正在使用任何 Spring AI Spring Boot Starters,DefaultToolExecutionExceptionProcessorToolExecutionExceptionProcessor 接口的自动配置实现。默认情况下,错误消息会发送回模型。DefaultToolExecutionExceptionProcessor 的构造函数允许你将 alwaysThrow 属性设置为 truefalse。如果设置为 true,将会抛出异常,而不是将错误消息发送回模型。

@Bean
ToolExecutionExceptionProcessor toolExecutionExceptionProcessor() {
return new DefaultToolExecutionExceptionProcessor(true);
}
java
备注

如果你定义了自己的 ToolCallback 实现,请确保在 call() 方法中的工具执行逻辑发生错误时抛出 ToolExecutionException

ToolExecutionExceptionProcessor 由默认的 ToolCallingManagerDefaultToolCallingManager)在内部使用,用于处理工具执行期间的异常。有关工具执行生命周期的更多详细信息,请参阅 工具执行

工具解析

将工具传递给模型的主要方法是在调用 ChatClientChatModel 时提供 ToolCallback,使用 Methods as ToolsFunctions as Tools 中描述的策略之一。

然而,Spring AI 也支持在运行时使用 ToolCallbackResolver 接口动态解析工具。

public interface ToolCallbackResolver {

/**
* Resolve the {@link ToolCallback} for the given tool name.
*/
@Nullable
ToolCallback resolve(String toolName);

}
java

使用这种方法时:

  • 在客户端,你向 ChatClientChatModel 提供工具名称,而不是 ToolCallback

  • 在服务器端,ToolCallbackResolver 实现负责将工具名称解析为相应的 ToolCallback 实例。

默认情况下,Spring AI 依赖于一个 DelegatingToolCallbackResolver,它将工具解析委托给一系列 ToolCallbackResolver 实例:

  • SpringBeanToolCallbackResolver 从类型为 FunctionSupplierConsumerBiFunction 的 Spring bean 中解析工具。更多详情请参阅 动态规范:@Bean

  • StaticToolCallbackResolverToolCallback 实例的静态列表中解析工具。当使用 Spring Boot 自动配置时,此解析器会自动配置应用程序上下文中定义的所有类型为 ToolCallback 的 bean。

如果你依赖 Spring Boot 的自动配置,你可以通过提供一个自定义的 ToolCallbackResolver bean 来定制解析逻辑。

@Bean
ToolCallbackResolver toolCallbackResolver(List<FunctionCallback> toolCallbacks) {
StaticToolCallbackResolver staticToolCallbackResolver = new StaticToolCallbackResolver(toolCallbacks);
return new DelegatingToolCallbackResolver(List.of(staticToolCallbackResolver));
}
java

ToolCallbackResolverToolCallingManager 在内部使用,用于在运行时动态解析工具,支持框架控制的工具执行用户控制的工具执行

可观测性

工具调用的监测功能正在开发中。目前,您可以使用日志功能来跟踪工具调用的操作。

日志记录

工具调用功能的所有主要操作都在 DEBUG 级别进行日志记录。您可以通过将 org.springframework.ai 包的日志级别设置为 DEBUG 来启用日志记录。