【JavaEE】【SpringAI】Tool Calling(工具调用)

10@TOC

一、概述

随着⼤语⾔模型(LLM)能⼒的⻜速发展,我们不再满⾜于仅仅让它们⽣成⽂本或回答问题。我们期望它们能成为真正的智能助⼿,能够与外部世界交互,执⾏具体任务,⽐如查询数据库、发送邮件或分析数据.

为了解决这⼀挑战,⼯具调⽤(ToolCalling)应运⽽⽣。ToolCalling(⼯具调⽤),是AI应⽤程序中的⼀种常⻅模式,允许⼤语⾔模型(LLM)根据⽤⼾请求,智能地选择、调⽤外部⼯具(如函数、API、服务)并获取结果的技术流程,从⽽增强其功能。

Tool Calling 和 FunctionCalling关系:

Function Calling 是Tool Calling早期更为流⾏的术语. 它是指LLM请求调⽤⼀个开发者预定义的函数(Function), 这⾥的"函数"就是你代码中的⼀个⽅法. ToolCalling是⼀个更通⽤、更⼴泛的概念,不仅包含了FunctionCalling,还涵盖了调⽤其他类型的⼯具.

应⽤场景

  1. 信息检索
    ⼤模型可以借助Tool从外部资源(如数据库、Web服务、⽂件系统或者WEB搜索引擎)检索信息.
  2. 采取⾏动
    ⼤模型可以借助Tool完成⼀些执⾏操作(如发送电⼦邮件、在数据库中创建新记录、提交表单或触发⼯作流)

二、快速使用

接着spring ai alibaba的依赖接着使用

2.1 添加依赖

添加 tool calling的依赖

xml 复制代码
        <dependency>
            <groupId>com.github.victools</groupId>
            <artifactId>jsonschema-generator</artifactId>
            <version>4.37.0</version>
        </dependency>

2.2 定义工具

使⽤注解 @Tool 来定义,为了帮助模型了解何时可以调⽤此⼯具,⼯具的描述⾮常重要,需要提供⼯具的详细说明。

定义一个返回实时时间的工具

java 复制代码
package com.spring.alibaba.tools;

import org.springframework.ai.tool.annotation.Tool;
import org.springframework.context.i18n.LocaleContextHolder;

import java.time.LocalDateTime;

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

2.3 使用工具

java 复制代码
package com.spring.alibaba.controller;

import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import com.spring.alibaba.tools.DateTimeTools;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping("/chat")
@RestController
public class ChatController {

    private ChatClient client;
    public ChatController(DashScopeChatModel model) {
        this.client = ChatClient.builder(model).build();
    }
    @RequestMapping("/call")
    public String call(String message) {
        return client.prompt()
                .tools(new DateTimeTools())//使用工具
                .user(message).call().content();
    }
}

三、原理

官方文档:https://docs.spring.io/spring-ai/reference/api/tools.html

3.1 请求响应流程

Tool Calling背后是⼀套标准化的 请求-响应 流程。SpringAI提供了⼀套灵活的⼯具调⽤机制,让开发者能够⽤统⼀的⽅式定义、解析和执⾏⼯具。

  1. 注册⼯具:在向AI模型发送请求时,应⽤程序需要提前告诉模型有哪些⼯具可⽤,包括⼯具的名称(name)、描述(description)和参数格式(inputschema)
  2. 模型决策:AI模型根据对话内容判断是否需要调⽤⼯具,当模型决定调⽤⼯具时,它会发送⼀个⼯具调⽤请求,其中包含⼯具名称和符合预定义的输⼊参数。
  3. 查找并执⾏:应⽤程序接收到这个"⼯具调⽤请求"后,会根据⼯具名称找到对应的⼯具,并⽤模型提供的参数来执⾏它
  4. 获取结果:⼯具执⾏后会产⽣⼀个结果(⽐如,查询到了今天的温度是25度),应⽤程序会处理返回的结果
  5. 返回结果:应⽤程序将这个⼯具执⾏的结果再次发送给AI模型
  6. ⽣成最终回复:AI模型结合⼯具返回的结果,组织语⾔,⽣成最终的⽤⼾回复

3.2 源码

Spring AI 为⽅法转⼯具(ToolCallback )提供了两种内置⽅式

  • 声明式:通过 @Tool 注解实现
  • 编程式:通过底层的 MethodToolCallback 实现

