工具调用

工具调用(也称为函数调用)是 AI 应用程序中的一种常见模式,允许模型与一组 API 或工具进行交互,从而增强其功能。spring-doc.cadn.net.cn

工具主要用于:spring-doc.cadn.net.cn

  • 信息检索。此类别中的工具可用于从外部源(如数据库、Web 服务、文件系统或 Web 搜索引擎)检索信息。目标是增强模型的知识,使其能够回答其他方式无法回答的问题。因此,它们可用于检索增强生成 (RAG) 方案。例如,工具可用于检索给定位置的当前天气、检索最新的新闻文章或查询数据库以获取特定记录。spring-doc.cadn.net.cn

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

尽管我们通常将工具调用称为模型功能,但实际上由客户端应用程序提供工具调用逻辑。模型只能请求工具调用并提供输入参数,而应用程序负责从输入参数执行工具调用并返回结果。该模型永远无法访问作为工具提供的任何 API,这是一个关键的安全考虑因素。spring-doc.cadn.net.cn

Spring AI 提供了方便的 API 来定义工具、解决来自模型的工具调用请求以及执行工具调用。以下部分概述了 Spring AI 中的工具调用功能。spring-doc.cadn.net.cn

检查 聊天模型比较 以查看哪些 AI 模型支持工具调用调用。

快速开始

让我们看看如何在 Spring AI 中开始使用工具调用。我们将实现两个简单的工具:一个用于信息检索,一个用于采取行动。信息检索工具将用于获取用户所在时区的当前日期和时间。作工具将用于设置指定时间的闹钟。spring-doc.cadn.net.cn

信息检索

AI 模型无法访问实时信息。模型无法回答任何假设了解信息(如当前日期或天气预报)的问题。但是,我们可以提供一个可以检索此信息的工具,并让模型在需要访问实时信息时调用此工具。spring-doc.cadn.net.cn

让我们实现一个工具,以DateTimeTools类。该工具将不接受任何参数。这LocaleContextHolder从Spring Framework可以提供用户的时区。该工具将被定义为一个带有@Tool.为了帮助模型了解是否以及何时调用此工具,我们将提供这些工具的作用的详细说明。spring-doc.cadn.net.cn

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

}

接下来,让我们使该工具可用于模型。在此示例中,我们将使用ChatClient以与模型交互。我们将通过传递DateTimeTools通过tools()方法。当模型需要知道当前日期和时间时,它将请求调用该工具。在内部,ChatClient将调用该工具并将结果返回给模型,然后模型将使用工具调用结果生成对原始问题的最终响应。spring-doc.cadn.net.cn

ChatModel chatModel = ...

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

System.out.println(response);

输出将如下所示:spring-doc.cadn.net.cn

Tomorrow is 2015-10-21.

您可以再次尝试询问相同的问题。这一次,不要向模型提供工具。输出将如下所示:spring-doc.cadn.net.cn

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.

如果没有该工具,模型就不知道如何回答问题,因为它无法确定当前日期和时间。spring-doc.cadn.net.cn

采取行动

AI 模型可用于生成实现某些目标的计划。例如,模型可以生成预订丹麦旅行的计划。但是,该模型无法执行该计划。这就是工具的用武之地:它们可用于执行模型生成的计划。spring-doc.cadn.net.cn

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

我们将新工具添加到相同的DateTimeTools类。新工具将采用单个参数,即 ISO-8601 格式的时间。然后,该工具将向控制台打印一条消息,指示已为给定时间设置警报。与以前一样,该工具被定义为一个带有@Tool,我们还使用它来提供详细说明,以帮助模型了解何时以及如何使用该工具。spring-doc.cadn.net.cn

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

}

接下来,让我们使这两个工具都可用于模型。我们将使用ChatClient以与模型交互。我们将通过传递DateTimeTools通过tools()方法。当我们要求在从 10 分钟后设置闹钟时,模型首先需要知道当前日期和时间。然后,它将使用当前日期和时间来计算闹钟时间。最后,它将使用警报工具设置警报。在内部,ChatClient将处理来自模型的任何工具调用请求,并将任何工具调用执行结果发送回给它,以便模型可以生成最终响应。spring-doc.cadn.net.cn

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-doc.cadn.net.cn

概述

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

工具调用的主要作序列
  1. 当我们想让某个工具可供模型使用时,我们会在 chat 请求中包含其定义。每个工具定义都包含输入参数的名称、描述和方案。spring-doc.cadn.net.cn

  2. 当模型决定调用工具时,它会发送一个响应,其中包含工具名称和输入参数,这些参数在定义的方案之后建模。spring-doc.cadn.net.cn

  3. 应用程序负责使用工具名称来识别并使用提供的输入参数执行工具。spring-doc.cadn.net.cn

  4. 工具调用的结果由应用程序处理。spring-doc.cadn.net.cn

  5. 应用程序将工具调用结果发送回模型。spring-doc.cadn.net.cn

  6. 该模型使用 tool call result 作为附加上下文生成最终响应。spring-doc.cadn.net.cn

工具是工具调用的构建块,它们由ToolCallback接口。Spring AI 提供了对指定ToolCallback(s) 从方法和函数中导出,但您始终可以定义自己的ToolCallback实现以支持更多用例。spring-doc.cadn.net.cn

