Spring AI Alibaba 快速上手:使用 DashScope 实现 Tool Calling(函数调用)实战

发布时间:2026/10/6 18:44:35

Spring AI Alibaba 快速上手:使用 DashScope 实现 Tool Calling(函数调用)实战 在 AI 应用开发中让大模型调用外部工具、访问实时数据或执行业务逻辑是常见需求。Spring AI Alibaba 结合阿里云 DashScope通义千问提供了简洁的 Tool Calling函数调用能力模型可以自动识别用户意图并调用注册的 Java 方法再将结果融入对话。本文以“查询天气”为案例完整演示在 Spring Boot 项目中集成 DashScope并分别使用注解Tool、接口Function以及FunctionTool.builder Lambda三种方式定义工具。同时针对每种工具定义方式均展示基于ChatModel底层手动循环 和ChatClient自动工具闭环 的调用实现共六种组合并解决ChatClient无法自动注入的问题。重要前置提示底层ChatModel#call()只负责和大模型网络通信不会自动执行工具。如果直接调用收到模型返回FunctionCall后直接返回JSON结构体不会执行业务逻辑。想要完整工具调用闭环方案A推荐使用ChatClient内部ToolCallingAdvisor自动完成工具执行多轮对话方案B底层API使用ChatModelDefaultToolCallingManager手写while循环驱动工具调用。2. 环境准备2.1 添加依赖在pom.xml中引入 Spring AI Alibaba 的 DashScope 起步依赖dependencygroupIdcom.alibaba.cloud.ai/groupIdartifactIdspring-ai-alibaba-starter-dashscope/artifactId!-- 请使用最新版本例如 1.0.0-M3 --/dependency提示建议在dependencyManagement中引入 Spring AI Alibaba BOM 统一管理版本。2.2 配置 application.propertiesserver.port8013# 设置全局编码格式server.servlet.encoding.enabledtrueserver.servlet.encoding.forcetrueserver.servlet.encoding.charsetUTF-8spring.application.nameSAA-13ToolCalling# SpringAIAlibaba Configspring.ai.dashscope.api-key${aliQwen-api}请提前在阿里云开通 DashScope 服务并获取 API Key设置环境变量aliQwen-api你的key。3. 定义工具三种方式3.1 方式一使用 Tool 注解声明式工具使用Tool注解标记 Java 方法Spring AI 会自动解析方法签名、参数和描述生成可供大模型调用的工具元数据。import org.springframework.ai.tool.annotation.Tool;public class WeatherTools {/*** 查询指定城市的天气* returnDirect false 表示工具结果会再次交给大模型由大模型组织最终回复*/Tool(description 查询指定城市的天气情况, returnDirect false)public String getWeather(String city) {// 实际项目中可调用第三方天气 API这里用模拟数据演示return String.format(%s晴气温 25℃湿度 40%%, city);}}关键参数说明description工具的描述信息大模型会根据它判断是否以及何时调用该函数。returnDirecttrue工具返回后直接作为最终响应不再调用大模型。false工具结果会送回给大模型由大模型结合上下文生成更自然的回答。3.2 方式二实现 Function 接口编程式工具通过实现java.util.function.FunctionT, R接口并包装为FunctionTool可以更灵活地控制工具逻辑适合复杂业务场景便于做代理、鉴权、单元测试。① 定义入参 recordpublic record WeatherRequest(String city) {}② 实现 Function 接口import org.springframework.stereotype.Component;import java.util.function.Function;Componentpublic class WeatherTool implements FunctionWeatherRequest, String {Overridepublic String apply(WeatherRequest request) {// 实际项目中可在此调用天气 APIString city request.city();return String.format(%s多云气温 22℃风力 3 级, city);}}③ 包装为 FunctionTool⚠️不推荐直接new FunctionTool(weatherTool)无元数据构造必须通过builder设置name、description大模型才能识别工具。生产最佳实践不要在Controller方法内每次请求构建FunctionTool统一在配置类注册为Bean复用。3.3 方式三FunctionTool.builder Lambda 编程构建进阶动态工具这种方式不需要编写注解也不需要实现Function接口直接使用 Lambda 表达式定义函数逻辑并通过FunctionTool.builder构建工具。它最大的优势是灵活可以动态生成工具、临时定义逻辑尤其适合需要根据运行时条件生成不同工具的场景。这里继续复用 3.2 中定义的WeatherRequestrecord 作为入参模型。import org.springframework.ai.tool.function.FunctionTool;import java.util.function.Function;public class WeatherToolLambda {/*** 使用 Lambda 定义天气查询逻辑*/public static final FunctionWeatherRequest, String WEATHER_FUNCTION request - {String city request.city();return String.format(%s阴气温 18℃风力 2 级, city);};/*** 通过 FunctionTool.builder 构建 FunctionTool*/public static FunctionTool createWeatherTool() {return FunctionTool.builder(getWeather, WEATHER_FUNCTION).description(查询指定城市的天气情况).inputType(WeatherRequest.class).returnDirect(false).build();}}关键参数说明name(getWeather)工具名称模型返回的函数调用请求会使用该名称。description(...)工具描述用于模型判断是否调用。inputType(WeatherRequest.class)指定入参类型Spring AI 会据此生成 JSON Schema。returnDirect(false)工具结果是否直接返回默认false。4. 使用 ChatModel 进行 Tool Calling底层手动循环使用底层ChatModel必须引入DefaultToolCallingManager手动驱动工具执行循环否则只能拿到FunctionCall JSON不会执行业务工具。4.1 手动配置 ChatClient解决自动注入问题在当前 Spring AI Alibaba 版本中ChatClient默认不会自动注入需要通过Configuration显式注册 Bean。同时将Function接口包装后的FunctionTool注册为BeanController直接注入复用避免每次请求重复构建对象。import org.springframework.ai.chat.client.ChatClient;import org.springframework.ai.chat.model.ChatModel;import org.springframework.ai.tool.function.FunctionTool;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;Configurationpublic class SaaLLMConfig {Beanpublic ChatClient chatClient(ChatModel chatModel) {return ChatClient.builder(chatModel).build();}/*** 将Function接口实现包装为FunctionTool注册为单例Bean复用*/Beanpublic FunctionTool queryWeatherFunctionTool(WeatherTool weatherTool){return FunctionTool.builder(weatherTool).name(queryWeather).description(查询指定城市天气情况).build();}}4.2 注解方式 ChatModel手动循环import com.example.study.tools.WeatherTools;import jakarta.annotation.Resource;import org.springframework.ai.chat.model.ChatModel;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.ai.tool.manager.DefaultToolCallingManager;import org.springframework.ai.tool.manager.ToolCallingManager;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;RestControllerpublic class ToolCallingController {Resourceprivate ChatModel chatModel;private final ToolCallingManager toolCallingManager new DefaultToolCallingManager();GetMapping(/toolcall/chat-annotation)public String chatWithAnnotation(RequestParam(name msg, defaultValue 北京天气怎么样) String msg) {// 1. 将注解式工具注册到回调数组ToolCallback[] tools ToolCallbacks.from(new WeatherTools());// 2. 构建带有工具回调的 ChatOptionsvar options ToolCallingChatOptions.builder().toolCallbacks(tools).build();// 3. 组装 PromptPrompt prompt new Prompt(msg, options);var response chatModel.call(prompt);// 手动驱动工具调用循环最大循环次数防止死循环int maxRound 5;int round 0;while (response.hasToolCalls() round maxRound) {var execResult toolCallingManager.executeToolCalls(prompt, response);prompt execResult.conversationHistory();response chatModel.call(prompt);round;}return response.getResult().getOutput().getText();}}4.3 接口方式 ChatModel手动循环import jakarta.annotation.Resource;import org.springframework.ai.chat.model.ChatModel;import org.springframework.ai.chat.prompt.Prompt;import org.springframework.ai.model.tool.ToolCallingChatOptions;import org.springframework.ai.tool.ToolCallback;import org.springframework.ai.tool.function.FunctionTool;import org.springframework.ai.tool.manager.DefaultToolCallingManager;import org.springframework.ai.tool.manager.ToolCallingManager;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;RestControllerpublic class ToolCallingController {Resourceprivate ChatModel chatModel;// 直接注入配置类构建完成的FunctionTool Bean不再重复builderResourceprivate FunctionTool queryWeatherFunctionTool;private final ToolCallingManager toolCallingManager new DefaultToolCallingManager();GetMapping(/toolcall/chat-function)public String chatWithFunction(RequestParam(name msg, defaultValue 上海天气如何) String msg) {ToolCallback[] tools new ToolCallback[]{queryWeatherFunctionTool};var options ToolCallingChatOptions.builder().toolCallbacks(tools).build();Prompt prompt new Prompt(msg, options);var response chatModel.call(prompt);int maxRound 5;int round 0;while (response.hasToolCalls() round maxRound) {var execResult toolCallingManager.executeToolCalls(prompt, response);prompt execResult.conversationHistory();response chatModel.call(prompt);round;}return response.getResult().getOutput().getText();}}4.4 Lambda 方式 ChatModel手动循环import com.example.study.tools.WeatherToolLambda;import jakarta.annotation.Resource;import org.springframework.ai.chat.model.ChatModel;import org.springframework.ai.chat.prompt.Prompt;import org.springframework.ai.model.tool.ToolCallingChatOptions;import org.springframework.ai.tool.ToolCallback;import org.springframework.ai.tool.function.FunctionTool;import org.springframework.ai.tool.manager.DefaultToolCallingManager;import org.springframework.ai.tool.manager.ToolCallingManager;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;RestControllerpublic class ToolCallingController {Resourceprivate ChatModel chatModel;private final ToolCallingManager toolCallingManager new DefaultToolCallingManager();GetMapping(/toolcall/chat-lambda)public String chatWithLambda(RequestParam(name msg, defaultValue 杭州天气怎么样) String msg) {FunctionTool functionTool WeatherToolLambda.createWeatherTool();ToolCallback[] tools new ToolCallback[]{functionTool};var options ToolCallingChatOptions.builder().toolCallbacks(tools).build();Prompt prompt new Prompt(msg, options);var response chatModel.call(prompt);int maxRound 5;int round 0;while (response.hasToolCalls() round maxRound) {var execResult toolCallingManager.executeToolCalls(prompt, response);prompt execResult.conversationHistory();response chatModel.call(prompt);round;}return response.getResult().getOutput().getText();}}5. 使用 ChatClient 进行 Tool Calling自动闭环推荐ChatClient内置ToolCallingAdvisor自动完成工具调用循环不需要手动写while循环支持流式返回。5.1 注解方式 ChatClient 调用import com.example.study.tools.WeatherTools;import jakarta.annotation.Resource;import org.springframework.ai.chat.client.ChatClient;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;RestControllerpublic class ToolCallingController {Resourceprivate ChatClient chatClient; // 注入手动配置的 BeanGetMapping(/toolcall/chatclient-annotation)public FluxString chatClientWithAnnotation(RequestParam(name msg, defaultValue 广州天气怎么样) String msg) {return chatClient.prompt(msg).tools(new WeatherTools()) // 直接传入注解工具对象.stream() // 启用流式调用.content(); // 返回文本内容的 Flux}}5.2 接口方式 ChatClient 调用import jakarta.annotation.Resource;import org.springframework.ai.chat.client.ChatClient;import org.springframework.ai.tool.function.FunctionTool;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;RestControllerpublic class ToolCallingController {Resourceprivate ChatClient chatClient;// 直接复用配置类中已经构建好的FunctionTool BeanResourceprivate FunctionTool queryWeatherFunctionTool;GetMapping(/toolcall/chatclient-function)public FluxString chatClientWithFunction(RequestParam(name msg, defaultValue 深圳天气如何) String msg) {return chatClient.prompt(msg).tools(queryWeatherFunctionTool) // 传入已经构建完成的FunctionTool.stream().content();}}5.3 Lambda 方式 ChatClient 调用import com.example.study.tools.WeatherToolLambda;import jakarta.annotation.Resource;import org.springframework.ai.chat.client.ChatClient;import org.springframework.ai.tool.function.FunctionTool;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;RestControllerpublic class ToolCallingController {Resourceprivate ChatClient chatClient;GetMapping(/toolcall/chatclient-lambda)public FluxString chatClientWithLambda(RequestParam(name msg, defaultValue 成都天气如何) String msg) {FunctionTool functionTool WeatherToolLambda.createWeatherTool();return chatClient.prompt(msg).tools(functionTool) // 传入 FunctionTool.stream().content();}}6. 测试与效果启动项目后分别测试六个接口。底层ChatModel接口返回完整文本ChatClient系列接口为SSE流式输出浏览器直接访问即可看到逐字输出。① ChatModel 注解工具curl http://localhost:8013/toolcall/chat-annotation?msg北京天气怎么样响应示例北京晴气温 25℃湿度 40%② ChatModel 接口工具curl http://localhost:8013/toolcall/chat-function?msg上海天气如何响应示例上海多云气温 22℃风力 3 级③ ChatModel Lambda 工具curl http://localhost:8013/toolcall/chat-lambda?msg杭州天气怎么样响应示例杭州阴气温 18℃风力 2 级④ ChatClient 注解工具流式 SSE浏览器打开http://localhost:8013/toolcall/chatclient-annotation?msg广州天气怎么样会看到文本逐渐输出例如广州晴气温 25℃湿度 40%⑤ ChatClient 接口工具流式 SSE浏览器打开http://localhost:8013/toolcall/chatclient-function?msg深圳天气如何会看到文本逐渐输出例如深圳多云气温 22℃风力 3 级⑥ ChatClient Lambda 工具流式 SSE浏览器打开http://localhost:8013/toolcall/chatclient-lambda?msg成都天气如何会看到文本逐渐输出例如成都阴气温 18℃风力 2 级注意以上示例中工具返回结果后因为returnDirect false注解方式默认或FunctionTool默认行为大模型会再次加工生成自然语言回复。若需直接返回工具结果可调整配置或使用returnDirect选项。7. 原理与关键点解析7.1 Tool Calling 工作流程用户提问→ 携带已注册工具的元数据发送给 DashScope 大模型。模型判断→ 如果需要调用某个工具返回一个“函数调用请求”包含工具名和参数。框架执行→ Spring AI 根据返回的工具名找到对应 Java 方法并执行获取结果。 ChatClientAdvisor自动执行原始ChatModel必须通过ToolCallingManager手动执行。二次生成→ 若returnDirect false框架将工具返回结果重新提交给模型模型结合上下文生成最终回复若为true则直接返回工具结果。7.2 三种工具定义方式对比特性Tool 注解方式Function 接口方式Lambda FunctionTool.builder定义方式在方法上添加注解实现FunctionT, R接口Lambda 表达式 builder 构建参数传递方法参数自动映射通过 record 封装入参通过 record 封装入参inputType指定灵活度简单快速适合单一方法更灵活适合复杂业务逻辑、代理鉴权、单元测试最灵活可动态构建无需类定义注册方式ToolCallbacks.from(obj)或.tools(obj)配置类Bean注册Controller直接注入复用FunctionTool.builder(...).build()或.tools(functionTool)推荐场景轻量级工具快速接入需要依赖注入、复杂过滤或自定义逻辑动态工具、临时 Lambda、避免编写类7.3 为什么 ChatClient 不能自动注入目前 Spring AI Alibaba 的自动配置还未将ChatClient纳入标准 Bean 管理因此需要我们在Configuration类中手动创建并返回。随着版本迭代这个问题很可能会被解决留意官方更新即可。7.4 returnDirect 的选择需要大模型润色结果例如“北京今天天气晴朗温度 25℃建议穿短袖” → 设为false。工具结果已是最终答案例如查询用户余额后直接返回数字 → 设为true可以节省一次模型调用成本。对于Function接口方式通过builder设置returnDirectFunctionTool functionTool FunctionTool.builder(weatherTool).name(queryWeather).description(查询指定城市天气情况).returnDirect(true).build();对于FunctionTool.builder Lambda方式可在 builder 中设置FunctionTool functionTool FunctionTool.builder(getWeather, WEATHER_FUNCTION).description(查询指定城市的天气情况).inputType(WeatherRequest.class).returnDirect(true) // returnDirect true.build();8. 总结本文以查询天气为例完整演示了 Spring AI Alibaba 中 Tool Calling 的六种实现组合注解工具 ChatModel底层手动循环接口工具 ChatModel底层手动循环Lambda 工具 ChatModel底层手动循环注解工具 ChatClient自动闭环流式接口工具 ChatClient自动闭环流式Lambda 工具 ChatClient自动闭环流式同时解决了ChatClient无法自动注入的问题并说明了流式返回的实现方法。生产优化点静态工具对象不要在Controller接口方法内重复构建统一在Configuration注册单例Bean复用减少对象创建开销。生产建议业务开发优先选择ChatClient避免手写工具循环只有需要完全接管工具执行流程、自定义鉴权拦截、需要用户确认后再执行工具场景才使用原始ChatModel DefaultToolCallingManager编程式Function工具优先用builder不要直接无元参数构造FunctionTool安全不要只在工具内部鉴权优先外层动态裁剪ToolCallback工具内部做兜底校验。Tool注解适合快速接入简单工具Function接口适合需要依赖注入或复杂业务逻辑的场景FunctionTool.builder Lambda适合动态构建、临时定义工具避免编写额外类。希望这篇教程能帮助你快速上手 Spring AI Alibaba 的函数调用功能为构建智能体应用打下坚实基础。
延伸阅读

