Spring AI 实战系列(六):Tool Calling深度实战,让大模型自动调用你的业务接口


一、系列回顾与本篇定位

1.1 系列回顾

  • 第一篇 :完成 Spring AI 与阿里云百炼的基础集成,基于ChatModel实现同步对话与 API Key 安全注入。
  • 第二篇 :解锁ChatClient,实现全局统一配置与链式调用,告别重复样板代码。
  • 第三篇:实现 DeepSeek/Qwen 双模型无缝共存与动态切换,完成双版本流式输出。
  • 第四篇:深度拆解 Prompt 工程全体系,从底层 Message 到模板化动态生成,掌握与大模型高效沟通的方法论。
  • 第五篇:掌握结构化输出能力,实现大模型输出到 Java 实体的自动映射,解决业务系统对接痛点。

系列栏目:Spring AI

Spring AI 实战教程(一)入门示例

Spring AI 实战系列(二):ChatClient封装,告别大模型开发样板代码

Spring AI 实战系列(三):多模型共存+双版本流式输出

Spring AI 实战系列(四):Prompt工程深度实战

Spring AI 实战系列(五):结构化输出,让大模型严格适配你的业务数据模型

Spring AI 实战系列(六):Tool Calling深度实战,让大模型自动调用你的业务接口

1.2 本篇定位

大模型虽然具备强大的知识储备与推理能力,但它也有明确的能力边界

  • 它无法获取实时数据(如当前时间、实时天气、最新股价)。
  • 它无法直接操作业务系统(如查询订单状态、创建工单、发送短信)。
  • 它无法执行复杂的逻辑计算(如精确的日期计算、数学公式推导、数据校验)。

Tool Calling(函数调用),正是解决这些问题的核心能力:它让大模型不再只是 "回答问题的顾问",而是变成了 "能调用工具的执行者"------ 大模型会自动识别用户意图,选择合适的工具函数调用,基于工具返回的结果生成最终回答。

本篇是系列企业级核心篇,我们将深度拆解 Spring AI Tool Calling 的全体系:

  • 从核心原理出发,彻底理解 Tool Calling 的工作流程与 Spring AI 的核心抽象。
  • 从零实现实用的日期计算工具,完成从工具定义、注册到调用的全流程实战。
  • 覆盖 ChatModel 底层实现与 ChatClient 简化实现两种方式,对比二者的开发体验。
  • 补充多工具注册、带参数工具调用、returnDirect高级用法等企业级高频场景。
  • 提供生产环境最佳实践与高频踩坑避坑指南,解决 Tool Calling 落地的核心问题。

二、核心概念拆解:Spring AI Tool Calling 全原理

2.1 什么是 Tool Calling

Tool Calling(函数调用)是当前主流大模型(如通义千问、DeepSeek、GPT-4o)都支持的核心能力,它的核心流程如下:

  1. 意图识别:大模型接收用户问题,判断是否需要调用工具。
  2. 工具选择:如果需要调用工具,大模型从注册的工具列表中选择最合适的工具。
  3. 参数提取:大模型从用户问题中提取工具所需的参数,生成标准的参数格式。
  4. 工具执行:开发者的代码接收大模型的工具调用请求,执行对应的业务逻辑,返回结果。
  5. 结果生成:大模型基于工具返回的结果,结合用户的原始问题,生成最终的自然语言回答。

简单来说:Tool Calling 就是给大模型 "开了外挂",让它能通过调用你的业务接口,获取实时数据、执行业务操作、完成复杂计算

2.2 Spring AI Tool Calling 核心组件

Spring AI 对 Tool Calling 做了非常优雅的封装,核心组件只有三个:

核心组件 作用 典型用法
@Tool 注解 标记一个 Java 方法为 "大模型可调用的工具",通过description属性告诉大模型这个工具的用途 @Tool(description = "获取当前日期的详细信息")
ToolCallback 工具的封装对象,负责将 Java 方法转换为大模型可识别的工具定义,同时处理工具的调用逻辑 ToolCallbacks.from(new YourToolClass())
ToolCallingChatOptions 工具调用的配置对象,负责将注册的工具集传递给大模型,告诉大模型有哪些工具可用 ToolCallingChatOptions.builder().toolCallbacks(tools).build()

2.3 为什么优先使用ChatClient实现 Tool Calling