ChatModelimplementations 透明地将工具调用请求分派给相应的ToolCallback实现,并将工具调用结果发送回模型,最终生成最终响应。他们使用ToolCallingManager接口,该接口负责管理工具执行生命周期。spring-doc.cadn.net.cn

ChatClientChatModel接受ToolCallback对象使工具可用于模型,并使用ToolCallingManager最终会处决他们。spring-doc.cadn.net.cn

除了传递ToolCallback对象,您还可以传递工具名称列表,该列表将使用ToolCallbackResolver接口。spring-doc.cadn.net.cn

以下部分将详细介绍所有这些概念和 API,包括如何自定义和扩展它们以支持更多用例。spring-doc.cadn.net.cn

方法即工具

Spring AI 提供了对指定工具(即ToolCallback(s)) 从方法中:spring-doc.cadn.net.cn

声明性规范:@Tool

您可以通过使用@Tool.spring-doc.cadn.net.cn

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

}

@Toolannotation 允许您提供有关该工具的关键信息:spring-doc.cadn.net.cn

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

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

  • returnDirect:工具结果是应直接返回给客户端还是传递回模型。有关更多详细信息,请参阅直接返回spring-doc.cadn.net.cn

  • resultConverter:这ToolCallResultConverter用于将工具调用的结果转换为String object发送回 AI 模型。有关更多详细信息,请参阅 Result Conversionspring-doc.cadn.net.cn

该方法可以是 static 或 instance,并且可以具有任何可见性(public、protected、package-private 或 private)。包含该方法的类可以是顶级类或嵌套类,并且还可以具有任何可见性(只要它位于您计划实例化的位置可访问)。spring-doc.cadn.net.cn

Spring AI 为 AOT 编译提供了内置支持@Tool-annotated 方法,只要包含这些方法的类是 Spring Bean(例如@Component).否则,您需要向 GraalVM 编译器提供必要的配置。例如,通过使用@RegisterReflection(memberCategories = MemberCategory.INVOKE_DECLARED_METHODS).

您可以为大多数类型(基元、POJO、枚举、列表、数组、映射等)的方法定义任意数量的参数(包括无参数)。同样,该方法可以返回大多数类型,包括void.如果该方法返回一个值,则返回类型必须是可序列化类型,因为结果将被序列化并发送回模型。spring-doc.cadn.net.cn

某些类型不受支持。有关更多详细信息,请参阅方法工具限制

Spring AI 将为@Tool-annotated 方法。模型使用架构来了解如何调用工具和准备工具请求。这@ToolParamannotation 可用于提供有关输入参数的其他信息,例如描述或参数是必需的还是可选的。默认情况下,所有输入参数都被视为必需参数。spring-doc.cadn.net.cn

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注记允许您提供有关工具参数的关键信息:spring-doc.cadn.net.cn

  • description:参数的描述,模型可以使用该描述来更好地了解如何使用它。例如,参数应采用什么格式、允许哪些值等。spring-doc.cadn.net.cn

  • required:参数是必需的还是可选的。默认情况下,所有参数都被视为必需参数。spring-doc.cadn.net.cn

如果参数注释为@Nullable,它将被视为可选,除非使用@ToolParam注解。spring-doc.cadn.net.cn

除了@ToolParam注解,您还可以使用@Schemaannotation 或@JsonProperty来自Jackson。有关更多详细信息,请参阅 JSON 架构spring-doc.cadn.net.cn

将工具添加到ChatClient

使用声明式规范方法时,可以将工具类实例传递给tools()方法。ChatClient.此类工具仅适用于它们被添加到的特定聊天请求。spring-doc.cadn.net.cn

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

在后台,ChatClient将生成一个ToolCallback从每个@Tool-annotated 方法,并将它们传递给模型。如果您希望生成ToolCallback(s) 的ToolCallbacksUtility 类。spring-doc.cadn.net.cn

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

添加默认工具ChatClient

使用声明式规范方法时,可以将默认工具添加到ChatClient.Builder通过将 Tool 类实例传递给defaultTools()方法。 如果同时提供了 default 和 runtime 工具,则 runtime 工具将完全覆盖 default 工具。spring-doc.cadn.net.cn

默认工具在所有ChatClient从同一ChatClient.Builder.它们对于在不同聊天请求中常用的工具很有用,但如果不小心使用,它们也可能很危险,有可能在不该使用时提供它们。
ChatModel chatModel = ...
ChatClient chatClient = ChatClient.builder(chatModel)
    .defaultTools(new DateTimeTools())
    .build();

将工具添加到ChatModel

使用声明式规范方法时,可以将工具类实例传递给toolCallbacks()方法ToolCallingChatOptions您用来调用ChatModel.此类工具仅适用于它们被添加到的特定聊天请求。spring-doc.cadn.net.cn

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

添加默认工具ChatModel

当使用声明式规范方法时,你可以将默认工具添加到ChatModel在构造时,通过将 Tool 类实例传递给toolCallbacks()方法ToolCallingChatOptions实例用于创建ChatModel. 如果同时提供了 default 和 runtime 工具,则 runtime 工具将完全覆盖 default 工具。spring-doc.cadn.net.cn

默认工具在该执行的所有聊天请求之间共享ChatModel实例。它们对于在不同聊天请求中常用的工具很有用,但如果不小心使用,它们也可能很危险,有可能在不该使用时提供它们。
ToolCallback[] dateTimeTools = ToolCallbacks.from(new DateTimeTools());
ChatModel chatModel = OllamaChatModel.builder()
    .ollamaApi(OllamaApi.builder().build())
    .defaultOptions(ToolCallingChatOptions.builder()
            .toolCallbacks(dateTimeTools)
            .build())
    .build();

