一.Tool Calling概述

随着大语言模型 (LLM) 能力的飞速发展,我们不再满足于仅仅让它们生成文本或回答问题。我们期望它们能成为真正的智能助手,能够与外部世界交互,执行具体任务,比如查询数据库、发送邮件或分析数据。为了解决这一挑战,工具调用 (Tool Calling) 应运而生。

Tool Calling (工具调用),是 AI 应用程序中的一种常见模式,允许大语言模型 (LLM) 根据用户请求,智能地选择、调用外部工具(如函数、API、服务)并获取结果的技术流程,从而增强其功能。

可以把 LLM 想象成为一个知识渊博、无所不知的博士,不论你问他什么理论问题,他都可以对答如流。但如果你问他 “帮我预订一下今晚公司附近那家最热门的意大利餐厅”,他就会束手无策 —— 因为他虽然能计算出公司附近最热门的意大利餐厅,但他没有连接订座系统,无法订座。

Tool Calling 就是解决这个问题的,它就像给这个博士配备了一个万能遥控器,这个遥控器有很多按钮,这些按钮可以执行一些具体任务,比如博士思考餐厅名称,按钮来执行订座。

Tool Calling 让 LLM 从一个 “无所不知的学者” 变成了一个 “无所不能的指挥家”,它负责思考和规划,而具体的工作则由外部工具执行。

二.和Function Calling 什么关系?

官方介绍如下:

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.

摘自 Tool Calling :: Spring AI Reference

Function Calling 是 Tool Calling 早期更为流行的术语。它是指 LLM 请求调用一个开发者预定义的函数 (Function),这里的 "函数" 就是你代码中的一个方法。Tool Calling 是一个更通用、更广泛的概念,不仅包含了 Function Calling,还涵盖了调用其他类型的工具。

Function Calling 好比是万能遥控器上的一个具体的按钮,比如 "开机" 按钮,它非常具体,按下去就直接执行开机这个单一操作。Tool Calling 则是整个万能遥控器的概念,这个遥控器上不仅有 "开机" 按钮,还有 "调音量"、"换台"、"投屏" 等按钮。它是一个更上层、更通用的抽象,涵盖了函数、API、查询等各种工具。

简单来说:在 Spring AI 里,定义一个 Tool (工具),它通常就是一个 Function (函数),我们可以认为 Function Calling 是实现 Tool Calling 的具体技术方式。两者在日常讨论中经常混用,但 Tool Calling 这个词更全面。

三.应用场景

1. 信息检索

大模型可以借助 Tool 从外部资源(如数据库、Web 服务、文件系统或者 WEB 搜索引擎)检索信息。

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

2. 采取行动

大模型可以借助 Tool 完成一些执行操作(如发送电子邮件、在数据库中创建新记录、提交表单或触发工作流)

如:模型可以生成预订北京旅行的计划。但是,该模型不具备执行计划的能力。这就是工具的用武之地。它们可用于执行模型生成的计划。

四.快速应用

1.添加依赖

 <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webflux</artifactId>
        </dependency>
       
        <!--   tool calling     -->
        <dependency>
            <groupId>com.github.victools</groupId>
            <artifactId>jsonschema-generator</artifactId>
            <!-- 可根据实际情况使用最新稳定版本 -->
            <version>4.37.0</version>
        </dependency>
    </dependencies>

 <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.alibaba.cloud.ai</groupId>
                <artifactId>spring-ai-alibaba-bom</artifactId>
                <version>1.0.0.2</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

2.配置文件

spring:
  ai:
    dashscope:
      api-key: ${DASH_SCOPE_API_KEY} #阿里百炼平台申请的api-key

3.调用工具代码与结果演示

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

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

    private final ChatClient client;

    public ChatController(DashScopeChatModel dashScopeChatModel) {
        this.client = ChatClient.builder(dashScopeChatModel).build();
    }

    @RequestMapping("/call")
    public String chat(String message) {
        return client.prompt()
                .user(message)
                .call()
                .content();
    }

}

4.定义工具

tool calling依赖上面已经添加

import org.springframework.ai.tool.annotation.Tool;

import java.time.LocalDateTime;

public class DateTimeTools {

    @Tool(description = "A tool to get the current date and time")
    public String getCurrentDateTime() {
        return LocalDateTime.now().atZone(java.time.ZoneId.systemDefault()).toString();
    }

}

