Tool Calling

Tool calling (也称为 function calling )是 AI 应用程序中的常见模式,允许模型与一组 API 或 tools 交互,从而增强其功能。

Tool calling (also known as function calling) is a common pattern in AI applications allowing a model to interact with a set of APIs, or tools, augmenting its capabilities.

工具主要用于:

Tools are mainly used for:

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

  • Information Retrieval. Tools in this category can be used to retrieve information from external sources, such as a database, a web service, a file system, or a web search engine. The goal is to augment the knowledge of the model, allowing it to answer questions that it would not be able to answer otherwise. As such, they can be used in Retrieval Augmented Generation (RAG) scenarios. For example, a tool can be used to retrieve the current weather for a given location, to retrieve the latest news articles, or to query a database for a specific record.

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

  • Taking Action. Tools in this category can be used to take action in a software system, such as sending an email, creating a new record in a database, submitting a form, or triggering a workflow. The goal is to automate tasks that would otherwise require human intervention or explicit programming. For example, a tool can be used to book a flight for a customer interacting with a chatbot, to fill out a form on a web page, or to implement a Java class based on an automated test (TDD) in a code generation scenario.

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

Even though we typically refer to tool calling as a model capability, it is actually up to the client application to provide the tool calling logic. The model can only request a tool call and provide the input arguments, whereas the application is responsible for executing the tool call from the input arguments and returning the result. The model never gets access to any of the APIs provided as tools, which is a critical security consideration.

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

Spring AI provides convenient APIs to define tools, resolve tool call requests from a model, and execute the tool calls. The following sections provide an overview of the tool calling capabilities in Spring AI.

查看 Chat Model Comparisons 以了解哪些 AI 模型支持工具调用。

Check the Chat Model Comparisons to see which AI models support tool calling invocation.

按照指南从已弃用的 FunctionCallback to ToolCallback API 迁移。

Follow the guide to migrate from the deprecated FunctionCallback to ToolCallback API.

Quick Start

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

Let’s see how to start using tool calling in Spring AI. We’ll implement two simple tools: one for information retrieval and one for taking action. The information retrieval tool will be used to get the current date and time in the user’s time zone. The action tool will be used to set an alarm for a specified time.

Information Retrieval

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

AI models don’t have access to real-time information. Any question that assumes awareness of information such as the current date or weather forecast cannot be answered by the model. However, we can provide a tool that can retrieve this information, and let the model call this tool when access to real-time information is needed.

让我们在一个 DateTimeTools 类中实现一个工具,用于获取用户时区的当前日期和时间。该工具将不带参数。Spring Framework 中的 LocaleContextHolder 可以提供用户的时区。该工具将被定义为使用 @Tool 注解的方法。为了帮助模型理解是否以及何时调用此工具,我们将提供工具功能的详细描述。

Let’s implement a tool to get the current date and time in the user’s time zone in a DateTimeTools class. The tool will take no argument. The LocaleContextHolder from Spring Framework can provide the user’s time zone. The tool will be defined as a method annotated with @Tool. To help the model understand if and when to call this tool, we’ll provide a detailed description of what the tools does.

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 与模型交互。我们将通过 tools() 方法传递 DateTimeTools 的实例来向模型提供工具。当模型需要知道当前日期和时间时,它将请求调用该工具。在内部, ChatClient 将调用该工具并将结果返回给模型,然后模型将使用该工具调用结果来生成对原始问题的最终响应。

Next, let’s make the tool available to the model. In this example, we’ll use the ChatClient to interact with the model. We’ll provide the tool to the model by passing an instance of DateTimeTools via the tools() method. When the model needs to know the current date and time, it will request the tool to be called. Internally, the ChatClient will call the tool and return the result to the model, which will then use the tool call result to generate the final response to the original question.

ChatModel chatModel = ...

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

System.out.println(response);

输出将类似于:

The output will be something like:

Tomorrow is 2015-10-21.

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

You can retry asking the same question again. This time, don’t provide the tool to the model. The output will be something like:

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.

没有工具,模型不知道如何回答问题,因为它无法确定当前日期和时间。

Without the tool, the model doesn’t know how to answer the question because it doesn’t have the ability to determine the current date and time.

Taking Actions

AI 模型可以用于生成实现特定目标的计划。例如,一个模型可以生成一个预订丹麦旅行的计划。然而,模型本身不具备执行该计划的能力。这就是工具的作用:它们可以用于执行模型生成的计划。

AI models can be used to generate plans for accomplishing certain goals. For example, a model can generate a plan for booking a trip to Denmark. However, the model doesn’t have the ability to execute the plan. That’s where tools come in: they can be used to execute the plan that a model generates.

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

In the previous example, we used a tool to determine the current date and time. In this example, we’ll define a second tool for setting an alarm at a specific time. The goal is to set an alarm for 10 minutes from now, so we need to provide both tools to the model to accomplish this task.

我们将把新工具添加到与之前相同的 DateTimeTools 类中。新工具将接受一个参数,即 ISO-8601 格式的时间。然后,该工具将向控制台打印一条消息,指示已为给定时间设置了闹钟。与之前一样,该工具被定义为一个带有 @Tool 注解的方法,我们还用它来提供详细的描述,以帮助模型理解何时以及如何使用该工具。

We’ll add the new tool to the same DateTimeTools class as before. The new tool will take a single parameter, which is the time in ISO-8601 format. The tool will then print a message to the console indicating that the alarm has been set for the given time. Like before, the tool is defined as a method annotated with @Tool, which we also use to provide a detailed description to help the model understand when and how to use the 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);
    }

}

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

Next, let’s make both tools available to the model. We’ll use the ChatClient to interact with the model. We’ll provide the tools to the model by passing an instance of DateTimeTools via the tools() method. When we ask to set up an alarm 10 minutes from now, the model will first need to know the current date and time. Then, it will use the current date and time to calculate the alarm time. Finally, it will use the alarm tool to set up the alarm. Internally, the ChatClient will handle any tool call request from the model and send back to it any tool call execution result, so that the model can generate the final response.

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

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

In the application logs, you can check the alarm has been set at the correct time.

Overview

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

Spring AI supports tool calling through a set of flexible abstractions that allow you to define, resolve, and execute tools in a consistent way. This section provides an overview of the main concepts and components of tool calling in Spring AI.