程序化规范:MethodToolCallback

您可以通过构建MethodToolCallback编程。spring-doc.cadn.net.cn

class DateTimeTools {

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

}

MethodToolCallback.Builder允许您构建一个MethodToolCallback实例并提供有关该工具的关键信息:spring-doc.cadn.net.cn

  • toolDefinition:这ToolDefinition定义工具名称、描述和输入架构的实例。您可以使用ToolDefinition.Builder类。必需。spring-doc.cadn.net.cn

  • toolMetadata:这ToolMetadata实例,该实例定义其他设置,例如是否应将结果直接返回到客户端,以及要使用的结果转换器。您可以使用ToolMetadata.Builder类。spring-doc.cadn.net.cn

  • toolMethod:这Method表示 Tool 方法的实例。必填。spring-doc.cadn.net.cn

  • toolObject:包含 tool 方法的对象实例。如果 method 是 static,则可以省略该参数。spring-doc.cadn.net.cn

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

ToolDefinition.Builder允许您构建一个ToolDefinition实例并定义工具名称、描述和输入架构:spring-doc.cadn.net.cn

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

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

  • inputSchema:工具输入参数的 JSON 架构。如果未提供,将根据方法参数自动生成 schema。您可以使用@ToolParamannotation 提供有关输入参数的其他信息,例如描述或参数是 required 还是 optional。默认情况下,所有输入参数都被视为必需参数。有关更多详细信息,请参阅 JSON 架构spring-doc.cadn.net.cn

ToolMetadata.Builder允许您构建一个ToolMetadata实例并定义该工具的其他设置:spring-doc.cadn.net.cn

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

该方法可以是 static 或 instance,并且可以具有任何可见性(public、protected、package-private 或 private)。包含该方法的类可以是顶级类或嵌套类,并且还可以具有任何可见性(只要它位于您计划实例化的位置可访问)。spring-doc.cadn.net.cn

Spring AI 为工具方法的 AOT 编译提供内置支持,只要包含这些方法的类是 Spring bean(例如@Component).否则,您需要向 GraalVM 编译器提供必要的配置。例如,通过使用@RegisterReflection(memberCategories = MemberCategory.INVOKE_DECLARED_METHODS).

您可以为大多数类型(基元、POJO、枚举、列表、数组、映射等)的方法定义任意数量的参数(包括无参数)。同样,该方法可以返回大多数类型,包括void.如果该方法返回一个值,则返回类型必须是可序列化类型,因为结果将被序列化并发送回模型。spring-doc.cadn.net.cn

某些类型不受支持。有关更多详细信息,请参阅方法工具限制

如果方法是静态的,则可以省略toolObject()方法,因为它不是必需的。spring-doc.cadn.net.cn

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(ToolDefinition.builder(method)
            .description("Get the current date and time in the user's timezone")
            .build())
    .toolMethod(method)
    .build();

Spring AI 将自动为方法的输入参数生成 JSON 模式。模型使用架构来了解如何调用工具和准备工具请求。这@ToolParamannotation 可用于提供有关输入参数的其他信息,例如描述或参数是必需的还是可选的。默认情况下,所有输入参数都被视为必需参数。spring-doc.cadn.net.cn

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

}

@ToolParam注记允许您提供有关工具参数的关键信息:spring-doc.cadn.net.cn

  • description:参数的描述,模型可以使用该描述来更好地了解如何使用它。例如,参数应采用什么格式、允许哪些值等。spring-doc.cadn.net.cn

  • required:参数是必需的还是可选的。默认情况下,所有参数都被视为必需参数。spring-doc.cadn.net.cn

如果参数注释为@Nullable,它将被视为可选,除非使用@ToolParam注解。spring-doc.cadn.net.cn

除了@ToolParam注解,您还可以使用@Schemaannotation 或@JsonProperty来自Jackson。有关更多详细信息,请参阅 JSON 架构spring-doc.cadn.net.cn

将工具添加到ChatClientChatModel

使用编程规范方法时,您可以将MethodToolCallback实例添加到tools()method 的ChatClient. 该工具仅适用于它所添加到的特定聊天请求。spring-doc.cadn.net.cn

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

添加默认工具ChatClient

使用编程规范方法时,您可以将默认工具添加到ChatClient.Builder通过传递MethodToolCallback实例添加到defaultTools()方法。 如果同时提供了 default 和 runtime 工具,则 runtime 工具将完全覆盖 default 工具。spring-doc.cadn.net.cn

默认工具在所有ChatClient从同一ChatClient.Builder.它们对于在不同聊天请求中常用的工具很有用,但如果不小心使用,它们也可能很危险,有可能在不该使用时提供它们。
ChatModel chatModel = ...
ToolCallback toolCallback = ...
ChatClient chatClient = ChatClient.builder(chatModel)
    .defaultTools(toolCallback)
    .build();

将工具添加到ChatModel

使用编程规范方法时,您可以将MethodToolCallback实例添加到toolCallbacks()方法ToolCallingChatOptions您用来调用ChatModel.该工具仅适用于它所添加到的特定聊天请求。spring-doc.cadn.net.cn

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

添加默认工具ChatModel