5.应用工具

  @RequestMapping("/call")
    public String chat(String message) {
        return client.prompt()
                .user(message)
                .tools(new DateTimeTools()) //添加工具
                .call()
                .content();
    }

6.运行结果

五.Tool Calling 原理

    1.Tool Calling 请求响应流程

    a) 注册工具: 在向 AI 模型发送请求时,应用程序需要提前告诉模型有哪些工具可用,包括工具的名称 (name)、描述 (description) 和参数格式 (input schema)

    描述 (Description) 是灵魂:这是模型决定是否调用工具的核心依据,描述必须清晰准确。一个模糊的描述 (如 "处理数据") 会导致模型无法正确调用。一个清晰的描述 (如 "根据城市名称查询该城市的当前天气温度") 则能极大提高调用的准确率

    b) 模型决策: AI 模型根据对话内容判断是否需要调用工具,当模型决定调用工具时,它会发送一个工具调用请求,其中包含工具名称和符合预定义的输入参数。

    c) 查找并执行: 应用程序接收到这个 "工具调用请求" 后,会根据工具名称找到对应的工具,并用模型提供的参数来执行它。

    d) 获取结果: 工具执行后会产生一个结果 (比如,查询到了今天的温度是 25 度),应用程序会处理返回的结果。

    e) 返回结果: 应用程序将这个工具执行的结果再次发送给 AI 模型。

    f)  生成最终回复: AI 模型结合工具返回的结果,组织语言,生成最终的用户回复。

    2.Tool Calling 部分源码解释

    Spring AI 为方法转工具(即 ToolCallback)提供了两种内置方式:

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

    a) 声明式定义工具

    Tool注解

    @Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Tool {
        String name() default "";
    
        String description() default "";
    
        boolean returnDirect() default false;
    
        Class<? extends ToolCallResultConverter> resultConverter() default DefaultToolCallResultConverter.class;
    }
    • Name:指定工具的名称,如果不指定则默认使用方法名称。AI 模型在调用工具时使用此名称来标识工具。因此,不允许在同一类中有两个同名的工具。

    • Description:工具的描述,模型可以使用它来了解何时以及如何调用工具,如果不指定则使用方法名。建议详细清晰地描述工具的功能,这对于工具的使用至关重要,直接影响大模型的使用效果。

    • returnDirect:指定工具执行的结果是直接返回,还是要发给大模型,默认发送给大模型。如果为true 上述流程直接从4-->6不会在发给大模型处理之后返回用户了。

    • resultConverter:指定工具执行结果转换器。Spring AI 内置了一个,默认将工具调用结果转换为 String,如果有特殊业务需求可以自行实现。

    input schema是什么?

    JSON Schema 是一种用于描述和验证 JSON 数据结构的规范。它定义了 JSON 数据应具备的格式、类型、字段、约束条件等,就像是一份数据模板数据说明书,用来确保 JSON 数据符合预期的结构。

    我们可以把 JSON Schema 想象成一个「表格填写指南」:

    • 表格 = 要接收的 JSON 数据
    • 填写指南 = JSON Schema(说明哪些项必填、是什么类型、有什么限制)

    例如

    {
      "name": "Alice",
      "age": 28,
      "email": "alice@example.com",
      "hobbies": ["reading", "swimming"]
    }
    
    {
      "name": "Alice",
      "age": 28,
      "email": "alice@example.com",
      "hobbies": ["reading", "swimming"]
    }

    对应的 JSON Schema

    {
      "$schema": "https://json-schema.org/draft/2020-12/schema",
      "$id": "https://example.com/user.schema.json",
      "title": "User",
      "type": "object",
      "properties": {
        "name": {
          "type": "string",
          "description": "用户姓名"
        },
        "age": {
          "type": "integer",
          "minimum": 0,
          "description": "用户年龄"
        },
        "email": {
          "type": "string",
          "format": "email",
          "description": "电子邮箱地址"
        },
        "hobbies": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "optional": true
        }
      },
      "required": ["name", "age", "email"]
    }

    工具调用流程中,JSON Schema 用于定义工具的输入参数格式。

    开发人员可以通过 @ToolParam 注解为输入参数提供额外信息(如描述、是否必需等),默认情况下所有输入参数均为必需参数。

    import java.time.LocalDateTime;
    import java.time.format.DateTimeFormatter;
    import org.springframework.ai.tool.annotation.Tool;
    import org.springframework.ai.tool.annotation.ToolParam;
    
    class DateTimeTools {
    
        @Tool(description = "Set a user alarm for the given time")
        void setAlarm(@ToolParam(description = "Time in ISO-8601 format") String time) {
            LocalDateTime alarmTime = LocalDateTime.parse(time,
                    DateTimeFormatter.ISO_DATE_TIME);
            System.out.println("Alarm set for " + alarmTime);
        }
    
    }

    @ToolParam 注解源码定义

    @Target({ElementType.PARAMETER, ElementType.FIELD,
            ElementType.ANNOTATION_TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface ToolParam {
        /**
         * 指定参数是否为必填,默认必填。如果参数被标记为@Nullable,则该参数将被视为可选,
         * 除非使用@ToolParam注解显式标记为必需
         */
        boolean required() default true;
    
        /**
         * 工具参数的描述,模型通过描述可以更好的理解如何使用它,例如,参数应采用什么格式,
         * 允许使用哪些值等。
         */
        String description() default "";
    }

    工具分类

    工具分为运行时工具(特定接口调用时加载)和默认工具(所有请求都会加载的)

    可以在创建chatclient的时候加上defaultTools

    
        private final ChatClient client;
    
        public ChatController(DashScopeChatModel dashScopeChatModel) {
            this.client = ChatClient.builder(dashScopeChatModel)
                    .defaultTools(new DateTimeTools())
                    .build();
        }

    b) 编程式定义工具

    定义工具

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

    上述这个还只是一个方法 还不是工具

    在代码中将该方法转化为可调用的工具

    @RequestMapping("/call2")
    public String chat2(String message) {
        //获取天气工具方法
        Method method = ReflectionUtils.findMethod(WeatherTools.class,
                "getCurrentWeatherByCityName", String.class);
    
        //创建工具回调
        ToolCallback toolCallback = MethodToolCallback.builder()
                .toolDefinition(ToolDefinitions.builder(method)
                        .description("Get current weather by city name")
                        .build())
                .toolMethod(method)
                .toolObject(new WeatherTools())
                .build();
    
        return client.prompt()
                .user(message)
                .toolCallbacks(toolCallback) //添加工具
                .call()
                .content();
    }.测试结果

    MethodToolCallback和ToolDefinitions介绍

    一下内容截取自spring ai官方 链接: https://springdoc.cn/spring-ai/api/tools.html

    添加默认工具

    直接在构造方法初始化client的时候添加

        public ChatController(DashScopeChatModel dashScopeChatModel) {
            //获取天气工具方法
            Method method = ReflectionUtils.findMethod(WeatherTools.class,
                    "getCurrentWeatherByCityName", String.class);
    
            //创建工具回调
            ToolCallback toolCallback = MethodToolCallback.builder()
                    .toolDefinition(ToolDefinitions.builder(method)
                            .description("Get current weather by city name")
                            .build())
                    .toolMethod(method)
                    .toolObject(new WeatherTools())
                    .build();
            
            this.client = ChatClient.builder(dashScopeChatModel)
                    .defaultToolCallbacks(toolCallback) //添加默认工具
                    .build();
        }

    如果臃肿不美观 可以运用IOC思想提取创建ToolCallBack类的方法 交给spring容器管理 然后再构造方法注入

    3.工具规范

    a) ToolCallback

    ToolCallback 是一个用于与 AI 模型交互的回调接口,允许开发者定义可被 AI 调用的外部工具,在 AI 应用(比如大语言模型)中集成"工具"(Tools),让 AI 模型可以在需要时调用外部功能。

    public interface ToolCallback {
    
        /**
         * 返回该工具的元信息描述
         * ToolDefinition 包含工具名称、描述、参数列表等结构化信息,供 AI 模型理解“什么情况下该调用这个工具”以及“需要传什么参数”。
         * 必须实现,是函数调用机制的核心部分
         * @return
         */
        ToolDefinition getToolDefinition();
    
        /**
         * 提供额外的非功能性元数据,主要用于框架层面的扩展性支持,不影响核心逻辑。
         * 使用了 default 方法,意味着子类可以不重写,默认返回空的元数据对象。
         * @return
         */
        default ToolMetadata getToolMetadata() {
            return ToolMetadata.builder().build();
        }
    
        /**
         * 抽象方法,必须由实现类提供具体逻辑。
         * 接收一个字符串输入(通常是 JSON 格式的参数),执行业务逻辑,并返回结果字符串。
         * 结果会传回给 AI 模型,供其继续推理或生成回复。
         */
        String call(String toolInput);
    
        /**
         * 带上下文的调用方法,也是默认实现。
         * 如果toolContext有值,则不支持,主要用于被子类重写支持上下文功能
         */
        default String call(String toolInput, @Nullable ToolContext toolContext) {
            if (toolContext != null && !toolContext.getContext().isEmpty()) {
                throw new UnsupportedOperationException("Tool context is not supported!");
            } else {
                return this.call(toolInput);
            }
        }
    
    }

    b) MethodToolCallback

    MethodToolCallback 是 Spring AI 框架中实现 Tool Calling的核心机制之一。它用于将普通 Java 方法包装成 AI 模型可调用的“工具”(Tool)。

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

    c) ToolDefinition

    public interface ToolDefinition {
    
        /**
         * 工具名称。在提供给模型的工具集中是唯一的
         */
        String name();
    
        /**
         * AI模型使用工具描述来确定工具的功能
         */
        String description();
    
        /**
         * 用于调用工具的参数的模式
         */
        String inputSchema();
    
    }

    ToolDefinition 实例

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

    d) ToolContext

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

     

    工具添加上下文 还是以获取当前时间为例

     @Tool(description = "A tool to get the current date and time")
        public String getCurrentDateTime(ToolContext context) {
            System.out.println("userID: " + context.getContext().get("userID"));
            return LocalDateTime.now().atZone(java.time.ZoneId.systemDefault()).toString();
        }
        @RequestMapping("/call")
        public String chat(String message) {
            return client.prompt()
                    .user(message)
                    .tools(new DateTimeTools()) //添加工具
                    .toolContext(Map.of("userID", "12345")) //调用时给工具传入上下文信息
                    .call()
                    .content();
        }
    

    测试结果

    e) Return Direct

    默认情况下,工具调用结果将作为响应返回模型,随后模型可利用该结果继续对话。

    某些场景下,开发人员可能希望将结果直接返回调用方而非传回模型。例如:当构建依赖 RAG 工具的代理时,开发人员可能希望直接将检索结果返回调用方,而非传回模型进行不必要的后处理;

    默认值为false

    二者对比

    4.工具的执行

    工具执行是指使用提供的输入参数调用工具并返回结果的过程。

    这个过程由 ToolCallingManager 接口处理,该接口是 Spring AI 中负责管理 "AI 调用工具" 全过程的核心组件,负责管理工具执行的完整生命周期。

    它主要管控以下核心问题:

    • 哪些工具可用?
    • AI 请求调用工具时,怎么执行?
    • 执行结果如何返回给 AI 继续对话?
    public interface ToolCallingManager {
    
        /**
         * 从模型的工具调用选项中解析工具定义,告诉模型:我能用哪些工具
         */
        List<ToolDefinition> resolveToolDefinitions(ToolCallingChatOptions chatOptions);
    
        /**
         * 执行模型所要求的工具调用:也就是真正的去"调工具"
         */
        ToolExecutionResult executeToolCalls(Prompt prompt, ChatResponse chatResponse);
    
    }

    流程图

    流程图文字解释

    • 当需要向模型提供工具时,我们将其定义包含在聊天请求(Prompt)中,并调用 ChatModel API 将请求发送至 AI 模型。
    • 当模型决定调用工具时,它会发送包含工具名称及符合定义模式的输入参数的响应(ChatResponse)。
    • ChatModel 将工具调用请求发送至 ToolCallingManager API。
    • ToolCallingManager 负责识别需调用的工具并使用提供的输入参数执行该工具。
    • 工具调用结果返回至 ToolCallingManager
    • ToolCallingManager 将工具执行结果返回给 ChatModel
    • ChatModel 将工具执行结果返回 AI 模型(ToolResponseMessage)。
    • AI 模型利用工具调用结果作为附加上下文生成最终响应,并通过 ChatClient 将其返回调用方(ChatResponse

    默认情况下,Spring AI 在每个 ChatModel 实现中透明地管理工具执行生命周期。但开发人员可选择退出此行为,自行控制工具执行。

     

     

    Logo

    AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

    更多推荐