3.2.1 声明式定义⼯具

@Tool 定义⼯具

java 复制代码
package org.springframework.ai.tool.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.ai.tool.execution.DefaultToolCallResultConverter;
import org.springframework.ai.tool.execution.ToolCallResultConverter;

/**
 * Marks a method as a tool in Spring AI.
 *
 * @author Thomas Vitale
 * @since 1.0.0
 */
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Tool {

	/**
	 * The name of the tool. If not provided, the method name will be used.
	 */
	String name() default "";

	/**
	 * The description of the tool. If not provided, the method name will be used.
	 */
	String description() default "";

	/**
	 * Whether the tool result should be returned directly or passed back to the model.
	 */
	boolean returnDirect() default false;

	/**
	 * The class to use to convert the tool call result to a String.
	 */
	Class<? extends ToolCallResultConverter> resultConverter() default DefaultToolCallResultConverter.class;

}
  • Name:指定⼯具的名称,如果不指定则默认使⽤⽅法名称。AI模型在调⽤⼯具时使⽤此名称来标识⼯具.因此,不允许在同⼀类中有两个同名的⼯具
  • Description: ⼯具的描述,模型可以使⽤它来了解何时以及如何调⽤⼯具,如果不指定则使⽤⽅法名,建议详细清晰的描述⼯具的功能,这对于⼯具的使⽤⾄关重要,直接影响⼤模型的使⽤效果
  • returnDirect: 指定⼯具执⾏的结果是直接返回,还是要发给⼤模型。默认发送给⼤模型
  • resultConverter: 指定⼯具执⾏结果转换器。SpringAI内置了⼀个, 默认将⼯具调⽤结果转换为String, 如果有特殊业务需求可以⾃⾏实现

为 ChatClient 添加默认⼯具,通过 ChatClient.Builder 的 defaultTools() ⽅法传⼊⼯具类实例来添加默认⼯具。若同时提供默认⼯具和运⾏时⼯具,运⾏时⼯具将覆盖默认⼯具。

java 复制代码
    private ChatClient client;
    public ChatController(DashScopeChatModel model) {
        this.client = ChatClient.builder(model)
                .defaultTools(new DateTimeTools())//模型默认使用工具
                .build();
    }

为 ChatModel 添加⼯具,通过调⽤ ChatModel 时使⽤的 ToolCallingChatOptions 中的toolCallbacks() ⽅法传⼊⼯具类实例

java 复制代码
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 的 ToolCallingChatOptions 实例的 toolCallbacks() 方法来在构建时向 ChatModel 添加默认工具。如果同时提供了默认工具和运行时工具,则运行时工具将完全覆盖默认工具。

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

3.2.2 编程式定义⼯具

除了声明式之外,我们也可以通过编程式构建 MethodToolCallback 将⽅法转为⼯具.

定义⼀个⽅法

java 复制代码
package com.spring.alibaba.tools;

public class WeatherTools {
    String getCurrentWeatherByCityName(String cityName) {
        switch (cityName){
            case " 北京 ":
                return " 北京今天天⽓: 晴空万⾥ ";
            case " 上海 ":
                return " 上海今天天⽓: 电闪雷鸣 ";
            case "⼴州 ":
                return "⼴州今天天⽓: 细⾬蒙蒙 ";
            default:
                return " 没有该城市的天⽓信息 ";
        }
    }
}

定义ToolCallback,通过 toolCallbacks 为 ChatClient添加⼯具

java 复制代码
    @RequestMapping("/call2")
    public String call2(String message) {
        Method method = ReflectionUtils.findMethod(WeatherTools.class, "getCurrentWeatherByCityName", String.class);
        if (method == null) {
            throw new IllegalArgumentException("Method not found");
        }

        ToolCallback toolCallback = MethodToolCallback.builder()
                .toolDefinition(ToolDefinitions.builder(method)
                        .name("getCurrentWeatherByCityName")
                        .description("根据城市名称获取当前天气")
                        .build())
                .toolMethod(method)
                .toolObject(new WeatherTools())
                .build();
        
        return client.prompt()
                .toolCallbacks(toolCallback)//使用工具
                .user(message).call().content();
    }