使用编程规范方法时,您可以将默认工具添加到ChatModel在构建时,通过传递MethodToolCallback实例添加到toolCallbacks()方法ToolCallingChatOptions实例用于创建ChatModel. 如果同时提供了 default 和 runtime 工具,则 runtime 工具将完全覆盖 default 工具。spring-doc.cadn.net.cn

默认工具在该执行的所有聊天请求之间共享ChatModel实例。它们对于在不同聊天请求中常用的工具很有用,但如果不小心使用,它们也可能很危险,有可能在不该使用时提供它们。
ToolCallback toolCallback = ...
ChatModel chatModel = OllamaChatModel.builder()
    .ollamaApi(OllamaApi.builder().build())
    .defaultOptions(ToolCallingChatOptions.builder()
            .toolCallbacks(toolCallback)
            .build())
    .build();

方法工具限制

目前不支持将以下类型用作工具的方法的参数或返回类型:spring-doc.cadn.net.cn

使用基于函数的工具规范方法支持函数类型。有关更多详细信息,请参阅函数即工具spring-doc.cadn.net.cn

作为工具的功能

Spring AI 为从函数指定工具提供了内置支持,可以使用低级FunctionToolCallback实现或动态为@Bean(s) 在运行时解决。spring-doc.cadn.net.cn

程序化规范:FunctionToolCallback

您可以将函数式类型 (Function,Supplier,ConsumerBiFunction) 导入到工具中,方法是构建FunctionToolCallback编程。spring-doc.cadn.net.cn

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

FunctionToolCallback.Builder允许您构建一个FunctionToolCallback实例并提供有关该工具的关键信息:spring-doc.cadn.net.cn

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

  • toolFunction:表示工具方法 (Function,Supplier,ConsumerBiFunction).必填。spring-doc.cadn.net.cn

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

  • inputType:函数输入的类型。必填。spring-doc.cadn.net.cn

  • inputSchema:工具输入参数的 JSON 架构。如果未提供,则将根据inputType.您可以使用@ToolParamannotation 提供有关输入参数的其他信息,例如描述或参数是 required 还是 optional。默认情况下,所有输入参数都被视为必需参数。有关更多详细信息,请参阅 JSON 架构spring-doc.cadn.net.cn

  • toolMetadata:这ToolMetadata实例,该实例定义其他设置,例如是否应将结果直接返回到客户端,以及要使用的结果转换器。您可以使用ToolMetadata.Builder类。spring-doc.cadn.net.cn

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

ToolMetadata.Builder允许您构建一个ToolMetadata实例并定义该工具的其他设置:spring-doc.cadn.net.cn

ToolCallback toolCallback = FunctionToolCallback
    .builder("currentWeather", new WeatherService())
    .description("Get the weather in location")
    .inputType(WeatherRequest.class)
    .build();

函数 inputs 和 outputs 可以是Void或 POJO。输入和输出 POJO 必须是可序列化的,因为结果将被序列化并发送回模型。函数以及输入和输出类型必须是 public。spring-doc.cadn.net.cn

某些类型不受支持。有关更多详细信息,请参阅 函数工具限制

将工具添加到ChatClient

使用编程规范方法时,您可以将FunctionToolCallback实例添加到tools()method 的ChatClient.该工具仅适用于它所添加到的特定聊天请求。spring-doc.cadn.net.cn

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

添加默认工具ChatClient

使用编程规范方法时,您可以将默认工具添加到ChatClient.Builder通过传递FunctionToolCallback实例添加到defaultTools()方法。 如果同时提供了 default 和 runtime 工具,则 runtime 工具将完全覆盖 default 工具。spring-doc.cadn.net.cn

默认工具在所有ChatClient从同一ChatClient.Builder.它们对于在不同聊天请求中常用的工具很有用,但如果不小心使用,它们也可能很危险,有可能在不该使用时提供它们。
ChatModel chatModel = ...
ToolCallback toolCallback = ...
ChatClient chatClient = ChatClient.builder(chatModel)
    .defaultTools(toolCallback)
    .build();

将工具添加到ChatModel

使用编程规范方法时,您可以将FunctionToolCallback实例添加到toolCallbacks()method 的ToolCallingChatOptions.该工具仅适用于它所添加到的特定聊天请求。spring-doc.cadn.net.cn

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

添加默认工具ChatModel

使用编程规范方法时,您可以将默认工具添加到ChatModel在构建时,通过传递FunctionToolCallback实例添加到toolCallbacks()方法ToolCallingChatOptions实例用于创建ChatModel. 如果同时提供了 default 和 runtime 工具,则 runtime 工具将完全覆盖 default 工具。spring-doc.cadn.net.cn

默认工具在该执行的所有聊天请求之间共享ChatModel实例。它们对于在不同聊天请求中常用的工具很有用,但如果不小心使用,它们也可能很危险,有可能在不该使用时提供它们。
ToolCallback toolCallback = ...
ChatModel chatModel = OllamaChatModel.builder()
    .ollamaApi(OllamaApi.builder().build())
    .defaultOptions(ToolCallingChatOptions.builder()
            .toolCallbacks(toolCallback)
            .build())
    .build();

动态规格:@Bean