tool calling 01
  1. 当我们想让一个工具可用于模型时,我们会将它的定义包含在聊天请求中。每个工具定义都包含一个名称、一个描述以及输入参数的模式。

  2. When we want to make a tool available to the model, we include its definition in the chat request. Each tool definition comprises of a name, a description, and the schema of the input parameters.

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

  4. When the model decides to call a tool, it sends a response with the tool name and the input parameters modeled after the defined schema.

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

  6. The application is responsible for using the tool name to identify and execute the tool with the provided input parameters.

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

  8. The result of the tool call is processed by the application.

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

  10. The application sends the tool call result back to the model.

  11. 模型使用工具调用结果作为附加上下文生成最终响应。

  12. The model generates the final response using the tool call result as additional context.

工具是工具调用的构建块,它们由 ToolCallback 接口建模。Spring AI 提供了内置支持,用于从方法和函数指定 ToolCallback ,但您始终可以定义自己的 ToolCallback 实现以支持更多用例。

Tools are the building blocks of tool calling and they are modeled by the ToolCallback interface. Spring AI provides built-in support for specifying ToolCallback(s) from methods and functions, but you can always define your own ToolCallback implementations to support more use cases.

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

ChatModel implementations transparently dispatch tool call requests to the corresponding ToolCallback implementations and will send the tool call results back to the model, which will ultimately generate the final response. They do so using the ToolCallingManager interface, which is responsible for managing the tool execution lifecycle.

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

Both ChatClient and ChatModel accept a list of ToolCallback objects to make the tools available to the model and the ToolCallingManager that will eventually execute them.

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

Besides passing the ToolCallback objects directly, you can also pass a list of tool names, that will be resolved dynamically using the ToolCallbackResolver interface.

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

The following sections will go into more details about all these concepts and APIs, including how to customize and extend them to support more use cases.

Methods as Tools

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

Spring AI provides built-in support for specifying tools (i.e. ToolCallback(s)) from methods in two ways:

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

  • declaratively, using the @Tool annotation

  • 程序式地,使用低级 MethodToolCallback 实现。

  • programmatically, using the low-level MethodToolCallback implementation.

Declarative Specification: @Tool

您可以通过使用 @Tool 注解方法将其转换为工具。

You can turn a method into a tool by annotating it with @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 注解允许您提供有关工具的关键信息:

The @Tool annotation allows you to provide key information about the tool:

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

  • name: The name of the tool. If not provided, the method name will be used. AI models use this name to identify the tool when calling it. Therefore, it’s not allowed to have two tools with the same name in the same class. The name must be unique across all the tools available to the model for a specific chat request.

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

  • description: The description for the tool, which can be used by the model to understand when and how to call the tool. If not provided, the method name will be used as the tool description. However, it’s strongly recommended to provide a detailed description because that’s paramount for the model to understand the tool’s purpose and how to use it. Failing in providing a good description can lead to the model not using the tool when it should or using it incorrectly.

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

  • returnDirect: Whether the tool result should be returned directly to the client or passed back to the model. See Return Direct for more details.

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

  • resultConverter: The ToolCallResultConverter implementation to use for converting the result of a tool call to a String object to send back to the AI model. See Result Conversion for more details.

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

The method can be either static or instance, and it can have any visibility (public, protected, package-private, or private). The class that contains the method can be either a top-level class or a nested class, and it can also have any visibility (as long as it’s accessible where you’re planning to instantiate it).

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

Spring AI provides built-in support for AOT compilation of the @Tool-annotated methods as long as the class containing the methods is a Spring bean (e.g. @Component). Otherwise, you’ll need to provide the necessary configuration to the GraalVM compiler. For example, by annotating the class with @RegisterReflection(memberCategories = MemberCategory.INVOKE_DECLARED_METHODS).

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

You can define any number of arguments for the method (including no argument) with most types (primitives, POJOs, enums, lists, arrays, maps, and so on). Similarly, the method can return most types, including void. If the method returns a value, the return type must be a serializable type, as the result will be serialized and sent back to the model.

某些类型不支持。有关更多详细信息,请参阅 Method Tool Limitations

Some types are not supported. See Method Tool Limitations for more details.

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

Spring AI will generate the JSON schema for the input parameters of the @Tool-annotated method automatically. The schema is used by the model to understand how to call the tool and prepare the tool request. The @ToolParam annotation can be used to provide additional information about the input parameters, such as a description or whether the parameter is required or optional. By default, all input parameters are considered required.

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 注解允许您提供有关工具参数的关键信息:

The @ToolParam annotation allows you to provide key information about a tool parameter:

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

  • description: The description for the parameter, which can be used by the model to understand better how to use it. For example, what format the parameter should be in, what values are allowed, and so on.

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

  • required: Whether the parameter is required or optional. By default, all parameters are considered required.

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

If a parameter is annotated as @Nullable, it will be considered optional unless explicitly marked as required using the @ToolParam annotation.

除了 @ToolParam 注解之外,您还可以使用 Swagger 的 @Schema 注解或 Jackson 的 @JsonProperty 。有关更多详细信息,请参阅 JSON Schema

Besides the @ToolParam annotation, you can also use the @Schema annotation from Swagger or @JsonProperty from Jackson. See JSON Schema for more details.

Adding Tools to ChatClient

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

When using the declarative specification approach, you can pass the tool class instance to the tools() method when invoking a ChatClient. Such tools will only be available for the specific chat request they are added to.

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

在底层, ChatClient 将从工具类实例中每个 @Tool 注解的方法生成一个 ToolCallback 并将其传递给模型。如果您更喜欢自己生成 ToolCallback ,可以使用 ToolCallbacks 实用类。

Under the hood, the ChatClient will generate a ToolCallback from each @Tool-annotated method in the tool class instance and pass them to the model. In case you prefer to generate the ToolCallback(s) yourself, you can use the ToolCallbacks utility class.

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

Adding Default Tools to ChatClient

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

When using the declarative specification approach, you can add default tools to a ChatClient.Builder by passing the tool class instance to the defaultTools() method. If both default and runtime tools are provided, the runtime tools will completely override the default tools.

