第4章:函数调用(Function Calling / Tool Calling)—让 AI 调用你的 API
📌 本章目标:建立对 Spring AI 的整体认知,理解它为什么重要,并快速搭建第一个 AI 应用。
返回目录
🎯 目标:让大模型具备“行动力”,能调用外部工具
在前面的章节中,我们已经学会了如何让 AI 回答问题、生成文本、甚至返回结构化的 Java 对象。但这些都还停留在“说”的层面 —— AI 像一个知识渊博的顾问,能告诉你“应该怎么做”,却无法真正“动手去做”。
本章的核心目标,就是给 AI 装上“手脚”,让它不仅能思考,还能主动调用你的后端服务、查询数据库、发送邮件、调用天气接口…… 这就是 **函数调用(Function Calling)**也叫 工具调用(Tool Calling) 。通过这一章,你将掌握如何在 Spring AI 中定义工具、注册工具,并让大模型根据用户需求自动决定是否调用这些工具,从而构建出真正具有“行动能力”的智能应用。
4.1 什么是 Function Calling?它解决了什么问题?
🔍 通俗理解:AI 的“技能包”
想象一下,你有一个智能助手,它知道很多知识,但如果你问它:“明天西安会下雨吗?我该带伞吗?”,它可能只会回答:“根据我的知识库,西安明天有降雨的可能性。” —— 这是“静态知识”。
但我们希望它能更进一步,比如:
“正在查询实时天气…… 明天西安有 80% 的降水概率,建议带伞。”
这就需要 AI主动去获取最新数据,而不是依赖训练时的旧知识。Function Calling 就是让 AI 能“调用外部函数”来完成这类任务的能力。
✅ 它解决了三大痛点:
- 知识滞后问题
大模型的知识是训练时固定的(比如 GPT-3.5 知识截止到 2024 年底)。通过函数调用,可以让 AI 实时查询数据库、调用天气 API、获取股票价格等,突破“知识过期”的限制。
- 执行外部操作的需求
用户说:“帮我订一张明天去杭州的高铁票。”,AI 不能只回答“你可以去 12306 订票”,而应该能调用订票系统 API 完成操作 —— 这就是“行动力”。
- 避免“胡说八道”(幻觉)
如果 AI 不知道答案,又不能调用工具,它可能会“编造”一个看似合理的回答。有了工具调用,AI 会明确知道:“这个问题我不能直接回答,需要调用天气服务才能解决”,从而减少幻觉。
Spring AI 提供了方便的 API 来定义工具、解决来自模型的工具调用请求以及执行工具调用,下面是Spring AI工具的调用流程,引用至官方图:
① 要在聊天请求中包含工具定义。每个工具定义都包含输入参数的名称、描述和方案。
② 模型通过思考,决定调用工具时,它会发送一个请求给应用程序,其中包含工具名称和输入参数
③ 应用程序负责使用工具名称来识别和执行具有提供的输入参数的工具。
④ 工具调用的结果由应用程序处理。
⑤ 应用程序将工具调用结果发送回模型。
⑥ 模型将工具调用的结果作为附加上下文,生成最终的结果。
4.2 定义 方法(Spring AI 的 ToolCallback)
在 Spring AI 中,我们通过 @Tool
注解来标记一个 Java 方法,告诉框架:“这个方法可以被 AI 调用”。
实战 1:创建一个天气查询工具
为了方便说明问题,这里使用了硬编码,可以调用线上发布的天气 API (比如高德API)
// chapter04/src/main/java/com/kaifamiao/chapter04/service/WatcherToolService.java@Component
@Slf4j
public class WatcherToolService {@Tool(description = "根据城市名称查询当前天气")public String getWeather(@ToolParam(description = "城市名称,如 北京、上海") String city) {log.info("根据城市名称查询当前天气: {}", city);// 模拟调用高德/和风天气 APIif ("西安".equals(city)) {return "西安,晴,气温 25°C,空气质量优";} else if ("上海".equals(city)) {return "上海,多云,气温 28°C,东南风 3 级";} else if ("北京".equals(city)) {return "北京,多云,气温 24°C,西南风 1 级";}return "未找到 " + city + " 的天气信息";}
}
@Tool
注解:标记该方法为一个可被 AI 调用的“工具”。description
:描述工具用途。非常重要! 大模型会根据这个描述决定是否调用它。- 方法参数:支持基本类型(String、int、boolean)和 POJO,Spring AI 会自动解析 JSON 参数。
- 返回值:结果会返回给大模型,由模型决定如何呈现给用户。
💡 方法名不重要,但
description
必须清晰准确,比如"根据城市名查询实时天气",而不是"查天气”。
工具注册与自动发现
Spring AI 会自动扫描所有带有 @Tool
注解的 Bean,并将它们注册到一个“工具箱”中。你不需要手动注册!
🔄 自动注册流程:
- 应用启动时,Spring 容器加载所有
@Component
,@Service
等 Bean。 - Spring AI 遍历这些 Bean,查找带有
@Tool
注解的方法。 - 将这些方法的元信息(名称、描述、参数)收集起来,构建成一个“工具列表”。
- 当调用
ChatClient
时,这个工具列表会作为 Prompt 的一部分发送给大模型。
Spring AI 会将你的工具转换成类似如下的 JSON 结构,发送给大模型:
{"name": "getWeather","description": "根据城市名称查询当前天气","parameters": {"type": "object","properties": {"city": {"type": "string","description": "城市名称,如 北京、上海"}},"required": ["city"]}
}
大模型看到这个描述后,就能理解:“哦,如果用户问天气,我可以调用这个 getWeather
函数。”
实战 2:通过 ChatClient 注册(推荐)
注册工具有多重方式,可以在单次请求中注册工具实例对象
// chapter04/src/main/java/com/kaifamiao/chapter04/controller/ChatController.java
@RestController
@Slf4j
public class ChatController {private final ChatClient chatClient;@Autowiredprivate WatcherToolService watcherToolService;public ChatController(ChatClient.Builder builder) {// this.chatClient = builder.build();this.chatClient = builder.build();}// http://localhost:8080/chat/watcher?question=今天西安天气如何?适合做什么活动?@GetMapping(value = "/chat/watcher", produces = "text/html;charset=UTF-8")public Flux<String> watcher(@RequestParam(defaultValue = "今天西安天气如何?适合做什么活动?")String question) {return chatClient.prompt().tools(watcherToolService).user(question).stream().content();}
}
因为
WatcherToolService
已经被注册到Spring 容器中,所以只需要注入到Controller中即可。tools 方法参数是一个Object类型的数组,所以注册多个工具
启动访问:
GET http://localhost:8080/chat/watcher?question=今天西安天气如何?适合做什么活动?
控制台输出:
根据城市名称查询当前天气: 西安
这说明提供的工具已经被调用了
浏览器输出:
今天西安天气晴朗,气温为25°C,空气质量优,非常适合户外活动!你可以考虑去大雁塔、大唐芙蓉园或城墙附近散步,感受古都的历史氛围;或者到曲江池遗址公园野餐、骑行。此外,这样的好天气也适合拍照、露营或与朋友一起郊外踏青。注意防晒和补充水分,享受美好的一天吧!
工具调用流程
1. 用户提问: “今天西安天气怎么样?”
2. ChatClient 发送请求:Spring AI 将用户问题 + 所有可用工具的描述,一起发送给大模型。
3. LLM 决策是否调用工具: 模型分析问题,发现涉及“实时天气”,而有一个 getWeather 工具可用,于是决定调用它,并生成调用参数:{ "city": "西安" }
4. Spring AI 执行工具方法: 框架自动调用 WeatherTool.getWeather("北京"),得到返回值。
5. 结果返回给 LLM: 执行结果(如“西安,晴,气温 25°C,空气质量优”)被送回给大模型。
6. LLM 生成最终回复: 模型结合原始问题和工具返回结果,生成自然语言回答:今天西安天气晴朗,气温 25°C,空气质量优 ...”
其它注册方式
前面在单次请求中指定了要用到的工具,如果没有指定,也可以在ChatClient.Builder
中指定默认的工具
ChatClient chatClient = ChatClient.builder(chatModel).defaultTools(watcherToolService) // 指定默认的工具相当于全局注册,可以指定多个.build();
4.3 其它方式定义工具
Spring AI 也提供了其它几种方式来定义和使用工具,具体可以参考官方文档:
Function as Tools
:https://docs.spring.io/spring-ai/reference/api/tools.html#_functions_as_toolsTool Specification
: https://docs.spring.io/spring-ai/reference/api/tools.html#_tool_specification
4.4 本地 Tool 的局限性:引出 MCP
虽然 @Tool
非常强大,但它有明显局限:
问题 | 说明 |
---|---|
语言绑定 | 工具必须用 Java 编写 |
进程耦合 | 工具必须与 AI 客户端在同一 JVM |
难以复用 | 每个应用都要重新实现相同工具 |
维护困难 | 工具逻辑与 AI 逻辑混杂 |
🚀 解决方案:Model Context Protocol (MCP)
后面会有章节单独介绍MCP
⚠️ 提前预告:MCP 是构建企业级 AI Agent 生态的关键!
下一章预告:第5章《嵌入模型(Embedding Models)与向量化存储》
在下一章中,我们将进入 AI 的“记忆”世界。
你将学习:
- 什么是 Embedding(嵌入)?为什么它能让计算机理解语义?
- 如何使用 Spring AI 调用 OpenAI、Ollama 等模型生成文本向量
- 如何计算两段文本的“语义相似度”
- 为后续的 RAG(检索增强生成) 打下坚实基础
准备好让 AI “记住”你的知识了吗?🧠
源代码地址:https://github.com/kaiwill/kaifamiao
👉『开发喵AI工具』👈