你可以将工具定义为 Spring bean,并让 Spring AI 在运行时使用ToolCallbackResolver接口(通过SpringBeanToolCallbackResolver实现)。此选项允许您使用任何Function,Supplier,ConsumerBiFunctionbean 作为工具。bean 名称将用作工具名称,而@Description来自 Spring Framework 的注释可用于提供工具的描述,模型使用它来了解何时以及如何调用该工具。如果您未提供描述,则方法名称将用作工具描述。但是,强烈建议提供详细的描述,因为这对于模型了解工具的用途以及如何使用它至关重要。未能提供良好的描述可能会导致模型在应该使用该工具时未使用该工具或错误地使用该工具。spring-doc.cadn.net.cn

@Configuration(proxyBeanMethods = false)
class WeatherTools {

    WeatherService weatherService = new WeatherService();

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

}
某些类型不受支持。有关更多详细信息,请参阅 函数工具限制

将自动生成工具输入参数的 JSON 方案。您可以使用@ToolParamannotation 提供有关输入参数的其他信息,例如描述或参数是 required 还是 optional。默认情况下,所有输入参数都被视为必需参数。有关更多详细信息,请参阅 JSON 架构spring-doc.cadn.net.cn

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

这种工具规范方法的缺点是不能保证类型安全,因为工具解析是在运行时完成的。要缓解这种情况,您可以使用@Bean注解并将值存储在常量中,以便您可以在 Chat 请求中使用它,而不是对工具名称进行硬编码。spring-doc.cadn.net.cn

@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() {
		...
	}

}

将工具添加到ChatClient

当使用动态规范方法时,你可以将工具名称(即函数 Bean 名称)传递给tools()method 的ChatClient. 该工具仅适用于它所添加到的特定聊天请求。spring-doc.cadn.net.cn

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

添加默认工具ChatClient

使用动态规范方法时,您可以将默认工具添加到ChatClient.Builder通过将工具名称传递给defaultTools()方法。 如果同时提供了 default 和 runtime 工具,则 runtime 工具将完全覆盖 default 工具。spring-doc.cadn.net.cn

默认工具在所有ChatClient从同一ChatClient.Builder.它们对于在不同聊天请求中常用的工具很有用,但如果不小心使用,它们也可能很危险,有可能在不该使用时提供它们。
ChatModel chatModel = ...
ChatClient chatClient = ChatClient.builder(chatModel)
    .defaultTools("currentWeather")
    .build();

将工具添加到ChatModel

使用动态规范方法时,您可以将工具名称传递给toolNames()方法ToolCallingChatOptions你用来调用ChatModel.该工具仅适用于它所添加到的特定聊天请求。spring-doc.cadn.net.cn

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

添加默认工具ChatModel

使用动态规范方法时,可以将默认工具添加到ChatModel在构建时,将工具名称传递给toolNames()方法ToolCallingChatOptions实例用于创建ChatModel. 如果同时提供了 default 和 runtime 工具,则 runtime 工具将完全覆盖 default 工具。spring-doc.cadn.net.cn

默认工具在该执行的所有聊天请求之间共享ChatModel实例。它们对于在不同聊天请求中常用的工具很有用,但如果不小心使用,它们也可能很危险,有可能在不该使用时提供它们。
ChatModel chatModel = OllamaChatModel.builder()
    .ollamaApi(OllamaApi.builder().build())
    .defaultOptions(ToolCallingChatOptions.builder()
            .toolNames("currentWeather")
            .build())
    .build();

函数工具限制

当前不支持将以下类型用作用作工具的函数的输入或输出类型:spring-doc.cadn.net.cn

使用基于方法的工具规范方法支持基元类型和集合。有关更多详细信息,请参阅方法作为工具spring-doc.cadn.net.cn

工具规格

在 Spring AI 中,工具是通过ToolCallback接口。在前面的部分中,我们已经看到了如何使用 Spring AI 提供的内置支持从方法和函数定义工具(参见作为工具和作为工具的函数)。本节将深入探讨工具规范以及如何自定义和扩展它以支持更多用例。spring-doc.cadn.net.cn

工具回调

ToolCallbackinterface 提供了一种定义 AI 模型可以调用的工具的方法,包括定义和执行逻辑。当您想从头开始定义一个工具时,它是要实现的主界面。例如,您可以定义一个ToolCallback从 MCP 客户端(使用模型上下文协议)或ChatClient(构建一个模块化的代理应用程序)。spring-doc.cadn.net.cn

该接口提供如下方法:spring-doc.cadn.net.cn

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

}

Spring AI 为工具方法(MethodToolCallback) 和工具函数 (FunctionToolCallback).spring-doc.cadn.net.cn

工具定义

ToolDefinitioninterface 为 AI 模型提供了解工具可用性所需的信息,包括工具名称、描述和输入架构。每ToolCallbackimplementation 必须提供ToolDefinition实例来定义工具。spring-doc.cadn.net.cn

该接口提供如下方法:spring-doc.cadn.net.cn

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

}
有关输入架构的更多详细信息,请参阅 JSON 架构

ToolDefinition.Builder允许您构建一个ToolDefinition实例使用默认实现 (DefaultToolDefinition).spring-doc.cadn.net.cn

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

方法工具定义

从方法构建工具时,ToolDefinition是自动生成的。如果您希望生成ToolDefinition您自己,您可以使用这个方便的构建器。spring-doc.cadn.net.cn

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

ToolDefinition从方法生成,包括作为工具名称的方法名称、作为工具描述的方法名称以及方法输入参数的 JSON 架构。如果方法带有@Tool,则工具名称和描述将取自注记(如果已设置)。spring-doc.cadn.net.cn

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

