当前位置: 首页 > news >正文

第十二篇:Spring AI 实战 12|Function Calling(工具调用):让 AI 拥有“动手能力”

前言

在上一章中,我们通过 ChatMemory 让 AI 拥有了“记忆力”。但在真实的企业级业务中,大模型存在天然的局限性:它无法获取实时的业务数据、无法精确执行复杂的内部逻辑,更无法直接操作你的数据库。
为了让 AI 从“只能说”进化到“能做事”,Spring AI 提供了强大的 Function Calling(工具调用) 机制
核心原理:我们在本地定义好 Java 方法,并将这些方法的描述(Schema)告诉大模型。当用户提问时,大模型会根据语义判断是否需要调用工具;如果需要,它会返回一个“调用意图”(包含函数名和参数),由我们的 Spring 程序在本地执行该函数,并将执行结果返回给大模型,最终由大模型组织成自然语言回复给用户。
本章我们以“智能订单 + 库存查询系统”为例,完整讲清 Spring AI Tool Calling 的正确打开方式,并避开 stream 模式的坑。

一、环境前置说明

运行前提:电脑安装 Ollama客户端,提前拉取开源模型文件

  1. JDK:21
  2. Gradle:8.8
  3. SpringBoot:3.5.14
  4. SpringAI:1.1.7
  5. IDEA:2023 社区版
  6. (本章代码是在上一篇的基础上新增/修改的)

二、 AI 从“会说”到“会做”

1、AI只能聊天,不能执行业务动作

在前面的章节中,我们已经让 AI 具备:多轮对话,记忆能力(Chat Memory),流式输出(Stream),但此时的 AI 仍然有一个核心问题:它只能聊天,不能执行业务动作。
例如用户输入查询订单 10001,AI 就无法给出相关的业务性回答。

2、Function Calling 是什么?

Function Calling(工具调用)就是:让 AI 在需要时,自动调用你的 Java 方法。
流程如下:

3、本章目标:智能订单 + 库存系统

我们实现两个工具:

  1. 查询订单
  2. 查询库存

三、业务层(Service)

1. 订单服务

packagecom.example.demo.service;importorg.springframework.stereotype.Service;@ServicepublicclassOrderService{publicStringqueryOrder(StringorderId){return""" 订单号:%s 状态:已发货 快递:顺丰速运 """.formatted(orderId);}}

2. 库存服务

packagecom.example.demo.service;importorg.springframework.stereotype.Service;@ServicepublicclassInventoryService{publicStringqueryInventory(Stringproduct){return""" 商品:%s 库存:128件 """.formatted(product);}}

四、Tool 层(关键)

1. 订单 Tool

packagecom.example.demo.tools;importcom.example.demo.service.OrderService;importorg.springframework.ai.tool.annotation.Tool;importorg.springframework.stereotype.Component;importorg.springframework.stereotype.Service;@ComponentpublicclassOrderTools{privatefinalOrderServiceorderService;publicOrderTools(OrderServiceorderService){this.orderService=orderService;}@Tool(name="queryOrder",description="根据订单号查询订单信息")publicStringqueryOrder(StringorderId){returnorderService.queryOrder(orderId);}}

2. 库存 Tool

packagecom.example.demo.tools;importcom.example.demo.service.InventoryService;importorg.springframework.ai.tool.annotation.Tool;importorg.springframework.stereotype.Component;@ComponentpublicclassInventoryTools{privatefinalInventoryServiceinventoryService;publicInventoryTools(InventoryServiceinventoryService){this.inventoryService=inventoryService;}@Tool(name="queryInventory",description="查询商品库存")publicStringqueryInventory(StringproductName){returninventoryService.queryInventory(productName);}}

五、ChatClient 配置

工具定义好后,我们需要在构建 ChatClient 时将其注入。Spring AI 会在后台自动把这些方法的签名转换成 JSON Schema 传给通义千问。
修改你的 ChatMemoryConfig.java,将Tools 注入并绑定:

