乐途乐途
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
  • Spring AI 学习路径
  • 第1章 Spring AI 概述与核心理念

    • 章节导读
    • Spring AI 概述与可移植 API
    • 核心模块与依赖关系
  • 第2章 快速入门与第一个AI应用

    • 章节导读
    • 环境准备与配置
    • 第一个 AI 对话应用
  • 第3章 聊天模型与ChatClient

    • 章节导读
    • ChatClient 详解
    • ChatModel 底层抽象
    • 多轮对话与 ChatMemory
  • 第4章 提示词管理与模板

    • 章节导读
    • Prompt 与 Message 体系
    • 提示词模板与动态构建
  • 第5章 输出解析与结构化响应

    • 章节导读
    • 输出解析器与 BeanOutputConverter
  • 第6章 嵌入模型与向量存储

    • 章节导读
    • EmbeddingModel 与文本向量化
    • ETL 数据注入流水线
    • 向量存储抽象与配置
  • 第7章 检索增强生成(RAG)

    • 章节导读
    • RAG 核心机制与流程
    • QuestionAnswerAdvisor 详解
    • RAG 实战案例
  • 第8章 函数调用(Function Calling)

    • 章节导读
    • Tool 注解与函数声明
    • 函数调用实战
  • 第9章 多模态

    • 章节导读
    • 图像输入与视觉模型
    • 图像生成
  • 第10章 Advisor拦截器链

    • 章节导读
    • Advisor 链与内置拦截器
  • 第11章 MCP 协议与跨语言工具集成

    • 章节导读
    • MCP 协议深入
  • 第12章 可观测性测试与最佳实践

    • 章节导读
    • 可观测性与指标监控
    • 测试策略与 Mock 实践
    • 最佳实践与面试考点汇总

白歌在白板上画了一个流程:"用户说'帮我查明天的课程',AI 知道自己查不到,于是调用 @Tool 标注的 getSchedule 方法,拿到结果后再组织语言回复用户。这就是 Tool Calling——AI 的'手'。"

Tool 注解与函数声明

定义与作用

@Tool 是 Spring AI 的核心注解,将一个 Java 方法标记为"AI 可调用工具"。当 AI 判断用户问题需要调用某个工具时,会输出函数名和参数,Spring AI 自动执行对应 Java 方法并将结果返回给 AI。

@Tool(description = "获取指定城市天气")
public String getWeather(String city) { ... }
//          ↑                    ↑
//    方法名 = AI 调用的函数名    参数 = AI 根据 JSON Schema 传递

核心原理:完整的 Tool Calling 请求-响应循环

图释:整个流程包含两轮 AI 调用。第一轮:AI 分析用户意图后决定调用工具,返回函数名和参数。第二轮:收到工具执行结果后,AI 将其转化为自然语言回复。

完整示例一:简单参数工具

场景说明

小崔需要为飞翔科技学生管理系统添加课程查询功能——AI 根据学生输入查询课程信息。

关键代码

@Component
public class CourseTools {

    @Tool(description = "根据课程编号查询课程详细信息,包括课程名称、教师、学分、上课时间")
    public String getCourseInfo(String courseCode) {
        // 模拟数据库查询
        return switch (courseCode.toUpperCase()) {
            case "CS101" -> "课程:计算机组成原理 | 教师:白歌 | 学分:4 | 时间:周一 8:00-10:00";
            case "CS201" -> "课程:数据结构与算法 | 教师:小崔 | 学分:3 | 时间:周三 10:00-12:00";
            case "MATH301" -> "课程:高等数学 | 教师:李眉 | 学分:5 | 时间:周二 14:00-16:00";
            default -> "未找到课程编号 " + courseCode;
        };
    }

    @Tool(description = "根据教师姓名查询该教师本学期所有课程安排")
    public String getTeacherSchedule(String teacherName) {
        return switch (teacherName) {
            case "白歌" -> "白歌本学期课程:\n- CS101 计算机组成原理(周一 8:00-10:00)\n- CS302 操作系统(周四 14:00-16:00)";
            case "小崔" -> "小崔本学期课程:\n- CS201 数据结构与算法(周三 10:00-12:00)\n- CS401 软件工程(周五 8:00-10:00)";
            default -> teacherName + " 的课程安排未找到";
        };
    }
}
@Configuration
public class ToolConfig {

    @Bean
    public ChatClient chatClient(ChatClient.Builder builder,
                                  CourseTools courseTools) {
        return builder
                .defaultTools(courseTools)  // 注册所有 @Tool 方法
                .build();
    }
}
@RestController
public class CourseController {

    private final ChatClient chatClient;

    public CourseController(ChatClient chatClient) {
        this.chatClient = chatClient;
    }

    @GetMapping("/course/ask")
    public String ask(@RequestParam String message) {
        return chatClient.prompt()
                .user(message)
                .call()
                .content();
    }
}

运行结果及分析

GET /course/ask?message=帮我查一下 CS101 是什么课

AI 内部决策:需要调用 getCourseInfo("CS101")
↓
执行 getCourseInfo("CS101") → "课程:计算机组成原理 | 教师:白歌 | ..."
↓
AI 回复:「CS101 是计算机组成原理,由白歌老师授课,4 学分。
         上课时间是周一上午 8:00-10:00。需要我帮你查白歌老师的其他课程吗?」