默认工具在由相同 ChatClient.Builder 构建的所有 ChatClient 实例执行的所有聊天请求中共享。它们对于跨不同聊天请求常用工具很有用,但如果不谨慎使用也可能存在危险,有可能在不应该提供时也提供它们。

Default tools are shared across all the chat requests performed by all the ChatClient instances built from the same ChatClient.Builder. They are useful for tools that are commonly used across different chat requests, but they can also be dangerous if not used carefully, risking to make them available when they shouldn’t.

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

Adding Tools to ChatModel

当使用声明式规范方法时,你可以将工具类实例传递给用于调用 ChatModelToolCallingChatOptionstoolCallbacks() 方法。此类工具将只对它们所添加到的特定聊天请求可用。

When using the declarative specification approach, you can pass the tool class instance to the toolCallbacks() method of the ToolCallingChatOptions you use to call a ChatModel. Such tools will only be available for the specific chat request they are added to.

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

Adding Default Tools to ChatModel

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

When using the declarative specification approach, you can add default tools to ChatModel at construction time by passing the tool class instance to the toolCallbacks() method of the ToolCallingChatOptions instance used to create the ChatModel. If both default and runtime tools are provided, the runtime tools will completely override the default tools.

默认工具在由该 ChatModel 实例执行的所有聊天请求中共享。它们对于跨不同聊天请求常用工具很有用,但如果不谨慎使用也可能存在危险,有可能在不应该提供时也提供它们。

Default tools are shared across all the chat requests performed by that ChatModel instance. They are useful for tools that are commonly used across different chat requests, but they can also be dangerous if not used carefully, risking to make them available when they shouldn’t.

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

Programmatic Specification: MethodToolCallback

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

You can turn a method into a tool by building a MethodToolCallback programmatically.

class DateTimeTools {

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

}

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

The MethodToolCallback.Builder allows you to build a MethodToolCallback instance and provide key information about the tool:

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

  • toolDefinition: The ToolDefinition instance that defines the tool name, description, and input schema. You can build it using the ToolDefinition.Builder class. Required.

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

  • toolMetadata: The ToolMetadata instance that defines additional settings such as whether the result should be returned directly to the client, and the result converter to use. You can build it using the ToolMetadata.Builder class.

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

  • toolMethod: The Method instance that represents the tool method. Required.

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

  • toolObject: The object instance that contains the tool method. If the method is static, you can omit this parameter.

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

  • toolCallResultConverter: The ToolCallResultConverter instance to use for converting the result of a tool call to a String object to send back to the AI model. If not provided, the default converter will be used (DefaultToolCallResultConverter).

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

The ToolDefinition.Builder allows you to build a ToolDefinition instance and define the tool name, description, and input schema:

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

  • name: The name of the tool. If not provided, the method name will be used. AI models use this name to identify the tool when calling it. Therefore, it’s not allowed to have two tools with the same name in the same class. The name must be unique across all the tools available to the model for a specific chat request.

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

  • description: The description for the tool, which can be used by the model to understand when and how to call the tool. If not provided, the method name will be used as the tool description. However, it’s strongly recommended to provide a detailed description because that’s paramount for the model to understand the tool’s purpose and how to use it. Failing in providing a good description can lead to the model not using the tool when it should or using it incorrectly.

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

  • inputSchema: The JSON schema for the input parameters of the tool. If not provided, the schema will be generated automatically based on the method parameters. You can use the @ToolParam annotation to provide additional information about the input parameters, such as a description or whether the parameter is required or optional. By default, all input parameters are considered required. See JSON Schema for more details.

ToolMetadata.Builder 允许您构建 ToolMetadata 实例并为该工具定义附加设置:

The ToolMetadata.Builder allows you to build a ToolMetadata instance and define additional settings for the tool:

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

  • returnDirect: Whether the tool result should be returned directly to the client or passed back to the model. See Return Direct for more details.

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

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

The method can be either static or instance, and it can have any visibility (public, protected, package-private, or private). The class that contains the method can be either a top-level class or a nested class, and it can also have any visibility (as long as it’s accessible where you’re planning to instantiate it).

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

Spring AI provides built-in support for AOT compilation of the tool methods as long as the class containing the methods is a Spring bean (e.g. @Component). Otherwise, you’ll need to provide the necessary configuration to the GraalVM compiler. For example, by annotating the class with @RegisterReflection(memberCategories = MemberCategory.INVOKE_DECLARED_METHODS).

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

You can define any number of arguments for the method (including no argument) with most types (primitives, POJOs, enums, lists, arrays, maps, and so on). Similarly, the method can return most types, including void. If the method returns a value, the return type must be a serializable type, as the result will be serialized and sent back to the model.

某些类型不支持。有关更多详细信息,请参阅 Method Tool Limitations

Some types are not supported. See Method Tool Limitations for more details.

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

If the method is static, you can omit the toolObject() method, as it’s not needed.

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 模式。模式用于模型理解如何调用工具并准备工具请求。 @ToolParam 注解可用于提供有关输入参数的附加信息,例如描述或参数是必需还是可选。默认情况下,所有输入参数都被视为必需。

Spring AI will generate the JSON schema for the input parameters of the method automatically. The schema is used by the model to understand how to call the tool and prepare the tool request. The @ToolParam annotation can be used to provide additional information about the input parameters, such as a description or whether the parameter is required or optional. By default, all input parameters are considered required.

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 注解允许您提供有关工具参数的关键信息:

The @ToolParam annotation allows you to provide key information about a tool parameter:

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

  • description: The description for the parameter, which can be used by the model to understand better how to use it. For example, what format the parameter should be in, what values are allowed, and so on.

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

  • required: Whether the parameter is required or optional. By default, all parameters are considered required.

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

If a parameter is annotated as @Nullable, it will be considered optional unless explicitly marked as required using the @ToolParam annotation.

除了 @ToolParam 注解之外,您还可以使用 Swagger 的 @Schema 注解或 Jackson 的 @JsonProperty 。有关更多详细信息,请参阅 JSON Schema

Besides the @ToolParam annotation, you can also use the @Schema annotation from Swagger or @JsonProperty from Jackson. See JSON Schema for more details.

Adding Tools to ChatClient and ChatModel

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