更多相关文章

2026/10/6 19:44:39

多用户多仓库进销存系统源码解析:基于uniapp的跨端库存管理方案

跑进销存这条路的人都知道,真正的痛点从来不是“做一张入库单”或者“打一张出库单”,而是当公司有多个门店、多个仓,甚至多人同时操作时,数据到底怎么算得清。前阵子我拿到一套基于uniapp的多用户多仓库进销存管理系统源码&#…

2026/10/6 19:44:39

用OpenSSH与dotfiles打造标准化Shell环境:登录配置安全一键搞定

把一套散落在不同机器上的 Shell 环境,收拢成可复用、可迁移、可安全交付的标准化方案,这件事我琢磨了很久。OpenShell 这个名字听起来像是一个开源项目,其实它更像是我给自己定的一套“终端环境建设规则”:把 SSH 登录、dotfiles…

2026/10/6 19:44:39

C++自定义字面量高级用法:编译期单位换算与字符串解析

一提到C里的自定义字面量,很多人的第一反应是“哦,不就是operator""嘛,给数值加个后缀,看着挺酷,实际上用不上”。说实话,我早几年也是这个态度。直到有一次在系统里接测量模块,满屏的…

2026/10/6 19:44:39

华为云码道代码智能体实战:从零上手到代码检视与修复