如果您希望显式提供部分或全部属性,可以使用ToolDefinition.Builder构建自定义ToolDefinition实例。spring-doc.cadn.net.cn

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

功能工具定义

从函数构建工具时,ToolDefinition是自动生成的。当您使用FunctionToolCallback.Builder要构建FunctionToolCallback实例中,您可以提供将用于生成ToolDefinition.有关更多详细信息,请参阅函数即工具spring-doc.cadn.net.cn

JSON 架构

向 AI 模型提供工具时,模型需要知道用于调用该工具的输入类型的架构。架构用于了解如何调用工具和准备工具请求。Spring AI 提供了内置支持,用于通过JsonSchemaGenerator类。该架构作为ToolDefinition.spring-doc.cadn.net.cn

请参阅 工具定义 以了解有关ToolDefinition以及如何将 input 架构传递给它。

JsonSchemaGenerator类在后台用于为方法或函数的输入参数生成 JSON 架构,使用方法作为工具和函数作为工具中描述的任何策略。JSON 架构生成逻辑支持一系列注释,您可以在方法和函数的输入参数上使用这些注释来自定义生成的架构。spring-doc.cadn.net.cn

本节介绍在为工具的输入参数生成 JSON 架构时可以自定义的两个主要选项:description 和 required status。spring-doc.cadn.net.cn

描述

除了提供工具本身的描述外,您还可以提供工具输入参数的描述。描述可用于提供有关输入参数的关键信息,例如参数应采用的格式、允许的值等。这有助于模型了解输入架构以及如何使用它。Spring AI 提供了内置支持,以使用以下注释之一为 Importing 参数生成描述:spring-doc.cadn.net.cn

此方法适用于方法和函数,您可以递归地将其用于嵌套类型。spring-doc.cadn.net.cn

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

}

必需/可选

默认情况下,每个输入参数都被视为必需参数,这会强制 AI 模型在调用工具时为其提供值。但是,您可以使用以下注释之一将 input 参数设置为可选,按以下优先顺序:spring-doc.cadn.net.cn

此方法适用于方法和函数,您可以递归地将其用于嵌套类型。spring-doc.cadn.net.cn

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

}
为输入参数定义正确的 required 状态对于降低幻觉风险并确保模型在调用工具时提供正确的输入至关重要。在前面的示例中,emailparameter 是可选的,这意味着模型可以在不提供值的情况下调用该工具。如果需要该参数,则模型在调用工具时必须为其提供一个值。如果不存在值,模型可能会编造一个,从而导致幻觉。

结果转换

工具调用的结果使用ToolCallResultConverter然后发送回 AI 模型。这ToolCallResultConverterinterface 提供了一种将工具调用的结果转换为String对象。spring-doc.cadn.net.cn

该接口提供如下方法:spring-doc.cadn.net.cn

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

}

结果必须是可序列化类型。默认情况下,结果使用 Jackson (DefaultToolCallResultConverter),但您可以通过提供自己的序列化过程来自定义序列化过程ToolCallResultConverter实现。spring-doc.cadn.net.cn

Spring AI 依赖于ToolCallResultConverter在 method 和 function 工具中。spring-doc.cadn.net.cn

方法工具调用结果转换

当使用声明式方法从方法构建工具时,您可以提供自定义ToolCallResultConverter以用于该工具,方法是将resultConverter()属性的@Tool注解。spring-doc.cadn.net.cn

class CustomerTools {

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

}

如果使用编程方法,则可以提供自定义ToolCallResultConverter以用于该工具,方法是将resultConverter()属性的MethodToolCallback.Builder.spring-doc.cadn.net.cn

有关更多详细信息,请参阅方法作为工具spring-doc.cadn.net.cn

函数工具调用结果转换

使用编程方法从函数构建工具时,您可以提供自定义的ToolCallResultConverter以用于该工具,方法是将resultConverter()属性的FunctionToolCallback.Builder.spring-doc.cadn.net.cn

有关更多详细信息,请参阅函数即工具spring-doc.cadn.net.cn

工具上下文

Spring AI 支持通过ToolContext应用程序接口。此功能允许您提供额外的用户提供的数据,这些数据可与 AI 模型传递的工具参数一起在工具执行中使用。spring-doc.cadn.net.cn

为工具提供额外的上下文信息
class CustomerTools {

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

}

ToolContext填充了用户在调用时提供的数据ChatClient.spring-doc.cadn.net.cn

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);
ToolContext发送到 AI 模型。

同样,您可以在调用ChatModel径直。spring-doc.cadn.net.cn

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

如果toolContext选项在默认选项和运行时选项中设置,结果会ToolContext将是两者的合并, 其中,运行时选项优先于默认选项。spring-doc.cadn.net.cn

直接返回

默认情况下,工具调用的结果将作为响应发送回模型。然后,模型可以使用结果继续对话。spring-doc.cadn.net.cn

在某些情况下,您宁愿将结果直接返回给调用方,而不是将其发送回模型。例如,如果您构建了一个依赖于 RAG 工具的代理,您可能希望将结果直接返回给调用方,而不是将其发送回模型进行不必要的后处理。或者,也许您有一些工具可以结束代理的推理循环。spring-doc.cadn.net.cn

ToolCallbackimplementation 可以定义是应将工具调用的结果直接返回给调用方还是发送回模型。默认情况下,结果将发送回模型。但是,您可以按工具更改此行为。spring-doc.cadn.net.cn