When using the programmatic specification approach, you can pass the MethodToolCallback instance to the tools() method of ChatClient. The tool will only be available for the specific chat request it’s added to.

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

Adding Default Tools to ChatClient

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

When using the programmatic specification approach, you can add default tools to a ChatClient.Builder by passing the MethodToolCallback instance to the defaultTools() method. If both default and runtime tools are provided, the runtime tools will completely override the default tools.

默认工具在由相同 ChatClient.Builder 构建的所有 ChatClient 实例执行的所有聊天请求中共享。它们对于跨不同聊天请求常用工具很有用,但如果不谨慎使用也可能存在危险,有可能在不应该提供时也提供它们。

Default tools are shared across all the chat requests performed by all the ChatClient instances built from the same ChatClient.Builder. They are useful for tools that are commonly used across different chat requests, but they can also be dangerous if not used carefully, risking to make them available when they shouldn’t.

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

Adding Tools to ChatModel

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

When using the programmatic specification approach, you can pass the MethodToolCallback instance to the toolCallbacks() method of the ToolCallingChatOptions you use to call a ChatModel. The tool will only be available for the specific chat request it’s added to.

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

Adding Default Tools to ChatModel

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

When using the programmatic specification approach, you can add default tools to a ChatModel at construction time by passing the MethodToolCallback instance to the toolCallbacks() method of the ToolCallingChatOptions instance used to create the ChatModel. If both default and runtime tools are provided, the runtime tools will completely override the default tools.

默认工具在由该 ChatModel 实例执行的所有聊天请求中共享。它们对于跨不同聊天请求常用工具很有用,但如果不谨慎使用也可能存在危险,有可能在不应该提供时也提供它们。

Default tools are shared across all the chat requests performed by that ChatModel instance. They are useful for tools that are commonly used across different chat requests, but they can also be dangerous if not used carefully, risking to make them available when they shouldn’t.

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

Method Tool Limitations

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