和之前的结构化输出、Prompt 工程一样,Spring AI 在ChatClient中对 Tool Calling 做了进一步的简化封装:

  • ChatModel 实现 :需要手动创建ToolCallback、配置ToolCallingChatOptions、组装Prompt,代码量较多,但能看到完整的底层流程。
  • ChatClient 实现 :直接通过.tools()方法注册工具,一行代码完成所有配置,代码极简,是生产环境的推荐写法。

三、实战落地:从工具定义到全流程调用

为了让教程更具实战价值,我们不再使用简单的 "获取当前时间" 工具,而是实现一个更实用的日期计算工具,包含两个核心方法:

  1. getCurrentDateDetail():获取当前日期的详细信息(包括星期几、是否为工作日)。
  2. calculateDaysBetween(String startDate, String endDate):计算两个日期之间的天数差。

这个工具能很好地展示 Tool Calling 的价值:大模型自己无法准确判断 "今天是星期几"、"距离某个日期还有多少天" 这类需要精确逻辑计算的问题,必须调用工具。

3.1 环境前提

  • 已完成 JDK 17+、Spring Boot 3.2.x 环境搭建
  • 已完成 Spring AI 基础配置,项目中已注册ChatModelChatClient实例
  • 已配置阿里云百炼 API Key 环境变量DASHSCOPE_API_KEY

3.2 第一步:定义工具类

创建DateCalculatorTools工具类,使用@Tool注解标记可调用的方法,同时通过description属性清晰地告诉大模型每个工具的用途、参数含义。

java 复制代码
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;

/**
 * 日期计算工具类:大模型可调用的业务工具
 */
public class DateCalculatorTools {

    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    /**
     * 获取当前日期的详细信息
     * @param returnDirect false(默认值):拿到工具返回结果后,再交给大模型生成最终回答
     *                     true:工具直接返回结果,不走大模型二次生成(适合纯数据查询场景)
     */
    @Tool(description = "获取当前日期的详细信息,包括日期、星期几、是否为工作日", returnDirect = false)
    public DateDetail getCurrentDateDetail() {
        LocalDate today = LocalDate.now();
        DayOfWeek dayOfWeek = today.getDayOfWeek();
        boolean isWorkday = dayOfWeek != DayOfWeek.SATURDAY && dayOfWeek != DayOfWeek.SUNDAY;

        // 返回结构化数据,Spring AI会自动转换为JSON给大模型
        return new DateDetail(
                today.format(DATE_FORMATTER),
                dayOfWeek.getDisplayName(java.time.format.TextStyle.FULL, java.util.Locale.CHINA),
                isWorkday ? "是" : "否"
        );
    }

    /**
     * 计算两个日期之间的天数差
     * @param startDate 开始日期,格式必须为yyyy-MM-dd(通过@ToolParam明确告诉大模型参数格式)
     * @param endDate 结束日期,格式必须为yyyy-MM-dd
     */
    @Tool(description = "计算两个日期之间的天数差,用于倒计时、日期间隔计算等场景")
    public long calculateDaysBetween(
            @ToolParam(description = "开始日期,格式必须为yyyy-MM-dd,例如2025-08-01") String startDate,
            @ToolParam(description = "结束日期,格式必须为yyyy-MM-dd,例如2025-10-01") String endDate) {
        try {
            LocalDate start = LocalDate.parse(startDate, DATE_FORMATTER);
            LocalDate end = LocalDate.parse(endDate, DATE_FORMATTER);
            return ChronoUnit.DAYS.between(start, end);
        } catch (Exception e) {
            throw new IllegalArgumentException("日期格式错误,请使用yyyy-MM-dd格式,例如2025-08-01");
        }
    }

    /**
     * 日期详情记录类:用于返回结构化数据
     */
    public record DateDetail(String date, String dayOfWeek, String isWorkday) {}
}

关键说明

  1. @Tool注解的description属性 :这是最重要的部分,必须清晰、准确地描述工具的用途,大模型就是通过这个description来判断是否调用该工具的。
  2. @ToolParam注解:用于明确告诉大模型每个参数的含义、格式要求,大幅提升大模型参数提取的准确率。
  3. 返回结构化数据 :工具方法可以返回 Java 实体(如DateDetail record),Spring AI 会自动将其转换为 JSON 格式传递给大模型,无需手动序列化。
  4. returnDirect属性
    • false(默认):工具返回结果后,大模型会基于结果生成自然语言回答(适合需要解释的场景)。
    • true:工具直接返回结果,不走大模型二次生成(适合纯数据查询、API 透传场景)。

3.3 第二步:ChatModel 底层实现(理解原理用)

先通过ChatModel实现完整的 Tool Calling 流程,理解底层逻辑,然后再看ChatClient的简化实现。