关键观察:用户说"帮我查一下 CS101 是什么课"而非"调用 getCourseInfo"——AI 自动理解了意图并选择了正确的工具。

完整示例二:复杂参数(POJO/Record)

场景说明

孔蓝要求会议室预订工具支持复杂参数——一次预订需要城市、日期、时长等多个字段。

关键代码

public record BookingRequest(
    String city,
    LocalDate checkIn,
    int nights,
    String roomType
) {}

@Component
public class MeetingRoomTools {

    @Tool(description = "预订会议室,需要提供办公楼、日期、时长(小时)和会议人数")
    public String bookMeetingRoom(
            String building,           // 简单参数
            @ToolParam(description = "预订日期,格式 yyyy-MM-dd") 
            LocalDate date,            // 特殊类型参数
            int hours,                 // 简单参数
            int participants           // 简单参数
    ) {
        // 模拟预订系统
        String room;
        if (participants <= 6) {
            room = "小型会议室 A" + (participants % 3 + 1);
        } else if (participants <= 20) {
            room = "中型会议室 B" + (participants % 2 + 1);
        } else {
            room = "大型会议室 C1";
        }

        return String.format(
            "已预订 %s 的 %s,日期 %s,时长 %d 小时,人数 %d 人",
            building, room, date, hours, participants
        );
    }

    @Tool(description = "查询指定办公楼所有可用会议室")
    public String getAvailableRooms(String building) {
        return switch (building) {
            case "A座" -> "A座可用会议室:A-201(6人)、A-305(12人)、A-101(30人)";
            case "B座" -> "B座可用会议室:B-102(4人)、B-301(8人)";
            default -> building + " 暂无会议室信息";
        };
    }
}
@RestController
public class BookingController {

    private final ChatClient chatClient;

    public BookingController(ChatClient.Builder builder,
                              MeetingRoomTools meetingRoomTools) {
        this.chatClient = builder
                .defaultTools(meetingRoomTools)
                .build();
    }

    @GetMapping("/booking/ask")
    public String ask(@RequestParam String message) {
        return chatClient.prompt()
                .user(message)
                .call()
                .content();
    }
}

运行结果

GET /booking/ask?message=帮我订明天 A 座 2 小时的会议室,8 个人参会

AI 内部决策:
  1. 识别需要调用 bookMeetingRoom
  2. 从自然语言中提取参数:
     - building = "A座"
     - date = "2026-06-14" (明天)
     - hours = 2
     - participants = 8

执行结果:「已预订 A座 的中型会议室 B1,日期 2026-06-14,时长 2 小时,人数 8 人」

AI 回复:「会议室已预订!A座 B1 中型会议室,明天(6月14日),2小时。
         8人会议。需要我帮你发送会议邀请吗?」

易错场景与面试考点

易错场景一:工具描述不清晰导致 AI 不调用

// ❌ 错误:描述太泛化
@Tool(description = "获取信息")
public String getInfo(String param) { ... }

问题分析

AI 根据 description 决定是否调用工具。太泛化的描述会让 AI 无法判断何时使用。

// ✅ 正确:描述精确说明功能和使用场景
@Tool(description = "获取指定城市的当前天气信息,包括温度、湿度、风速")
public String getWeather(String city) { ... }

易错场景二:参数类型不匹配

// ❌ 错误:AI 返回 "2026-06-14" 但参数是 LocalDate,可能解析失败
@Tool(description = "预订会议室")
public String bookRoom(LocalDate date, int hours) { ... }

问题分析

Spring AI 自动从参数生成 JSON Schema,LocalDate 会被映射为 string 类型。确保 AI 传参格式与目标类型兼容。可以通过 @ToolParam 注解补充描述。

// ✅ 正确:为参数提供描述
@Tool(description = "预订会议室")
public String bookRoom(
    @ToolParam(description = "预订日期,格式 yyyy-MM-dd") LocalDate date,
    @ToolParam(description = "时长(小时)") int hours
) { ... }

面试高频题

Q1:AI 如何决定是否调用工具?

AI 模型在推理时会分析用户意图和可用工具列表(含 name + description + JSON Schema)。如果判断某工具能帮助回答问题,模型输出工具名和参数(JSON 格式)。Spring AI 解析 JSON 匹配到 @Tool 方法并执行,结果返回给模型生成最终回复。

Q2:defaultTools 和 functions() 的区别?

defaultTools:注册到 ChatClient.Builder,全局生效,每次调用自动可用。functions(String...):在单次调用时通过 .functions("toolName") 指定,仅当次请求生效。适合需要按场景控制工具可用性的情况。

本章小结

  • @Tool 将 Java 方法暴露为 AI 可调用工具,Spring AI 自动生成 JSON Schema
  • 支持简单参数(String/int)和复杂参数(LocalDate/Record)
  • 完整流程:用户提问 → AI 决定调用 → Spring AI 执行 → 结果返回 → AI 回复
  • 工具描述是 AI 使用工具的关键参考,务必精确描述功能与参数
上一页
章节导读
下一页
函数调用实战