@Bean("qwenMemoryChatClient")publicChatClientqwenMemoryChatClient(// 明确指定注入通义千问的模型 Bean@Qualifier("qwenChatModel")ChatModelqwenChatModel,ChatMemorychatMemory,OrderToolsorderTools,InventoryToolsinventoryTools){returnChatClient.builder(qwenChatModel)// 使用 Builder 模式挂载对话记忆 Advisor.defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build()).defaultTools(orderTools,inventoryTools).build();}

七、Controller

编辑controller用于测试

@GetMapping("/chat/call_test")publicStringtest(@RequestParam(defaultValue="default-session")StringconversationId,@RequestParamStringmsg){returnqwenMemoryChatClient.prompt().user(msg).advisors(a->a.param(ChatMemory.CONVERSATION_ID,conversationId)).call().content();}

踩坑:
Tool Calling + stream = 不稳定(Qwen / OpenAI兼容模型);
Tool Calling 必须使用 call()

八、测试效果

  1. 查询订单
    http://localhost:8080/ai/chat/call_test?msg=%E6%9F%A5%E8%AF%A2%E8%AE%A2%E5%8D%9510001
  2. 查询库存
    http://localhost:8080/ai/chat/call_test?msg=iPhone16%E8%BF%98%E6%9C%89%E5%BA%93%E5%AD%98%E5%90%97
    结果如下:

九、本章总结与进阶思考

通过短短几十行代码,我们成功让大模型跨越了“语言世界”,触碰到了真实的“业务世界”。Function Calling 在企业级开发中的核心价值在于:
解耦:AI 只负责“意图识别”和“自然语言组织”,具体的业务逻辑(查库、调第三方接口)依然由我们传统的 Java 代码完成。
安全:AI 永远无法直接执行 SQL,它只能调用我们暴露的、经过严格参数校验的 @Tool 方法。
至此,你的 AI 应用已经具备了“记忆”与“行动”两大核心能力。

十、 参考文献

  1. SpringAI官方文档
http://www.jsqmd.com/news/1016298/

相关文章:

  • 2026年热门的抽绳中转袋/吨袋/盐城中转袋厂家对比推荐 - 行业平台推荐
  • 2026年EPE珍珠棉厂家怎么选?技术、交付与性价比实测对比(含西南、华东、华北产区分析) - 优质品牌商家
  • Terraform云成本预估:在apply前精准预测每月开销
  • 智能电子鼻项目避坑指南:ZPH02、SIM800C模块与STM32联调的那些‘玄学’问题
  • Arduino机械臂小车避坑指南:从面包板乱抖到PCB稳定供电,我的大一项目血泪史
  • Phi-2本地部署实战:2.7B小语言模型轻量级对话系统搭建指南
  • 2026年靠谱的沈阳大型政府机关搬家公司/沈阳大小型居民搬家公司品牌实力榜 - 品牌宣传支持者
  • 告别糊涂账:SAP采购发票与入库单金额对不上的完整排查与调整指南(含物料账影响)
  • 手把手教你用mbedTLS调试TLS连接:从错误码0x7180(MAC验证失败)说开去
  • DCGAN实战:MNIST生成的原理、架构与GAN Hacks调优
  • 微重力下颗粒阻力特性研究及其工程应用
  • 给STM32 LWIP做一次‘性能体检’:手把手教你用Wireshark和iperf诊断网络瓶颈
  • 2026年通用电商彩盒包装/彩盒包装设计厂家选择推荐 - 行业平台推荐
  • 别再被`sasl.kerberos.service.name`搞晕了!手把手教你配置Kafka+Kerberos认证(附主机域名避坑指南)
  • 避坑指南:解决PLC与Matlab通信中最常见的5个连接失败问题(基于S7-1200实测)
  • 别再死记硬背了!用这套实战Demo,5分钟搞懂Prometheus四大核心Metric类型
  • 影刀RPA新手教程_XPath语法速查表从入门到实战的15个核心表达式
  • 芯片测试中AU故障飙升至45%?可能是你的DFT约束没设对(以sync_set_reset为例)
  • QGIS 3.34.0尝鲜3DTiles:大雁塔模型加载实测与性能优化踩坑全记录
  • 线性回归实战指南:从零搭建可解释的业务预测模型
  • 用HAL库重写那个“只能收一个字节”的STM32串口中断,我发现了CubeMX没告诉你的细节
  • 温度依赖型神经网络模型设计与热力学特性分析
  • 从Notebook到生产环境的ML模型部署实战指南
  • AI安全新范式:Mythos如何实现漏洞发现与利用的自动化闭环
  • 入局智能体云时代:Google Cloud全栈赋能企业数字化新变革
  • 终极Navicat重置方案:Mac版Navicat16/17无限试用完整指南
  • 六类推理优化模式:降低AI推理成本40%的工程实践
  • 数据工程师生存地图:从语境缺失到系统性工程能力
  • HIVE面试别再死记硬背了!从内部表到数据倾斜,我用一个真实项目案例给你讲透
  • Emoji与Emoticon在文本挖掘中的语义处理实战