java 复制代码
import com.atguigu.study.utils.DateCalculatorTools;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.model.tool.ToolCallingChatOptions;
import org.springframework.ai.support.ToolCallbacks;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * Tool Calling 底层实现:基于ChatModel
 */
@RestController
public class ToolCallingController {

    @Resource
    private ChatModel chatModel;

    /**
     * ChatModel 实现Tool Calling全流程
     * 访问示例:http://localhost:xxxx/toolcall/chat?msg=距离2026-10-01还有多少天?
     */
    @GetMapping("/toolcall/chat")
    public String chat(@RequestParam(name = "msg", defaultValue = "今天是星期几?") String msg) {
        // 1. 将工具类注册为ToolCallback集合
        ToolCallback[] tools = ToolCallbacks.from(new DateCalculatorTools());

        // 2. 将工具集配置进ChatOptions,告诉大模型有哪些工具可用
        ChatOptions options = ToolCallingChatOptions.builder()
                .toolCallbacks(tools)
                .build();

        // 3. 构建Prompt,包含用户问题和工具配置
        Prompt prompt = new Prompt(msg, options);

        // 4. 调用大模型:Spring AI会自动处理工具选择、参数提取、工具执行、结果生成的全流程
        return chatModel.call(prompt).getResult().getOutput().getText();
    }
}

关键说明

  • 你不需要手动处理 "大模型什么时候调用工具"、"怎么提取参数"、"怎么把结果返回给大模型" 这些复杂逻辑,Spring AI 已经全部封装好了。
  • 你只需要做三件事:定义工具、注册工具、调用模型,剩下的全交给Spring AI。

3.4 第三步:ChatClient简化实现

ChatClient对Tool Calling做了极致简化,直接通过.tools()方法注册工具,一行代码完成所有配置,代码量比ChatModel减少了60%以上。

java 复制代码
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

/**
 * Tool Calling 简化实现:基于ChatClient
 */
@RestController
public class ToolCallingClientController {

    @Resource
    private ChatClient chatClient;

    /**
     * ChatClient 实现Tool Calling(同步调用)
     * 访问示例:http://localhost:8013/toolcall/chat2?msg=今天是星期几?是工作日吗?
     */
    @GetMapping("/toolcall/chat2")
    public String chat2(@RequestParam(name = "msg", defaultValue = "今天是星期几?") String msg) {
        return chatClient.prompt(msg)
                // 核心:一行代码注册工具,支持传入工具类实例或工具类的Class
                .tools(new DateCalculatorTools())
                .call()
                .content();
    }

    /**
     * ChatClient 实现Tool Calling(流式调用)
     * 访问示例:http://localhost:8013/toolcall/chat3?msg=距离2026-10-01还有多少天?帮我详细解释一下
     */
    @GetMapping(value = "/toolcall/chat3", produces = "text/html;charset=utf-8")
    public Flux<String> chat3(@RequestParam(name = "msg", defaultValue = "今天是星期几?") String msg) {
        return chatClient.prompt(msg)
                .tools(new DateCalculatorTools())
                .stream()
                .content();
    }
}

接口测试说明:启动项目后,访问对应接口,你会看到:

  1. 问 "今天是星期几?是工作日吗?":大模型会自动调用getCurrentDateDetail()工具,基于返回的结果生成自然语言回答。
  2. 问 "距离 2026-10-01 还有多少天?":大模型会自动调用calculateDaysBetween()工具,提取当前日期作为开始日期,计算天数差并生成回答。

四、进阶场景:企业级高频需求全实现

4.1 多工具同时注册

Spring AI支持同时注册多个工具,大模型会根据用户问题自动选择最合适的工具调用。我们再添加一个 "随机密码生成工具",展示多工具注册的用法。

java 复制代码
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;

import java.security.SecureRandom;

/**
 * 随机密码生成工具
 */
public class PasswordGeneratorTools {

    private static final String CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*";
    private static final SecureRandom RANDOM = new SecureRandom();

    @Tool(description = "生成指定长度的随机密码,包含大小写字母、数字和特殊字符")
    public String generatePassword(
            @ToolParam(description = "密码长度,建议8-20位") int length) {
        if (length < 8 || length > 20) {
            throw new IllegalArgumentException("密码长度必须在8-20位之间");
        }
        StringBuilder password = new StringBuilder(length);
        for (int i = 0; i < length; i++) {
            password.append(CHARS.charAt(RANDOM.nextInt(CHARS.length())));
        }
        return password.toString();
    }
}