1. 从零上手华为云码道代码智能体:一个后端老兵的真实踩坑记录 第一次听说华为云码道(CodeArts)代码智能体的时候,我正被一个祖传项目折磨得够呛。那是一个跑了快六年的老系统,代码里到处是复制粘贴留下的痕迹&#xf…

2026/10/6 19:39:38

Agent-Reach 实战:用 CLI 让 AI Agent 稳定触达外部世界

Agent-Reach 这个名字第一次看到的时候,我下意识以为是某个网络代理工具,后来翻了翻社区讨论和相关的技术标签,才反应过来它指向的是另一件事:让 AI Agent 真正"够得着"外部世界的那一层能力。CLI、Python、AI Agent 这…

2026/10/5 6:32:56

Jev+Agent接管浏览器:browser-use实战与jev-ultrafast性能优化

1. 从“Jev”说起:为什么我要把Agent接进浏览器“Jev”这个词最近在圈子里出现的频率越来越高,很多人第一次听到会以为是某个新模型的名字,其实它更像是一种思路——把Jev模型的能力当作底座,通过Agent的方式去接管浏览器&#xf…

2026/10/6 4:01:51

多智能体集群实战:DeepAgents编排、MCP与A2A协议及Skills体系

1. 从"单兵作战"到"集群协同":多智能体编排到底在解决什么问题如果你最近在折腾 Agent 相关的东西,大概率会有一种感觉:单个 Agent 能做的事情,其实很快就摸到天花板了。你给它一个提示词,挂几个工…

2026/10/6 17:46:51

无源低通滤波器设计实战:从RC到LC,手把手教你避开那些坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/6 0:03:23

MR25H40CDF+STM32F031C6工业级高可靠数据存储方案

1. 项目概述:为什么在工业现场非得用 MR25H40CDF 配 STM32F031C6 做数据存储?在工厂产线的 PLC 控制柜里、在风电变流器的散热片背面、在矿井监测终端的金属外壳下,你经常能看到一块指甲盖大小的黑色芯片——它既不是 Flash,也不是…

2026/10/6 0:03:23

MRAM+STM32工业断电数据保全实战指南

1. 项目概述:为什么在工业现场非得用 MR25H40CDF 配 STM32F031C6 做数据存储?在工厂产线的PLC柜里、在野外无人值守的环境监测终端里、在高速运转的包装机控制板上,你经常能看到一块指甲盖大小的黑色芯片,旁边贴着“MR25H40CDF”丝…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