The following types are not currently supported as parameters or return types for methods used as tools:

  • Optional

  • 异步类型(例如 CompletableFutureFuture

  • Asynchronous types (e.g. CompletableFuture, Future)

  • 响应式类型(例如 FlowMonoFlux

  • Reactive types (e.g. Flow, Mono, Flux)

  • 函数类型(例如 FunctionSupplierConsumer )。

  • Functional types (e.g. Function, Supplier, Consumer).

功能类型通过基于函数的工具规范方法得到支持。更多细节请参见 Functions as Tools

Functional types are supported using the function-based tool specification approach. See Functions as Tools for more details.

Functions as Tools

Spring AI 内置支持从函数中指定工具,可以通过低级 FunctionToolCallback 实现以编程方式指定,也可以作为运行时解析的 @Bean (s)动态指定。

Spring AI provides built-in support for specifying tools from functions, either programmatically using the low-level FunctionToolCallback implementation or dynamically as @Bean(s) resolved at runtime.

Programmatic Specification: FunctionToolCallback

你可以通过编程方式构建 FunctionToolCallback ,将功能类型( FunctionSupplierConsumerBiFunction )转化为工具。

You can turn a functional type (Function, Supplier, Consumer, or BiFunction) into a tool by building a FunctionToolCallback programmatically.

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 实例并提供关于工具的关键信息:

The FunctionToolCallback.Builder allows you to build a FunctionToolCallback instance and provide key information about the tool:

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

  • name: The name of the tool. AI models use this name to identify the tool when calling it. Therefore, it’s not allowed to have two tools with the same name in the same context. The name must be unique across all the tools available to the model for a specific chat request. Required.

  • toolFunction :表示工具方法的函数对象( FunctionSupplierConsumerBiFunction )。必填。

  • toolFunction: The functional object that represents the tool method (Function, Supplier, Consumer, or BiFunction). Required.

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

  • description: The description for the tool, which can be used by the model to understand when and how to call the tool. If not provided, the method name will be used as the tool description. However, it’s strongly recommended to provide a detailed description because that’s paramount for the model to understand the tool’s purpose and how to use it. Failing in providing a good description can lead to the model not using the tool when it should or using it incorrectly.

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

  • inputType: The type of the function input. Required.

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

  • inputSchema: The JSON schema for the input parameters of the tool. If not provided, the schema will be generated automatically based on the inputType. You can use the @ToolParam annotation to provide additional information about the input parameters, such as a description or whether the parameter is required or optional. By default, all input parameters are considered required. See JSON Schema for more details.

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

  • toolMetadata: The ToolMetadata instance that defines additional settings such as whether the result should be returned directly to the client, and the result converter to use. You can build it using the ToolMetadata.Builder class.

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

  • toolCallResultConverter: The ToolCallResultConverter instance to use for converting the result of a tool call to a String object to send back to the AI model. If not provided, the default converter will be used (DefaultToolCallResultConverter).

ToolMetadata.Builder 允许您构建 ToolMetadata 实例并为该工具定义附加设置:

The ToolMetadata.Builder allows you to build a ToolMetadata instance and define additional settings for the tool:

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

  • returnDirect: Whether the tool result should be returned directly to the client or passed back to the model. See Return Direct for more details.

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

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

The function inputs and outputs can be either Void or POJOs. The input and output POJOs must be serializable, as the result will be serialized and sent back to the model. The function as well as the input and output types must be public.

某些类型不支持。更多细节请参见 Function Tool Limitations

Some types are not supported. See Function Tool Limitations for more details.

Adding Tools to ChatClient

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

When using the programmatic specification approach, you can pass the FunctionToolCallback instance to the tools() method of ChatClient. The tool will only be available for the specific chat request it’s added to.

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

Adding Default Tools to ChatClient

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

When using the programmatic specification approach, you can add default tools to a ChatClient.Builder by passing the FunctionToolCallback instance to the defaultTools() method. If both default and runtime tools are provided, the runtime tools will completely override the default tools.

默认工具在由相同 ChatClient.Builder 构建的所有 ChatClient 实例执行的所有聊天请求中共享。它们对于跨不同聊天请求常用工具很有用,但如果不谨慎使用也可能存在危险,有可能在不应该提供时也提供它们。

Default tools are shared across all the chat requests performed by all the ChatClient instances built from the same ChatClient.Builder. They are useful for tools that are commonly used across different chat requests, but they can also be dangerous if not used carefully, risking to make them available when they shouldn’t.

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

Adding Tools to ChatModel

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

When using the programmatic specification approach, you can pass the FunctionToolCallback instance to the toolCallbacks() method of ToolCallingChatOptions. The tool will only be available for the specific chat request it’s added to.

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

Adding Default Tools to ChatModel

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

When using the programmatic specification approach, you can add default tools to a ChatModel at construction time by passing the FunctionToolCallback instance to the toolCallbacks() method of the ToolCallingChatOptions instance used to create the ChatModel. If both default and runtime tools are provided, the runtime tools will completely override the default tools.

默认工具在由该 ChatModel 实例执行的所有聊天请求中共享。它们对于跨不同聊天请求常用工具很有用,但如果不谨慎使用也可能存在危险,有可能在不应该提供时也提供它们。

Default tools are shared across all the chat requests performed by that ChatModel instance. They are useful for tools that are commonly used across different chat requests, but they can also be dangerous if not used carefully, risking to make them available when they shouldn’t.

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

Dynamic Specification: @Bean

除了以编程方式指定工具外,您还可以将工具定义为 Spring bean,并让 Spring AI 使用 ToolCallbackResolver 接口(通过 SpringBeanToolCallbackResolver 实现)在运行时动态解析它们。此选项使您能够将任何 FunctionSupplierConsumerBiFunction bean 用作工具。bean 名称将用作工具名称,并且可以使用 Spring Framework 中的 @Description 注解为工具提供描述,模型会使用该描述来理解何时以及如何调用工具。如果您不提供描述,则将使用方法名称作为工具描述。但是,强烈建议提供详细描述,因为这对于模型理解工具的用途和如何使用至关重要。未能提供良好描述可能导致模型在应该使用工具时不使用工具或错误地使用工具。

Instead of specifying tools programmatically, you can define tools as Spring beans and let Spring AI resolve them dynamically at runtime using the ToolCallbackResolver interface (via the SpringBeanToolCallbackResolver implementation). This option gives you the possibility to use any Function, Supplier, Consumer, or BiFunction bean as a tool. The bean name will be used as the tool name, and the @Description annotation from Spring Framework can be used to provide a description for the tool, used by the model to understand when and how to call the tool. If you don’t provide a description, the method name will be used as the tool description. However, it’s strongly recommended to provide a detailed description because that’s paramount for the model to understand the tool’s purpose and how to use it. Failing in providing a good description can lead to the model not using the tool when it should or using it incorrectly.

@Configuration(proxyBeanMethods = false)
class WeatherTools {

    WeatherService weatherService = new WeatherService();

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

}

某些类型不支持。更多细节请参见 Function Tool Limitations

Some types are not supported. See Function Tool Limitations for more details.

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

The JSON schema for the input parameters of the tool will be generated automatically. You can use the @ToolParam annotation to provide additional information about the input parameters, such as a description or whether the parameter is required or optional. By default, all input parameters are considered required. See JSON Schema for more details.

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

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

This tool specification approach has the drawback of not guaranteeing type safety, as the tool resolution is done at runtime. To mitigate this, you can specify the tool name explicitly using the @Bean annotation and storing the value in a constant, so that you can use it in a chat request instead of hard-coding the tool name.

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

}

Adding Tools to ChatClient

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

When using the dynamic specification approach, you can pass the tool name (i.e. the function bean name) to the tools() method of ChatClient. The tool will only be available for the specific chat request it’s added to.

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

Adding Default Tools to ChatClient

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

When using the dynamic specification approach, you can add default tools to a ChatClient.Builder by passing the tool name to the defaultTools() method. If both default and runtime tools are provided, the runtime tools will completely override the default tools.

默认工具在由相同 ChatClient.Builder 构建的所有 ChatClient 实例执行的所有聊天请求中共享。它们对于跨不同聊天请求常用工具很有用,但如果不谨慎使用也可能存在危险,有可能在不应该提供时也提供它们。

Default tools are shared across all the chat requests performed by all the ChatClient instances built from the same ChatClient.Builder. They are useful for tools that are commonly used across different chat requests, but they can also be dangerous if not used carefully, risking to make them available when they shouldn’t.

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

Adding Tools to ChatModel

使用动态规范方法时,您可以将工具名称传递给用于调用 ChatModelToolCallingChatOptionstoolNames() 方法。该工具将仅适用于添加它的特定聊天请求。

When using the dynamic specification approach, you can pass the tool name to the toolNames() method of the ToolCallingChatOptions you use to call the ChatModel. The tool will only be available for the specific chat request it’s added to.

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

Adding Default Tools to ChatModel

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

When using the dynamic specification approach, you can add default tools to ChatModel at construction time by passing the tool name to the toolNames() method of the ToolCallingChatOptions instance used to create the ChatModel. If both default and runtime tools are provided, the runtime tools will completely override the default tools.

默认工具在由该 ChatModel 实例执行的所有聊天请求中共享。它们对于跨不同聊天请求常用工具很有用,但如果不谨慎使用也可能存在危险,有可能在不应该提供时也提供它们。

Default tools are shared across all the chat requests performed by that ChatModel instance. They are useful for tools that are commonly used across different chat requests, but they can also be dangerous if not used carefully, risking to make them available when they shouldn’t.

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

Function Tool Limitations

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

The following types are not currently supported as input or output types for functions used as tools:

  • Primitive types

  • Optional

  • 集合类型(例如 ListMapArraySet

  • Collection types (e.g. List, Map, Array, Set)

  • 异步类型(例如 CompletableFutureFuture

  • Asynchronous types (e.g. CompletableFuture, Future)

  • 反应式类型(例如 FlowMonoFlux )。

  • Reactive types (e.g. Flow, Mono, Flux).

使用基于方法的工具规范方法支持原始类型和集合。有关更多详细信息,请参阅 Methods as Tools

Primitive types and collections are supported using the method-based tool specification approach. See Methods as Tools for more details.

Tool Specification

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

In Spring AI, tools are modeled via the ToolCallback interface. In the previous sections, we’ve seen how to define tools from methods and functions using the built-in support provided by Spring AI (see Methods as Tools and Functions as Tools). This section will dive deeper into the tool specification and how to customize and extend it to support more use cases.

Tool Callback

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

The ToolCallback interface provides a way to define a tool that can be called by the AI model, including both definition and execution logic. It’s the main interface to implement when you want to define a tool from scratch. For example, you can define a ToolCallback from an MCP Client (using the Model Context Protocol) or a ChatClient (to build a modular agentic application).

该接口提供以下方法:

The interface provides the following methods:

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 AI provides built-in implementations for tool methods (MethodToolCallback) and tool functions (FunctionToolCallback).

Tool Definition

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

The ToolDefinition interface provides the required information for the AI model to know about the availability of the tool, including the tool name, description, and input schema. Each ToolCallback implementation must provide a ToolDefinition instance to define the tool.

该接口提供以下方法:

The interface provides the following methods:

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 Schema

See JSON Schema for more details on the input schema.

ToolDefinition.Builder 允许您使用默认实现( DefaultToolDefinition )构建 ToolDefinition 实例。

The ToolDefinition.Builder lets you build a ToolDefinition instance using the default implementation (DefaultToolDefinition).

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

Method Tool Definition

从方法构建工具时, ToolDefinition 会自动为您生成。如果您更喜欢自己生成 ToolDefinition ,则可以使用此便捷构建器。

When building tools from a method, the ToolDefinition is automatically generated for you. In case you prefer to generate the ToolDefinition yourself, you can use this convenient builder.

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

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

The ToolDefinition generated from a method includes the method name as the tool name, the method name as the tool description, and the JSON schema of the method input parameters. If the method is annotated with @Tool, the tool name and description will be taken from the annotation, if set.

有关更多详细信息,请参见 Methods as Tools

See Methods as Tools for more details.

如果您宁愿显式提供部分或所有属性,则可以使用 ToolDefinition.Builder 构建自定义 ToolDefinition 实例。

If you’d rather provide some or all of the attributes explicitly, you can use the ToolDefinition.Builder to build a custom ToolDefinition instance.

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

Function Tool Definition

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

When building tools from a function, the ToolDefinition is automatically generated for you. When you use the FunctionToolCallback.Builder to build a FunctionToolCallback instance, you can provide the tool name, description, and input schema that will be used to generate the ToolDefinition. See Functions as Tools for more details.

JSON Schema

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

When providing a tool to the AI model, the model needs to know the schema of the input type for calling the tool. The schema is used to understand how to call the tool and prepare the tool request. Spring AI provides built-in support for generating the JSON Schema of the input type for a tool via the JsonSchemaGenerator class. The schema is provided as part of the ToolDefinition.

有关 ToolDefinition 以及如何将输入模式传递给它的更多详细信息,请参见 Tool Definition

See Tool Definition for more details on the ToolDefinition and how to pass the input schema to it.

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

The JsonSchemaGenerator class is used under the hood to generate the JSON schema for the input parameters of a method or a function, using any of the strategies described in Methods as Tools and Functions as Tools. The JSON schema generation logic supports a series of annotations that you can use on the input parameters for methods and functions to customize the resulting schema.

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

This section describes two main options you can customize when generating the JSON schema for the input parameters of a tool: description and required status.

Description

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

Besides providing a description for the tool itself, you can also provide a description for the input parameters of a tool. The description can be used to provide key information about the input parameters, such as what format the parameter should be in, what values are allowed, and so on. This is useful to help the model understand the input schema and how to use it. Spring AI provides built-in support for generating the description for an input parameter using one of the following annotations:

  • 来自 Spring AI 的 @ToolParam(description = "&#8230;&#8203;")

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

  • 来自 Jackson 的 @JsonClassDescription(description = "&#8230;&#8203;")

  • @JsonClassDescription(description = "…​") from Jackson

  • 来自 Jackson 的 @JsonPropertyDescription(description = "&#8230;&#8203;")

  • @JsonPropertyDescription(description = "…​") from Jackson

  • 来自 Swagger 的 @Schema(description = "&#8230;&#8203;")

  • @Schema(description = "…​") from Swagger.

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

This approach works for both methods and functions, and you can use it recursively for nested types.

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

}

Required/Optional

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

By default, each input parameter is considered required, which forces the AI model to provide a value for it when calling the tool. However, you can make an input parameter optional by using one of the following annotations, in this order of precedence:

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

  • @ToolParam(required = false) from Spring AI

  • * 来自 Jackson 的 @JsonProperty(required = false)

  • @JsonProperty(required = false) from Jackson

  • * 来自 Swagger 的 @Schema(required = false)

  • @Schema(required = false) from Swagger

  • @Nullable from Spring Framework.

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

This approach works for both methods and functions, and you can use it recursively for nested types.

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

}

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

Defining the correct required status for the input parameter is crucial to mitigate the risk of hallucinations and ensure the model provides the right input when calling the tool. In the previous example, the email parameter is optional, which means the model can call the tool without providing a value for it. If the parameter was required, the model would have to provide a value for it when calling the tool. And if no value existed, the model would probably make one up, leading to hallucinations.

Result Conversion

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

The result of a tool call is serialized using a ToolCallResultConverter and then sent back to the AI model. The ToolCallResultConverter interface provides a way to convert the result of a tool call to a String object.

该接口提供以下方法:

The interface provides the following method:

@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 ) 序列化为 JSON,但您可以通过提供自己的 ToolCallResultConverter 实现来定制序列化过程。

