【SpringAIAlibaba新手村系列】(13)Tool Calling 函数工具调用技术

张开发
2026/4/7 16:34:50 15 分钟阅读

分享文章

【SpringAIAlibaba新手村系列】(13)Tool Calling 函数工具调用技术
第十三章 Tool Calling 函数工具调用技术版本标注Spring AI:1.1.2Spring AI Alibaba:1.1.2.0章节定位Tool Calling 仍然是最核心的能力之一。在 Spring AI Alibaba1.1.2.x中它通常不是孤立存在而是嵌入到ReactAgent、多智能体编排、RAG Workflow、MCP Client 和 Graph 节点中协同工作。s01 s02 s03 s04 s05 s06 s07 s08 s09 s10 s11 s12 [ s13 ] s14 s15 s16 s17 s18大模型会说, 工具调用让它开始会做-- Tool Calling 是 AI 从对话走向执行的关键一步。一、为什么需要 Tool Calling1.1 AI 的能力边界我自己第一次真正意识到 Tool Calling 的价值不是在看概念定义的时候而是在写接口时突然发现模型虽然会说但它其实不会“做”。AI 大模型虽然很聪明但它有一个根本限制它只能生成文字无法执行真实世界的操作比如你问 AI今天天气怎么样 → AI 只能猜测或回答我不知道实时天气帮我查下北京到上海的火战票 → AI 无法真的帮你查询现在几点了 → AI 也无法获取当前时间1.2 Tool Calling 的诞生Tool Calling 真正解决的不是“让模型回答得更花哨”而是让它在需要外部信息时不再只靠猜。Tool Calling函数工具调用让 AI 能够在回答过程中发现需要某些信息时自动调用外部工具获取传统方式 用户现在几点 → AI我不知道靠猜 Tool Calling 方式 用户现在几点 → AI发现需要获取时间 → 自动调用 getCurrentTime() 函数 → 获取到2025年1月1日 10:30:00 → 把时间告诉用户1.3 可以调用的工具类型工具类型示例说明查询类天气查询、股价查询获取外部信息操作类发送邮件、订机票执行实际操作计算类数学计算、代码执行处理复杂运算数据库类查询订单、用户信息访问业务数据二、Tool Calling 核心原理2.1 工作流程┌─────────────────────────────────────────────────────────┐ │ Tool Calling 工作流程 │ ├─────────────────────────────────────────────────────────┤ │ │ │ 1. 注册工具 │ │ 把函数(方法)注册到 AI 能识别的格式 │ │ │ │ 2. 用户提问 │ │ 北京明天天气怎么样 │ │ │ │ 3. AI 分析 │ │ → 需要调用天气查询工具 │ │ → 自动提取参数城市北京时间明天 │ │ │ │ 4. 执行工具 │ │ 调用 getWeather(city北京, date明天) │ │ 返回天气数据 │ │ │ │ 5. 返回答案 │ │ 结合工具返回的结果给出完整回答 │ │ │ └─────────────────────────────────────────────────────────┘2.2 Tool 注解在 Spring AI 里最直观的入门方式就是直接用Tool暴露一个普通 Java 方法。这样写的好处是很明显的先把工具定义清楚再让模型学会什么时候用它。Spring AI 提供了Tool注解来声明一个可调用的方法public class DateTimeTools { Tool(description 获取当前时间) public String getCurrentTime() { return LocalDateTime.now().toString(); } Tool(description 根据城市名称获取天气) public String getWeather(String city) { // 调用外部天气API return city 明天晴转多云20-28度; } }三、项目代码详解3.1 定义工具类package com.atguigu.study.utils; import org.springframework.ai.tool.annotation.Tool; import org.springframework.stereotype.Component; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; /** * 日期时间工具类 * 我这里先拿“日期时间”做示例是因为这个工具足够简单结果也稳定最适合观察模型到底有没有真的触发工具调用。 */ Component public class DateTimeTools { /** * 获取当前时间 * * 这里真正关键的不是方法体而是 Tool(description ...) * 这段描述本质上就是给模型看的“工具说明书”。 */ Tool(description 获取当前日期和时间) public String getCurrentDateTime() { LocalDateTime now LocalDateTime.now(); return now.format(DateTimeFormatter.ofPattern(yyyy年MM月dd日 HH:mm:ss)); } /** * 获取当前日期 */ Tool(description 获取当前日期) public String getCurrentDate() { return LocalDateTime.now().format(DateTimeFormatter.ofPattern(yyyy年MM月dd日)); } }3.2 控制器代码package com.atguigu.study.controller; import com.atguigu.study.utils.DateTimeTools; import jakarta.annotation.Resource; import org.springframework.ai.chat.client.ChatClient; 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; import reactor.core.publisher.Flux; /** * 工具调用控制器 * 展示让 AI 调用外部函数的能力 */ RestController public class ToolCallingController { // 注入 ChatModel底层API更灵活 Resource private ChatModel chatModel; // 注入 ChatClient高级API更简洁 Resource private ChatClient chatClient; /** * 方式一使用底层 ChatModel API更适合理解原理 * * 接口http://localhost:8013/toolcall/chat?msg你是谁现在几点 */ GetMapping(/toolcall/chat) public String chat(RequestParam(name msg, defaultValue 你是谁现在几点) String msg) { // 1. 把普通 Java 工具对象转换成 Spring AI 能识别的工具列表 ToolCallback[] tools ToolCallbacks.from(new DateTimeTools()); // 2. 在这次模型调用里显式注册工具 ChatOptions options ToolCallingChatOptions.builder() .toolCallbacks(tools) .build(); // 3. 把“用户问题 工具能力”一起交给模型 Prompt prompt new Prompt(msg, options); // 4. 如果模型判断需要工具就会先触发工具调用再基于结果生成最终回答 return chatModel.call(prompt) .getResult() .getOutput() .getText(); } /** * 方式二使用高级 ChatClient API更适合日常开发 * * 接口http://localhost:8013/toolcall/chat2?msg你是谁现在几点 */ GetMapping(/toolcall/chat2) public FluxString chat2(RequestParam(name msg, defaultValue 你是谁现在几点) String msg) { return chatClient.prompt(msg) // .tools() 方法直接注册工具 .tools(new DateTimeTools()) .stream() .content(); } }3.3ToolCallbacks.from(new DateTimeTools())到底在做什么这一句代码看起来很短但它其实是 Tool Calling 里非常关键的一步ToolCallback[] tools ToolCallbacks.from(new DateTimeTools());很多初学者第一次看到时会以为是不是这里已经把DateTimeTools里的方法执行了一遍其实不是。它做的事情更准确地说是把一个普通的 Java 工具对象转换成 Spring AI 能识别和调用的一组ToolCallback。也就是说这一步不是“执行工具”而是“注册工具”。3.1.new DateTimeTools()只是一个普通对象你自己写的这个类本质上只是一个普通 Java 类public class DateTimeTools { Tool(description 获取当前日期和时间) public String getCurrentDateTime() { ... } Tool(description 获取当前日期) public String getCurrentDate() { ... } }如果没有 Spring AI 再做一层处理大模型其实并不知道这个类里有哪些方法哪些方法可以作为工具暴露出去每个方法是干什么的调用时需要什么参数3.2.ToolCallbacks.from(...)的作用是“翻译”ToolCallbacks.from(new DateTimeTools())会去扫描这个对象里的方法重点找哪些方法带有Tool每个方法的名称每个方法的描述信息方法参数结构方法返回值类型然后把这些信息包装成ToolCallback[]你可以把ToolCallback理解成一个工具的说明书 调用代理。它里面既包含“这个工具是干什么的”的元信息也包含“模型决定调用它时最终应该执行哪个 Java 方法”的调用逻辑。3.3. 为什么返回的是数组因为一个工具类里可能不止一个工具方法。比如DateTimeTools里就有getCurrentDateTime()getCurrentDate()所以ToolCallbacks.from(...)最终返回的不是单个对象而是一组ToolCallback。也就是说ToolCallback[] tools ToolCallbacks.from(new DateTimeTools());相当于把DateTimeTools这个类里可暴露的所有工具方法统一整理成了一个“可供模型调用的工具列表”。3.4. 这一步为什么不是“立刻执行工具”因为工具真正执行的时机不是在from(...)这里而是在后面模型推理时。完整流程是这样的你先用ToolCallbacks.from(...)把工具注册好再把这些工具交给ChatModel或ChatClient模型分析用户问题时判断自己是否需要调用工具如果需要就由 Spring AI 通过ToolCallback去真正执行对应方法方法执行结果再返回给模型模型据此组织最终回答所以from(...)是注册阶段工具方法真正运行是模型决策之后的执行阶段3.5. 一句话总结ToolCallbacks.from(new DateTimeTools())的本质是通过反射扫描DateTimeTools对象中的Tool方法把它们包装成 Spring AI 可识别的ToolCallback[]从而让大模型后续能够按工具名和参数自动调用这些 Java 方法。3.4 工具真正执行时代码到底是怎么跑起来的这个问题我自己一开始也非常困惑。平常写 Java 代码时我们已经习惯了这种执行方式String now dateTimeTools.getCurrentDateTime();也就是“代码写到哪里就执行到哪里”。但 Tool Calling 看起来不是这样因为你明明没有手写这一句调用工具方法最后却真的执行了。这里最关键的结论是Spring AI 不会额外启动一个新进程去执行工具它还是在当前 Spring Boot 应用进程里完成方法调用。只不过这次不是你在源码里写死了调用哪个方法而是模型先返回一个“我要调用哪个工具”的结构化请求框架再根据这个请求去定位并执行对应的 Java 方法。整个执行链路大致可以理解成这样你先通过ToolCallbacks.from(...)注册工具模型收到用户问题后先判断自己是否需要某个工具如果需要它会返回一个工具调用请求例如工具名 参数Spring AI 拿到这个请求后在当前应用里找到对应工具方法通过反射执行这个 Java 方法把工具返回结果再交还给模型模型基于这个结果生成最终答案如果用更接近程序执行的方式表达大概等价于String toolName getCurrentDateTime; Method method registry.find(toolName); Object result method.invoke(targetObject);所以 Tool Calling 不是“随机执行代码”也不是“AI 自己直接运行 Java”。它更像是一种数据驱动的动态方法调用普通调用你在源码里提前写死调用谁Tool Calling模型先产出调用指令框架再运行时决定调用谁这也是为什么 Tool Calling 看上去像“模型会用工具了”但从程序底层看本质上还是当前 Java 应用在执行普通方法只不过中间多了一层“模型决策 框架分发”的过程。四、自定义工具的最佳实践4.1 Tool 注解的关键要素// ✅ 好的描述示例 Tool(description 获取指定城市的当前天气状况) public String getWeather(String city) { ... } // ❌ 模糊的描述AI 不知道怎么用这个工具 Tool(description 天气查询) public String getWeather(String city) { ... }4.2 工具方法命名// ✅ 清晰的方法名一看就知道干什么 public String getWeatherByCity(String city) public String queryUserOrderHistory(String userId) // ⚠️ 模糊的方法名不推荐 public String doSomething(String param) public String query(String id)4.3 参数设计// ✅ 必要的参数清晰易懂 public String getWeather( Param(name city, description 城市名称如北京、上海) String city, Param(name date, description 查询日期格式yyyy-MM-dd) String date ) // ✅ 不需要参数的简单工具 Tool(description 获取当前登录用户信息) public UserInfo getCurrentUser() { ... }五、本章小结5.1 核心概念概念说明ToolSpring AI 注解声明可调用的方法ToolCallback工具调用的封装类ToolCallingChatOptions配置工具调用的选项类.tools()ChatClient 注册工具的方法5.2 使用流程1. 编写工具类用Component让Spring管理 2. 在方法上添加Tool注解 3. 编写清晰的方法描述 4. 在控制器中注册工具 5. AI自动识别并调用工具5.3 注意事项描述要清晰AI 通过描述理解工具用途参数要完整帮助 AI 正确提取参数返回值要规范清晰的返回值便于 AI 理解结果本章重点理解 Tool Calling 的核心原理掌握 Tool 注解的使用方法学会在 ChatClient 中注册工具下章剧透s14学会了自定义 Tool 后下一章我们将学习 MCPModel Context Protocol——标准化外部工具调用的协议TIPTool vs MCP 工具的区别本章我们学习的 Tool Calling 使用的是Tool 注解方式这是 Spring AI 原生的工具定义方式。而下一章要学的 MCP 是另一种标准化协议。它们区别如下特性Tool 注解MCP 协议定义方式代码 注解独立配置文件适用范围同项目内的方法任意外部服务连接方式进程内直接调用HTTP/STDIO复用性同项目复用跨项目复用复杂度简单需要额外配置怎么选如果是项目内部的工具如业务逻辑用Tool更简单直接如果要调用外部独立服务如天气API、数据库用MCP更标准化本章学的Tool是基础下一章学的 MCP 是进阶。两者并不冲突实际项目经常结合使用编辑者Flittly更新时间2026年4月相关资源Spring AI Tool Calling

更多文章