ToolCallingManager负责管理工具执行生命周期的returnDirect属性。如果该属性设置为true,则工具调用的结果将直接返回给调用方。否则,结果将发送回模型。spring-doc.cadn.net.cn

如果一次请求多个工具调用,则returnDirect属性必须设置为true对于所有工具,将结果直接返回给调用方。否则,结果将被发送回模型。
将工具调用结果直接返回给调用方
  1. 当我们想让某个工具可供模型使用时,我们会在 chat 请求中包含其定义。如果我们希望将工具执行的结果直接返回给调用者,我们将returnDirect属性设置为true.spring-doc.cadn.net.cn

  2. 当模型决定调用工具时,它会发送一个响应,其中包含工具名称和输入参数,这些参数在定义的方案之后建模。spring-doc.cadn.net.cn

  3. 应用程序负责使用工具名称来识别并使用提供的输入参数执行工具。spring-doc.cadn.net.cn

  4. 工具调用的结果由应用程序处理。spring-doc.cadn.net.cn

  5. 应用程序将工具调用结果直接发送给调用者,而不是将其发送回模型。spring-doc.cadn.net.cn

方法 Return Direct

当使用声明式方法从方法构建工具时,您可以通过设置returnDirect属性的@Toolannotation 添加到true.spring-doc.cadn.net.cn

class CustomerTools {

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

}

如果使用编程方法,则可以设置returnDirect属性ToolMetadata接口并将其传递给MethodToolCallback.Builder.spring-doc.cadn.net.cn

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

有关更多详细信息,请参阅方法作为工具spring-doc.cadn.net.cn

函数 Return Direct

使用编程方法从函数构建工具时,您可以设置returnDirect属性ToolMetadata接口并将其传递给FunctionToolCallback.Builder.spring-doc.cadn.net.cn

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

有关更多详细信息,请参阅函数即工具spring-doc.cadn.net.cn

工具执行

工具执行是使用提供的输入参数调用工具并返回结果的过程。工具执行由ToolCallingManager接口,该接口负责管理工具执行生命周期。spring-doc.cadn.net.cn

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

}

如果您使用的是任何 Spring AI Spring Boot Starters,DefaultToolCallingManagerToolCallingManager接口。您可以通过提供自己的工具执行行为来自定义ToolCallingManager豆。spring-doc.cadn.net.cn

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

默认情况下, Spring AI 从每个ChatModel实现。但是,您可以选择退出此行为并自行控制工具的执行。本节介绍这两种情况。spring-doc.cadn.net.cn

框架控制的工具执行

当使用默认行为时, Spring AI 将自动拦截来自模型的任何工具调用请求,调用工具并将结果返回给模型。所有这些都是由每个ChatModel使用ToolCallingManager.spring-doc.cadn.net.cn

框架控制的工具执行生命周期
  1. 当我们想让工具可供模型使用时,我们会将其定义包含在聊天请求 (Prompt) 并调用ChatModelAPI 将请求发送到 AI 模型。spring-doc.cadn.net.cn

  2. 当模型决定调用工具时,它会发送响应 (ChatResponse) 替换为工具名称和输入参数,这些参数将按照定义的方案进行建模。spring-doc.cadn.net.cn

  3. ChatModel将工具调用请求发送到ToolCallingManager应用程序接口。spring-doc.cadn.net.cn

  4. ToolCallingManager负责识别要调用的工具,并使用提供的输入参数执行该工具。spring-doc.cadn.net.cn

  5. 工具调用的结果将返回到ToolCallingManager.spring-doc.cadn.net.cn

  6. ToolCallingManager将工具执行结果返回给ChatModel.spring-doc.cadn.net.cn

  7. ChatModel将工具执行结果发送回 AI 模型(ToolResponseMessage).spring-doc.cadn.net.cn

  8. AI 模型使用工具调用结果作为附加上下文生成最终响应,并将其发送回调用方 (ChatResponse) 通过ChatClient.spring-doc.cadn.net.cn

目前,与模型交换的有关工具执行的内部消息不会向用户公开。如果您需要访问这些消息,则应使用用户控制的工具执行方法。

确定工具调用是否符合执行条件的逻辑由ToolExecutionEligibilityPredicate接口。默认情况下,工具执行资格是通过检查internalToolExecutionEnabled属性ToolCallingChatOptions设置为true(默认值),并且如果ChatResponse包含任何工具调用。spring-doc.cadn.net.cn

public class DefaultToolExecutionEligibilityPredicate implements ToolExecutionEligibilityPredicate {

	@Override
	public boolean test(ChatOptions promptOptions, ChatResponse chatResponse) {
		return ToolCallingChatOptions.isInternalToolExecutionEnabled(promptOptions) && chatResponse != null
				&& chatResponse.hasToolCalls();
	}

}

您可以提供ToolExecutionEligibilityPredicate创建ChatModel豆。spring-doc.cadn.net.cn

用户控制的工具执行

在某些情况下,您宁愿自己控制工具执行生命周期。您可以通过设置internalToolExecutionEnabled属性ToolCallingChatOptionsfalse.spring-doc.cadn.net.cn

当您调用ChatModel使用此选项,工具执行将委派给调用方,从而使您能够完全控制工具执行生命周期。您有责任在ChatResponse并使用ToolCallingManager.spring-doc.cadn.net.cn