The result must be a serializable type. By default, the result is serialized to JSON using Jackson (DefaultToolCallResultConverter), but you can customize the serialization process by providing your own ToolCallResultConverter implementation.

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

Spring AI relies on the ToolCallResultConverter in both method and function tools.

Method Tool Call Result Conversion

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

When building tools from a method with the declarative approach, you can provide a custom ToolCallResultConverter to use for the tool by setting the resultConverter() attribute of the @Tool annotation.

class CustomerTools {

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

}

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

If using the programmatic approach, you can provide a custom ToolCallResultConverter to use for the tool by setting the resultConverter() attribute of the MethodToolCallback.Builder.

有关更多详细信息,请参见 Methods as Tools

See Methods as Tools for more details.

Function Tool Call Result Conversion

当使用编程方法从函数构建工具时,您可以通过设置 FunctionToolCallback.BuilderresultConverter() 属性,为工具提供自定义的 ToolCallResultConverter

When building tools from a function using the programmatic approach, you can provide a custom ToolCallResultConverter to use for the tool by setting the resultConverter() attribute of the FunctionToolCallback.Builder.

有关更多详细信息,请参阅 Functions as Tools

See Functions as Tools for more details.

Tool Context

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

Spring AI supports passing additional contextual information to tools through the ToolContext API. This feature allows you to provide extra, user-provided data that can be used within the tool execution along with the tool arguments passed by the AI model.