多工具注册实现

java 复制代码
@GetMapping("/toolcall/multi")
public String multiToolChat(@RequestParam(name = "msg") String msg) {
    // 同时注册多个工具:日期计算 + 密码生成
    return chatClient.prompt(msg)
            .tools(new DateCalculatorTools(), new PasswordGeneratorTools())
            .call()
            .content();
}

测试时,问 "生成一个 12 位的随机密码",大模型会自动调用generatePassword()工具;问 "今天是星期几",大模型会自动调用getCurrentDateDetail()工具,完全无需手动判断。

4.2 returnDirect高级用法

当工具返回的结果本身就是用户需要的最终数据(如纯 JSON 数据、API 透传结果),不需要大模型二次生成自然语言回答时,可以将returnDirect设置为true,直接返回工具结果,提升响应速度,节省Token消耗。

我们修改getCurrentDateDetail()方法,将returnDirect设置为true

java 复制代码
@Tool(description = "获取当前日期的详细信息,直接返回JSON数据", returnDirect = true)
public DateDetail getCurrentDateDetail() {
    // 方法实现不变
}

此时调用该工具,会直接返回 JSON 格式的工具结果,不再经过大模型二次生成:

java 复制代码
{
    "date": "2026-07-28",
    "dayOfWeek": "星期二",
    "isWorkday": "是"
}

4.3 带复杂参数的工具调用

工具方法支持多种参数类型:基本类型(int、long、String)、复杂对象、集合等。Spring AI 会自动处理参数的类型转换,无需开发者手动处理。

我们添加一个 "日程安排工具",展示复杂对象参数的用法:

java 复制代码
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;

import java.util.List;

/**
 * 日程安排工具
 */
public class ScheduleTools {

    public record ScheduleEvent(String title, String date, String time, String location) {}

    @Tool(description = "创建日程安排,返回确认信息")
    public String createSchedule(
            @ToolParam(description = "日程标题") String title,
            @ToolParam(description = "日程日期,格式yyyy-MM-dd") String date,
            @ToolParam(description = "日程时间,格式HH:mm") String time,
            @ToolParam(description = "日程地点") String location) {
        return String.format("日程创建成功:【%s】于 %s %s 在 %s 举行", title, date, time, location);
    }
}

五、实践建议

5.1 工具设计原则

  1. 单一职责:一个工具只做一件事,避免 "全能工具",工具越简单,大模型越容易正确调用。
  2. 清晰的 Description@Tool@ToolParamdescription必须清晰、准确、无歧义,这是大模型正确调用工具的核心。
  3. 参数校验:在工具方法中必须对参数进行严格校验(如日期格式、长度范围、非空校验),避免非法参数导致业务异常。
  4. 结构化返回:优先返回 Java 实体或 Record,避免返回纯字符串,结构化数据能让大模型更准确地理解工具返回的结果。

5.2 安全管控

  1. 权限校验:在工具方法执行前,必须校验当前用户的权限,避免未授权用户调用敏感工具(如创建订单、删除数据)。
  2. 输入过滤:对用户输入的参数进行过滤,防止 SQL 注入、XSS 攻击、Prompt Injection 等安全风险。
  3. 敏感数据脱敏:如果工具返回的结果包含敏感数据(如手机号、身份证号),必须进行脱敏处理后再返回给大模型。
  4. 工具白名单:生产环境建议使用工具白名单机制,只允许注册经过安全审核的工具,避免随意注册工具带来的安全风险。

5.3 异常处理与降级

  1. 工具异常捕获:在工具方法中捕获所有异常,返回友好的错误信息给大模型,让大模型基于错误信息生成合理的回答。
  2. 降级机制:当工具调用失败(如第三方 API 超时、数据库连接失败)时,提供降级返回值,避免整个请求失败。
  3. 超时控制:为工具调用设置超时时间,避免工具执行时间过长导致整个请求阻塞。

5.4 日志与监控

  1. 工具调用日志:记录每次工具调用的用户问题、工具名称、参数、返回结果、执行耗时,用于排查问题和优化工具。
  2. 调用次数统计:统计每个工具的调用次数、失败率,识别高频使用的工具和不稳定的工具。
  3. Token 消耗监控:统计 Tool Calling 的 Token 消耗(包括工具调用请求和结果返回的 Token),用于成本核算。

六、避坑指南

6.1 坑点 1:工具方法必须是public