MethodToolCallback.Builder 允许您构建 MethodToolCallback 实例并提供有关工具的关键信息:

  • toolDefinition:定义工具名称、描述和输入模式的 ToolDefinition 实例。您可以使用 ToolDefinition.Builder 类构建它。必需。
  • toolMetadata:定义附加设置的 ToolMetadata 实例,例如结果是否应直接返回给客户端,以及要使用的结果转换器。您可以使用 ToolMetadata.Builder 类构建它。
  • toolMethod:表示工具方法的 Method 实例。必需。
  • toolObject:包含工具方法的对象实例。如果方法是静态的,则可以省略此参数。
  • toolCallResultConverter:用于将工具调用结果转换为 String 对象以发送回 AI 模型的 ToolCallResultConverter 实例。如果未提供,将使用默认转换器(DefaultToolCallResultConverter)。

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

  • name:工具的名称。如果未提供,将使用方法名称。AI 模型使用此名称在调用工具时识别它。因此,同一类中不允许有两个同名的工具。名称在特定聊天请求可用的所有工具中必须是唯一的。
  • description:工具的描述,模型可以使用它来理解何时以及如何调用工具。如果未提供,方法名称将用作工具描述。但是,强烈建议提供详细描述,因为这对模型理解工具的目的和如何使用至关重要。未能提供好的描述可能导致模型在应该使用工具时没有使用,或者使用不正确。
  • inputSchema:工具输入参数的 JSON 模式。如果未提供,模式将根据方法参数自动生成。您可以使用 @ToolParam 注解提供有关输入参数的额外信息,例如描述或参数是必需还是可选。默认情况下,所有输入参数都被认为是必需的。

为 ChatModel 添加⼯具,可以通过调⽤ ChatModel 时使⽤的 ToolCallingChatOptions 中的 toolCallbacks() ⽅法传⼊ MethodToolCallback 实例

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

四、工具规范

4.1 ToolCallback

ToolCallback 是⼀个⽤于与AI模型交互的回调接⼝,允许开发者定义可被AI调⽤的外部⼯具, 在AI应⽤(⽐如⼤语⾔模型)中集成"⼯具"(Tools),让AI模型可以在需要时调⽤外部功能

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

java 复制代码
public interface ToolCallback {

	/**
	 *  返回该⼯具的元信息描述
     * ToolDefinition 包含⼯具名称、描述、参数列表等结构化信息,供 AI 模型理解 "什么情况下该调⽤这个⼯具"以及"需要传什么参数"。
     *  必须实现,是函数调⽤机制的核⼼部分
	 */
	ToolDefinition getToolDefinition();

	/**
	 *提供额外的⾮功能性元数据,主要⽤于框架层⾯的扩展性⽀持,不影响核⼼逻辑。
     * 使⽤了 default ⽅法,意味着⼦类可以不重写,默认返回空的元数据对象。
	 */
	default ToolMetadata getToolMetadata() {
		return ToolMetadata.builder().build();
	}

	/**
	 * 抽象⽅法,必须由实现类提供具体逻辑。
     * 接收⼀个字符串输⼊(通常是JSON 格式的参数),执⾏业务逻辑,并返回结果字符串。 
     * 结果会传回给 AI 模型,供其继续推理或⽣成回复。
	 */
	String call(String toolInput);

	/**
	 * 带上下⽂的调⽤⽅法,也是默认实现。
     * 如果toolContext有值, 则不⽀持, 主要⽤于被⼦类重写⽀持上下⽂功能
	 */
	default String call(String toolInput, @Nullable ToolContext tooContext) {
		if (tooContext != null && !tooContext.getContext().isEmpty()) {
			throw new UnsupportedOperationException("Tool context is not supported!");
		}
		return call(toolInput);
	}

}

4.2 MethodToolCallback

MethodToolCallback 是Spring AI 框架中实现 Tool Calling的核⼼机制之⼀.它⽤于将普通Java⽅法包装成AI模型可调⽤的"⼯具"(Tool)

java 复制代码
public final class MethodToolCallback implements ToolCallback {
    //⼯具默认执⾏的转换器
    private static final ToolCallResultConverter DEFAULT_RESULT_CONVERTER = new
            DefaultToolCallResultConverter();
//⼯具默认元数据
    private static final ToolMetadata DEFAULT_TOOL_METADATA =
            ToolMetadata.builder().build();
//⼯具定义,也称为⼯具的元信息:名字、描述、参数列表等,告诉 AI "我能⼲啥"
    private final ToolDefinition toolDefinition;
//⼯具额外元数据(如权限, 分类)
    private final ToolMetadata toolMetadata;
//真正要执⾏的那个Java ⽅法对象(通过反射获取)