tool context
class CustomerTools {

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

}

ToolContext 将使用用户在调用 ChatClient 时提供的数据进行填充。

The ToolContext is populated with the data provided by the user when invoking 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);

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

None of the data provided in the ToolContext is sent to the AI model.

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

Similarly, you can define tool context data when invoking the ChatModel directly.

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 将是两者的合并,其中运行时选项优先于默认选项。

If the toolContext option is set both in the default options and in the runtime options, the resulting ToolContext will be the merge of the two, where the runtime options take precedence over the default options.

Return Direct

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

By default, the result of a tool call is sent back to the model as a response. Then, the model can use the result to continue the conversation.

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

There are cases where you’d rather return the result directly to the caller instead of sending it back to the model. For example, if you build an agent that relies on a RAG tool, you might want to return the result directly to the caller instead of sending it back to the model for unnecessary post-processing. Or perhaps you have certain tools that should end the reasoning loop of the agent.

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

Each ToolCallback implementation can define whether the result of a tool call should be returned directly to the caller or sent back to the model. By default, the result is sent back to the model. But you can change this behavior per tool.

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

The ToolCallingManager, responsible for managing the tool execution lifecycle, is in charge of handling the returnDirect attribute associated with the tool. If the attribute is set to true, the result of the tool call is returned directly to the caller. Otherwise, the result is sent back to the model.

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

If multiple tool calls are requested at once, the returnDirect attribute must be set to true for all the tools to return the results directly to the caller. Otherwise, the results will be sent back to the model.

return direct
  1. 当我们要使工具可用于模型时,我们将其定义包含在聊天请求中。如果我们要将工具执行的结果直接返回给调用方,我们将 returnDirect 属性设置为 true

  2. When we want to make a tool available to the model, we include its definition in the chat request. If we want the result of the tool execution to be returned directly to the caller, we set the returnDirect attribute to true.

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

  4. When the model decides to call a tool, it sends a response with the tool name and the input parameters modeled after the defined schema.

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

  6. The application is responsible for using the tool name to identify and execute the tool with the provided input parameters.

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

  8. The result of the tool call is processed by the application.

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

  10. The application sends the tool call result directly to the caller, instead of sending it back to the model.

Method Return Direct

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

When building tools from a method with the declarative approach, you can mark a tool to return the result directly to the caller by setting the returnDirect attribute of the @Tool annotation to true.

class CustomerTools {

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

}

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

If using the programmatic approach, you can set the returnDirect attribute via the ToolMetadata interface and pass it to the MethodToolCallback.Builder.

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

有关更多详细信息,请参见 Methods as Tools

See Methods as Tools for more details.

Function Return Direct

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

When building tools from a function with the programmatic approach, you can set the returnDirect attribute via the ToolMetadata interface and pass it to the FunctionToolCallback.Builder.

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

有关更多详细信息,请参阅 Functions as Tools

See Functions as Tools for more details.

Tool Execution

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

The tool execution is the process of calling the tool with the provided input arguments and returning the result. The tool execution is handled by the ToolCallingManager interface, which is responsible for managing the tool execution lifecycle.

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 Starter, DefaultToolCallingManagerToolCallingManager 接口的自动配置实现。您可以通过提供自己的 ToolCallingManager bean 来自定义工具执行行为。

If you’re using any of the Spring AI Spring Boot Starters, DefaultToolCallingManager is the autoconfigured implementation of the ToolCallingManager interface. You can customize the tool execution behavior by providing your own ToolCallingManager bean.

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

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

By default, Spring AI manages the tool execution lifecycle transparently for you from within each ChatModel implementation. But you have the possibility to opt-out of this behavior and control the tool execution yourself. This section describes these two scenarios.

Framework-Controlled Tool Execution

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

When using the default behavior, Spring AI will automatically intercept any tool call request from the model, call the tool and return the result to the model. All of this is done transparently for you by each ChatModel implementation using a ToolCallingManager.

framework manager
  1. 当我们要使工具可用于模型时,我们将其定义包含在聊天请求 ( Prompt ) 中,并调用 ChatModel API,该 API 将请求发送到 AI 模型。

  2. When we want to make a tool available to the model, we include its definition in the chat request (Prompt) and invoke the ChatModel API which sends the request to the AI model.

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

  4. When the model decides to call a tool, it sends a response (ChatResponse) with the tool name and the input parameters modeled after the defined schema.

  5. ChatModel 将工具调用请求发送到 ToolCallingManager API。

  6. The ChatModel sends the tool call request to the ToolCallingManager API.

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

  8. The ToolCallingManager is responsible for identifying the tool to call and executing it with the provided input parameters.

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

  10. The result of the tool call is returned to the ToolCallingManager.

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

  12. The ToolCallingManager returns the tool execution result back to the ChatModel.

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

  14. The ChatModel sends the tool execution result back to the AI model (ToolResponseMessage).

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

  16. The AI model generates the final response using the tool call result as additional context and sends it back to the caller (ChatResponse) via the ChatClient.

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

Currently, the internal messages exchanged with the model regarding the tool execution are not exposed to the user. If you need to access these messages, you should use the user-controlled tool execution approach.

确定工具调用是否符合执行条件的逻辑由 ToolExecutionEligibilityPredicate 接口处理。默认情况下,工具执行资格通过检查 ToolCallingChatOptionsinternalToolExecutionEnabled 属性是否设置为 true (默认值),以及 ChatResponse 是否包含任何工具调用来确定。

The logic determining whether a tool call is eligible for execution is handled by the ToolExecutionEligibilityPredicate interface. By default, the tool execution eligibility is determined by checking if the internalToolExecutionEnabled attribute of ToolCallingChatOptions is set to true (the default value), and if the ChatResponse contains any tool calls.