现象 :大模型无法识别工具,提示 "没有可用的工具"。原因 :工具方法的访问修饰符不是public,Spring AI 无法通过反射调用该方法。解决方案 :确保所有被@Tool注解标记的方法都是public的。

6.2 坑点 2:参数类型不匹配

现象 :大模型调用工具时,参数提取失败,或者类型转换异常。原因 :工具方法的参数类型与大模型生成的参数类型不匹配(如方法参数是int,大模型生成了String)。解决方案

  • 优先使用基本类型的包装类(IntegerLong)或String,避免类型转换问题。
  • 通过@ToolParam明确告诉大模型参数的类型和格式要求。

6.3 坑点 3:Description 写得不够清晰

现象 :大模型不会调用工具,或者调用错误的工具。原因@Tooldescription写得太模糊,大模型无法理解工具的用途。解决方案

  • Description 要包含:工具的用途、适用场景、返回值的含义。
  • 可以在 Description 中给出使用示例,帮助大模型理解。

6.4 坑点 4:returnDirect的误用

现象 :设置returnDirect=true后,大模型不再生成回答,直接返回了工具的原始结果。原因 :误解了returnDirect的作用,它适合纯数据查询场景,不适合需要解释的场景。解决方案

  • 需要大模型基于工具结果生成自然语言回答时,设置returnDirect=false(默认)。
  • 只需要工具返回的原始数据时,才设置returnDirect=true

6.5 坑点 5:流式调用与 Tool Calling 的兼容性

现象 :流式调用时,工具调用的结果无法正确展示。原因 :部分大模型在流式调用时,Tool Calling 的支持不够完善。解决方案

  • 优先使用同步调用实现 Tool Calling,稳定性更高。
  • 如果必须使用流式调用,建议在工具调用完成后,再使用流式输出生成最终回答。

七、本篇总结

本篇我们深度掌握了 Spring AI Tool Calling的全体系:

  • 从核心原理出发,理解了 Tool Calling 的工作流程与Spring AI的核心抽象。
  • 从零实现了实用的日期计算工具,完成了从工具定义、注册到调用的全流程实战。
  • 覆盖了ChatModel底层实现与 ChatClient 简化实现两种方式,明确了生产环境的推荐写法。
  • 补充了多工具注册、returnDirect高级用法、带复杂参数的工具调用等企业级高频场景。
  • 提供了生产环境最佳实践与高频踩坑避坑指南,为 Tool Calling 的生产落地提供了完整的解决方案。

Tool Calling 是大模型从 "问答助手" 走向 "业务执行者" 的关键一步,只有掌握了 Tool Calling,才能真正将大模型能力深度融入业务流程,实现 "大模型 + 业务系统" 的深度融合。


八、下篇预告

本篇我们掌握了 Tool Calling 核心能力,让大模型能自动调用我们的业务接口。在本系列的下一篇中,我们将深入 Spring AI 的核心拼图 ------对话记忆(Chat Memory)

  • 深度拆解对话记忆的核心原理,理解大模型如何 "记住" 历史对话。
  • 从零实现基于内存的对话记忆,完成带上下文感知的多轮对话。
  • 覆盖会话隔离、记忆窗口大小控制、持久化存储等企业级高频场景。
  • 补充生产环境对话记忆的最佳实践与性能优化指南。

如果本文对你有帮助,欢迎点赞、收藏、评论,跟着系列教程一步步完成Spring AI应用的全流程落地。

相关推荐
NoSi EFUL2 小时前
Redis6.2.6下载和安装
java
郝学胜-神的一滴2 小时前
张量维度操控心法:从reshape到升维降维,吃透PyTorch形状操作的底层逻辑
人工智能·pytorch·python·深度学习·程序人生·算法·机器学习
极光代码工作室2 小时前
基于AI的学习辅助系统设计
人工智能·机器学习·ai·系统设计
Matrix_112 小时前
论文阅读:中央凹堆叠成像技术
论文阅读·人工智能·计算摄影
RemainderTime2 小时前
基于 Spring AI + DeepSeek:构建AI Agent 企业级服务与底层原理解析
人工智能·后端·spring·ai
yuanlaile2 小时前
想转后端,java和go学哪个更好?
java·开发语言·golang
Zzzzmo_2 小时前
【JavaEE】多线程01
java·jvm·java-ee·多线程
希望永不加班2 小时前
SpringBoot Web 模块核心组件:从 DispatcherServlet 讲起
java·前端·spring boot·后端·spring
草莓熊Lotso2 小时前
2026年4月UU远程副屏功能测评:多设备协同生态再升级
人工智能