    private final Method toolMethod;
//⼯具对象:如果⽅法不是静态的,需要这个对象来调⽤(即object.method()
    @Nullable
    private final Object toolObject;
//⼯具转换器:把⽅法返回值转成字符串,返回给 AI(默认⽤JSON)

    private final ToolCallResultConverter toolCallResultConverter;
}

4.3ToolDefinition

ToolDefinition 接⼝提供AI模型识别⼯具可⽤性所需的信息,包括⼯具名称、描述及输⼊模式.每个

ToolCallback 实现必须提供ToolDefinition实例来定义⼯具

java 复制代码
public interface ToolDefinition {

    /**
     *⼯具名称.在提供给模型的⼯具集中是唯⼀的
     */
    String name();
    /**
     * AI模型使⽤⼯具描述来确定⼯具的功能
     */
    String description();
    /**
     *⽤于调⽤⼯具的参数的模式
     */
    String inputSchema();
}

4.4 ToolContext

Spring AI ⽀持通过ToolContextAPI向⼯具传递额外的上下⽂信息.此功能允许开发⼈员提供⽤⼾⾃定义的额外数据,这些数据可与AI模型传递的⼯具参数⼀起在⼯具执⾏过程中使⽤

4.5 Return Direct

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

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

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

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

  1. 当我们需要使工具可供模型使用时,我们会在聊天请求中包含其定义。如果希望工具执行结果直接返回给调用者,则将 returnDirect 属性设置为 true。
  2. 当模型决定调用工具时,它会发送一个包含工具名称和根据定义架构建模的输入参数的响应。
  3. 应用程序负责使用工具名称识别工具并使用提供的输入参数执行工具。
  4. 工具调用的结果由应用程序处理。
  5. 应用程序将工具调用结果直接发送给调用者,而不是将其发送回模型。

五、工具执行

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

默认情况下,SpringAI在每个 ChatModel 实现中透明地管理⼯具执⾏⽣命周期。但开发⼈员可选择退出此⾏为,⾃⾏控制⼯具执⾏.

  1. 当需要向模型提供⼯具时,我们将其定义包含在聊天请求(Prompt )中,并调⽤ChatModel API 将请求发送⾄AI模型.
  2. 当模型决定调⽤⼯具时,它会发送包含⼯具名称及符合定义模式的输⼊参数的响应(ChatResponse)
  3. ChatModel 将⼯具调⽤请求发送⾄ToolCallingManager API.
  4. ToolCallingManager 负责识别需调⽤的⼯具并使⽤提供的输⼊参数执⾏该⼯具
  5. ⼯具调⽤结果返回⾄ ToolCallingManager
  6. ToolCallingManager 将⼯具执⾏结果返回给 ChatModel
  7. ChatModel 将⼯具执⾏结果返回AI模型(ToolResponseMessage ).
  8. AI模型利⽤⼯具调⽤结果作为附加上下⽂⽣成最终响应,并通过ChatClient 将其返回调⽤⽅(ChatResponse )
相关推荐
于先生吖2 小时前
高并发稳定运营,JAVA 动漫短剧小程序 + H5 源码
java·开发语言·小程序
云和数据.ChenGuang2 小时前
鸿蒙应用对接DeepSeek大模型:构建智能问答系统的技术实践
java·华为·langchain·harmonyos·euler·openduler
曹牧2 小时前
在 Eclipse 中变更 SVN 地址
java·svn·eclipse
中科三方2 小时前
域名NS记录修改全攻略:规则、误区、实操流程和常见问题
java·后端·spring
墨白曦煜2 小时前
告别 Thread.stop():并发编程的最高礼仪——两阶段终止模式
java
重生之我是Java开发战士2 小时前
【笔试强训】Week1:点击消除,数组中两个字符串的最小距离,dd爱框框,腐烂的苹果,大数乘法
java·开发语言·算法
七夜zippoe2 小时前
设计模式在Spring等框架中的应用:模板方法、工厂、适配器等
java·spring·设计模式·模板·适配器·工厂
Full Stack Developme2 小时前
SpringBoot配置文件优先级详解
java·spring boot·后端
❀͜͡傀儡师2 小时前
使用 Docker 一键部署 EasyNVR 视频流媒体平台
java·docker·媒体