public class DefaultToolExecutionEligibilityPredicate implements ToolExecutionEligibilityPredicate {

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

}

您可以在创建 ChatModel bean 时提供自己的 ToolExecutionEligibilityPredicate 自定义实现。

You can provide your custom implementation of ToolExecutionEligibilityPredicate when creating the ChatModel bean.

User-Controlled Tool Execution

在某些情况下,您宁愿自己控制工具执行生命周期。您可以通过将 ToolCallingChatOptionsinternalToolExecutionEnabled 属性设置为 false 来实现此目的。

There are cases where you’d rather control the tool execution lifecycle yourself. You can do so by setting the internalToolExecutionEnabled attribute of ToolCallingChatOptions to false.

当您使用此选项调用 ChatModel 时,工具执行将委托给调用方,从而让您完全控制工具执行生命周期。您有责任检查 ChatResponse 中的工具调用并使用 ToolCallingManager 执行它们。

When you invoke a ChatModel with this option, the tool execution will be delegated to the caller, giving you full control over the tool execution lifecycle. It’s your responsibility checking for tool calls in the ChatResponse and executing them using the ToolCallingManager.

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

The following example demonstrates a minimal implementation of the user-controlled tool execution approach:

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 提供的对工具执行的内置支持。但是,没有什么能阻止您实现自己的工具执行逻辑。

When choosing the user-controlled tool execution approach, we recommend using a ToolCallingManager to manage the tool calling operations. This way, you can benefit from the built-in support provided by Spring AI for tool execution. However, nothing prevents you from implementing your own tool execution logic.

以下示例展示了用户控制的工具执行方法与 ChatMemory API 结合使用的最小实现:

The next examples shows a minimal implementation of the user-controlled tool execution approach combined with the usage of the ChatMemory API:

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

Exception Handling

当工具调用失败时,异常会作为 ToolExecutionException 传播,可以捕获它来处理错误。 ToolExecutionExceptionProcessor 可用于处理具有两种结果的 ToolExecutionException :要么生成要发送回 AI 模型的错误消息,要么抛出异常由调用者处理。

When a tool call fails, the exception is propagated as a ToolExecutionException which can be caught to handle the error. A ToolExecutionExceptionProcessor can be used to handle a ToolExecutionException with two outcomes: either producing an error message to be sent back to the AI model or throwing an exception to be handled by the caller.

@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 Starter, DefaultToolExecutionExceptionProcessorToolExecutionExceptionProcessor 接口的自动配置实现。默认情况下,错误消息会发送回模型。 DefaultToolExecutionExceptionProcessor 构造函数允许您将 alwaysThrow 属性设置为 truefalse 。如果 true ,则会抛出异常,而不是将错误消息发送回模型。

If you’re using any of the Spring AI Spring Boot Starters, DefaultToolExecutionExceptionProcessor is the autoconfigured implementation of the ToolExecutionExceptionProcessor interface. By default, the error message is sent back to the model. The DefaultToolExecutionExceptionProcessor constructor lets you set the alwaysThrow attribute to true or false. If true, an exception will be thrown instead of sending an error message back to the model.

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

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

If you defined your own ToolCallback implementation, make sure to throw a ToolExecutionException when an error occurs as part of the tool execution logic in the call() method.

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

The ToolExecutionExceptionProcessor is used internally by the default ToolCallingManager (DefaultToolCallingManager) to handle exceptions during tool execution. See Tool Execution for more details about the tool execution lifecycle.

Tool Resolution

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

The main approach for passing tools to a model is by providing the ToolCallback(s) when invoking the ChatClient or the ChatModel, using one of the strategies described in Methods as Tools and Functions as Tools.

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

However, Spring AI also supports resolving tools dynamically at runtime using the ToolCallbackResolver interface.

public interface ToolCallbackResolver {

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

}

使用这种方法时:

When using this approach:

  • 在客户端,您向 ChatClientChatModel 提供工具名称,而不是 ToolCallback (s)。

  • On the client-side, you provide the tool names to the ChatClient or the ChatModel instead of the ToolCallback(s).

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

  • On the server-side, a ToolCallbackResolver implementation is responsible for resolving the tool names to the corresponding ToolCallback instances.

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

By default, Spring AI relies on a DelegatingToolCallbackResolver that delegates the tool resolution to a list of ToolCallbackResolver instances:

  • SpringBeanToolCallbackResolver 从类型为 FunctionSupplierConsumerBiFunction 的 Spring bean 中解析工具。更多详细信息请参见 Dynamic Specification: @Bean

  • The SpringBeanToolCallbackResolver resolves tools from Spring beans of type Function, Supplier, Consumer, or BiFunction. See Dynamic Specification: @Bean for more details.

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

  • The StaticToolCallbackResolver resolves tools from a static list of ToolCallback instances. When using the Spring Boot Autoconfiguration, this resolver is automatically configured with all the beans of type ToolCallback defined in the application context.

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

If you rely on the Spring Boot Autoconfiguration, you can customize the resolution logic by providing a custom ToolCallbackResolver bean.

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

ToolCallbackResolverToolCallingManager 内部使用,以在运行时动态解析工具,支持 Framework-Controlled Tool ExecutionUser-Controlled Tool Execution

The ToolCallbackResolver is used internally by the ToolCallingManager to resolve tools dynamically at runtime, supporting both Framework-Controlled Tool Execution and User-Controlled Tool Execution.

Observability

工具调用包括可观察性支持,通过 spring.ai.tool observations 测量完成时间并传播跟踪信息。请参见 Tool Calling Observability

Tool calling includes observability support with spring.ai.tool observations that measure completion time and propagate tracing information. See Tool Calling Observability.

可选地,Spring AI 可以将工具调用参数和结果导出为 span 属性,出于敏感性原因默认禁用。详细信息: Tool Call Arguments and Result Data

Optionally, Spring AI can export tool call arguments and results as span attributes, disabled by default for sensitivity reasons. Details: Tool Call Arguments and Result Data.

Logging

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

All the main operations of the tool calling features are logged at the DEBUG level. You can enable the logging by setting the log level to DEBUG for the org.springframework.ai package.