当前位置: 首页 > 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.zskr.cn/news/1528119.html

相关文章:

  • 2026年EPE珍珠棉厂家怎么选?技术、交付与性价比实测对比(含西南、华东、华北产区分析) - 优质品牌商家
  • 告别糊涂账:SAP采购发票与入库单金额对不上的完整排查与调整指南(含物料账影响)
  • 智能电子鼻项目避坑指南:ZPH02、SIM800C模块与STM32联调的那些‘玄学’问题
  • 别再被`sasl.kerberos.service.name`搞晕了!手把手教你配置Kafka+Kerberos认证(附主机域名避坑指南)
  • 别再死记硬背了!用这套实战Demo,5分钟搞懂Prometheus四大核心Metric类型
  • AI安全新范式:Mythos如何实现漏洞发现与利用的自动化闭环
  • 入局智能体云时代:Google Cloud全栈赋能企业数字化新变革
  • HIVE面试别再死记硬背了!从内部表到数据倾斜,我用一个真实项目案例给你讲透
  • 别再被‘目标计算机积极拒绝’搞懵了!手把手教你排查pip安装LangChain时的网络/代理问题
  • RAG嵌入模型选型实战指南:避开MTEB陷阱,聚焦业务语义对齐
  • DisplayPort调试实战:当你的4K显示器黑屏时,如何通过DPCD寄存器状态定位链路训练失败原因
  • 2026年电动开窗器链条式厂商综合实力分析:谁更值得信赖? - 优质品牌商家
  • 保姆级教程:在银河麒麟V10系统上,为飞腾FT2000设备制作grub2启动U盘(附常见错误排查)
  • CH32V30x开发避坑指南:MounRiver里移动了Core、Ld这些文件夹,编译报错怎么一步步调回来?
  • 从一道笔试题看编程基本功:字符分类与闰年判断的N种实现与优化思路
  • 多模态RAG实战:从PDF解析到图文检索的可复现工作流
  • 机器学习模型监控实战:数据漂移、性能衰减与业务影响三层防御
  • 小米穿戴表盘设计终极指南:如何用Mi-Create创建个性化表盘
  • Autosar CAN开发避坑指南:为什么你的板子接上CAN盒就是不通?从物理层开始排查
  • 嵌入式开发避坑指南:汽车ECU刷写中Flash Driver的RAM地址分配与安全实践
  • 2026年深圳静电梅花联轴器选型指南:可靠性、性能与本土化服务深度分析 - 优质品牌商家
  • 你的时间序列模型稳吗?EViews平稳性检验与ARCH效应排查避坑指南
  • XMENTOR:解决可解释AI中的解释冲突难题
  • VIM插件折腾记:从coc.nvim安装到搞定C++/Python补全,我踩过的那些坑
  • 避坑指南:Dell T440服务器换硬盘后,千万别忘了处理这个‘Foreign’状态
  • 高级索引技术:突破基础RAG检索瓶颈的四大实战方法
  • 联邦学习在医疗报告生成中的挑战与FedTAR框架创新
  • 【课程设计/毕业设计】基于 SpringBoot 的社区垃圾投放监督管理系统的设计与实现【附源码、数据库、万字文档】
  • 避开这些坑!用上海市计算机学会乙组真题‘平衡01串’和‘逆序对数’来检验你的基础算法掌握度
  • 别死记硬背了!用这5个真实案例拆解NISP二级里的密码学与网络安全核心