以下示例演示了用户控制的工具执行方法的最小实现:spring-doc.cadn.net.cn

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());
在选择用户控制的工具执行方法时,我们建议使用ToolCallingManager管理工具调用作。这样,您就可以从 Spring AI 提供的对工具执行的内置支持中受益。但是,没有什么可以阻止您实现自己的工具执行逻辑。

以下示例显示了用户控制的工具执行方法的最小实现,并结合了ChatMemory应用程序接口:spring-doc.cadn.net.cn

ToolCallingManager toolCallingManager = DefaultToolCallingManager.builder().build();
ChatMemory chatMemory = MessageWindowChatMemory.builder().build();
String conversationId = UUID.randomUUID().toString();

ChatOptions chatOptions = ToolCallingChatOptions.builder()
    .toolCallbacks(ToolCallbacks.from(new MathTools()))
    .internalToolExecutionEnabled(false)
    .build();
Prompt prompt = new Prompt(
        List.of(new SystemMessage("You are a helpful assistant."), new UserMessage("What is 6 * 8?")),
        chatOptions);
chatMemory.add(conversationId, prompt.getInstructions());

Prompt promptWithMemory = new Prompt(chatMemory.get(conversationId), chatOptions);
ChatResponse chatResponse = chatModel.call(promptWithMemory);
chatMemory.add(conversationId, chatResponse.getResult().getOutput());

while (chatResponse.hasToolCalls()) {
    ToolExecutionResult toolExecutionResult = toolCallingManager.executeToolCalls(promptWithMemory,
            chatResponse);
    chatMemory.add(conversationId, toolExecutionResult.conversationHistory()
        .get(toolExecutionResult.conversationHistory().size() - 1));
    promptWithMemory = new Prompt(chatMemory.get(conversationId), chatOptions);
    chatResponse = chatModel.call(promptWithMemory);
    chatMemory.add(conversationId, chatResponse.getResult().getOutput());
}

UserMessage newUserMessage = new UserMessage("What did I ask you earlier?");
chatMemory.add(conversationId, newUserMessage);

ChatResponse newResponse = chatModel.call(new Prompt(chatMemory.get(conversationId)));

异常处理

当工具调用失败时,异常将作为ToolExecutionException可以捕获它来处理错误。一个ToolExecutionExceptionProcessor可用于处理ToolExecutionException有两种结果:生成要发送回 AI 模型的错误消息,或者引发由调用方处理的异常。spring-doc.cadn.net.cn

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

}

如果您使用的是任何 Spring AI Spring Boot Starters,DefaultToolExecutionExceptionProcessorToolExecutionExceptionProcessor接口。默认情况下,错误消息将发送回模型。这DefaultToolExecutionExceptionProcessorconstructor 允许您设置alwaysThrow属性设置为truefalse.如果true,则会引发异常,而不是将错误消息发送回模型。spring-doc.cadn.net.cn

@Bean
ToolExecutionExceptionProcessor toolExecutionExceptionProcessor() {
    return new DefaultToolExecutionExceptionProcessor(true);
}
如果您定义了自己的ToolCallbackimplementation 中,请确保抛出一个ToolExecutionExceptioncall()方法。

ToolExecutionExceptionProcessor默认在内部使用ToolCallingManager (DefaultToolCallingManager) 处理工具执行期间的异常。有关工具执行生命周期的更多详细信息,请参阅工具执行spring-doc.cadn.net.cn

工具分辨率

将工具传递给模型的主要方法是提供ToolCallback(s) 在调用ChatClientChatModel, 使用 Methods as ToolsFunctions as Tools中描述的策略之一。spring-doc.cadn.net.cn

但是,Spring AI 还支持在运行时使用ToolCallbackResolver接口。spring-doc.cadn.net.cn

public interface ToolCallbackResolver {

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

}

使用此方法时:spring-doc.cadn.net.cn

  • 在客户端,您将工具名称提供给ChatClientChatModel而不是ToolCallback(s) 的spring-doc.cadn.net.cn

  • 在服务器端,一个ToolCallbackResolverimplementation 负责将工具名称解析为对应的ToolCallback实例。spring-doc.cadn.net.cn

默认情况下,Spring AI 依赖于DelegatingToolCallbackResolver,将工具分辨率委托给ToolCallbackResolver实例:spring-doc.cadn.net.cn

  • SpringBeanToolCallbackResolver解析 Spring bean 类型的工具Function,Supplier,ConsumerBiFunction.看动态规格:@Bean了解更多详情。spring-doc.cadn.net.cn

  • StaticToolCallbackResolver从静态ToolCallback实例。使用 Spring Boot Autoconfiguration 时,此解析器会自动配置所有 bean 类型ToolCallback在应用程序上下文中定义。spring-doc.cadn.net.cn

如果您依赖 Spring Boot Autoconfiguration,则可以通过提供自定义的ToolCallbackResolver豆。spring-doc.cadn.net.cn

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

ToolCallbackResolverToolCallingManager在运行时动态解析工具,同时支持 Framework-Controlled Tool ExecutionUser-Controlled Tool Executionspring-doc.cadn.net.cn

可观察性

用于工具调用的插桩正在进行中。目前,您可以使用日志记录功能来跟踪工具调用作。spring-doc.cadn.net.cn

Logging

工具调用功能的所有主要作都记录在DEBUG水平。您可以通过将日志级别设置为DEBUG对于org.springframework.ai包。spring-doc.cadn.net.cn