Spring AI Alibaba集成实战:对话、流式输出、函数调用与RAG

发布时间:2026/10/20 5:11:13
Spring AI Alibaba集成实战:对话、流式输出、函数调用与RAG 最近接了个内部系统改造的活儿要把大模型对话能力嵌进现有 Spring Boot 微服务里。动手前我调研了一圈最后选了 Spring AI Alibaba可以说这个选择直接把我从“手写 HTTP 调用 JSON 解析地狱”里救了出来。如果你也是 Java 技术栈打算把通义系列模型的对话、绘图、语音、知识库问答能力集成到项目里那么这篇教程基本覆盖了你从零到生产可用所需要的全部关键步骤。我会从环境准备、第一个对话、流式输出、函数调用一直讲到我实际踩过的坑和排查思路全程是能直接抄作业的代码和配置。1. 先搞清楚 Spring AI Alibaba 是什么避免用错方向1.1 一句话定位Spring AI Alibaba 是构建在 Spring AI 抽象之上的模型接入实现它的核心价值是把大模型能力以 Spring 标准 API 的形式暴露给开发者。换句话说它是一个 Starter 加一堆适配器你只要在 pom 里加上依赖在 application.yml 里配好模型服务商的密钥就可以像注入普通 Bean 一样注入 ChatClient、ImageModel、AudioSpeechModel 这些对象然后调用方法拿到结果。它解决的痛点非常具体Java 开发者接大模型时绕不开鉴权、网络、重试、Token 计费、流式传输、JSON Schema 这类跟业务无关的脏活。直接用模型服务商 SDK 也不是不行但一换服务商整个调用层基本要重写。而基于 Spring AI Alibaba你的业务代码面向抽象接口底层换了模型上层代码通常不用动。1.2 和直接用模型服务商 SDK 的区别我给团队内部讲选型时列过一张对比表今天也贴出来对比维度直接用 SDK使用 Spring AI Alibaba业务代码占比约 20%剩下都在处理网络和序列化约 80%专注 Prompt 和结果处理换模型服务商成本高调用层推倒重来低多数场景改配置即可调试手段自己封装日志继承 Spring Boot 配置体系和 Actuator与 Spring 生态整合需要在 Service 层自己做封装原生支持 Bean 注入、AOP、属性绑定学习门槛看服务商文档看 Spring 编程模型即可这里我不展开高深理论只讲一个最直观的体验用 SDK 时我想打个日志看请求参数和响应耗时得在拦截器里写一大堆代码用 Spring AI Alibaba 之后它天然融入 Spring 的配置体系和生命周期管理很多观测能力顺手就有。1.3 版本与生态边界Spring AI Alibaba 1.x 基于 Spring AI 1.0我的建议是 Spring Boot 用 3.4.x 或 3.5.xJDK 用 17 或 21。还在用 Spring Boot 2.7 的老项目别想着直接加依赖得先把 Spring Boot 升上去这一步牵扯的兼容问题后面第五节我会专门聊。另外要注意Spring AI Alibaba 不是“大模型全家桶”它目前聚焦在通义系列模型以及与 Spring AI 生态相关的向量数据库、函数调用等能力上。如果项目里还要接别的模型服务商Spring AI 本身也有对应的适配模块只是不在 Alibaba 这个 Starter 的范围内这是很多刚上手的人容易搞混的地方。2. 动手前准备版本、依赖、密钥一个都不能少2.1 确认 JDK 与 Spring Boot 基线我见过太多从网上复制依赖却起不来的情况问题往往不在代码而在版本基线。Spring AI Alibaba 1.0.0 发布后对 Spring Boot 版本有明确的适配范围。我自己用 Spring Boot 3.4.5 JDK 21 跑得很稳用 3.5.x 也没问题。如果你用 Spring Boot 4.x需要选择支持该版本的 Spring AI Alibaba 后续版本而不是拿 1.0.0 硬试。检查基线可以用最简单的办法项目里先只留 spring-boot-starter-parent 作为父工程然后看启动日志中 Spring 版本和 Spring AI 内置版本是否匹配。只要出现版本冲突第一屏的报错信息通常会直接给出冲突类名千万别跳过看。2.2 引入 BOM 和 Starter依赖引入的核心在版本管理。Spring AI Alibaba 提供了自己的 BOM用来统一管理它内部各个模块的版本。我建议在 dependencyManagement 里引入它而不是在上百个依赖里手工写版本号。dependencyManagement dependencies dependency groupIdcom.alibaba.cloud.ai/groupId artifactIdspring-ai-alibaba-bom/artifactId version1.0.0/version typepom/type scopeimport/scope /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-dependencies/artifactId version3.4.5/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement然后加最常用的 Starterdependency groupIdcom.alibaba.cloud.ai/groupId artifactIdspring-ai-alibaba-starter/artifactId /dependency这个 Starter 会把对话模型、嵌入模型、图片模型、语音模型的基础依赖都带进来。如果后续只想用某个能力比如只做聊天可以换成 spring-ai-alibaba-chat-starter 之类的细分模块减少无谓的依赖体积。不过第一次做 Demo直接用全量 Starter 是最省心的。2.3 配置模型服务商密钥和模型参数配置文件是接入过程中最容易翻车的一环。注意不同版本的配置前缀有变化1.0.0 版本常用的配置如下spring: ai: dashscope: api-key: ${DASHSCOPE_API_KEY} chat: options: model: qwen-plus temperature: 0.7这里我把 api-key 用环境变量引用而不是硬编码进配置避免密钥泄漏到代码仓库。模型参数里的 temperature 控制随机性做代码生成类任务我会调到 0.2 左右做创意写作再调回 0.8这个没有绝对标准按场景试。提示如果你拿到的新版本默认配置前缀变成了 spring.ai.alibaba说明官方调整了配置结构。最稳妥的做法是直接看对应版本官方文档的配置页不要凭经验硬猜。3. 5分钟跑通第一个对话ChatClient 基础实战3.1 注入 ChatClient 并发送请求Spring AI Alibaba 的入口对象是 ChatClient这是在 Spring AI 1.0 里主推的流式 API。先声明一个配置类把 ChatClient.Builder 注入进来然后构建我们的客户端Service public class ChatService { private final ChatClient chatClient; public ChatService(ChatClient.Builder builder) { this.chatClient builder .defaultSystem(你是一个乐于助人的技术助手回答尽量简洁准确。) .build(); } public String ask(String question) { return chatClient.prompt(question) .call() .content(); } }代码就这么点。defaultSystem 相当于给模型设定了一段系统提示词每个请求都会带上。如果你希望每个请求单独指定系统提示可以在 prompt 之后追加 .system(...) 覆盖。3.2 验证调用成功的两个信号写完调一下控制台能看到两个关键信号第一是请求日志里出现模型服务商返回的 Token 统计第二是返回内容符合预期。如果返回空字符串大概率是 Prompt 被系统提示词约束得太死或者是 temperature 过低导致模型倾向于保守回答排查方向别搞反。第一次跑通之后我建议顺手做一个简单的耗时记录。Spring AI 的同步调用通常 2 到 5 秒如果你的接口动辄十几秒先别怪模型慢检查是不是网络代理、DNS 解析或者本地网关拦截了请求。3.3 参数传递让每次请求可定制真实业务里不会只有一个固定模型参数。使用 ChatClient 的请求级配置可以做到不同接口不同模型String result chatClient.prompt(帮我写一封请假邮件) .options(DashScopeChatOptions.builder() .withModel(qwen-turbo) .withTemperature(0.4) .build()) .call() .content();qwen-turbo 适合快速、低成本的场景qwen-plus 是均衡选择qwen-max 适合复杂推理。把模型选择权从全局配置挪到请求级是很多生产项目最终都会走的方向因为不同业务场景对成本和效果的要求完全不同。4. 让对话更像真实产品流式输出与多轮记忆4.1 流式输出解决“响应太慢”的体验问题同步调用虽然代码简单但用户看到的是一个一直在转圈的按钮。换成流式输出后模型生成到一半界面就可以开始打字。Spring AI Alibaba 对 Spring AI 的流式支持是完整的返回一个响应式流public FluxString askStream(String question) { return chatClient.prompt(question) .stream() .content(); }Controller 层直接返回 Flux配合 WebFlux 的响应式协议前端就能用标准 SSE 接收。如果你项目里还是 Spring MVC也可以把 Flux 收集成 List 后一次性返回但那就失去流式的意义了。注意流式输出一定要处理好连接断开的情况。前端页面刷新或主动取消请求时Flux 的取消信号会自动传到下游但你自己的业务逻辑里如果有状态清理动作需要监听取消事件否则会造成状态残留。4.2 多轮记忆记住上下文的正确姿势大模型本身没有记忆所有“记住”都是靠把历史消息重新发给模型实现的。在 Spring AI 里ChatMemory 负责管理会话历史InMemoryChatMemory 是入门最简单的实现ChatMemory chatMemory new InMemoryChatMemory(); ChatClient chatClient ChatClient.builder(chatModel) .defaultMemory(chatMemory) .build(); chatClient.prompt(我叫小李我喜欢骑行).call().content(); chatClient.prompt(我叫什么名字我的爱好是什么).call().content();第二次提问时模型能给出正确答案因为 ChatClient 自动把前一轮的对话消息拼了进去。要控制记忆长度可以用 WindowChatMemory 并设置窗口大小比如只保留最近 10 条消息避免请求体越来越长导致费用飙升。4.3 记忆的副作用与隔离策略多轮记忆用起来爽坑也不少。多用户共用同一个 ChatClient 时如果用的是一份默认记忆A 用户的对话会被 B 用户看到这在生产环境是严重事故。解决办法是按会话维度隔离记忆在每次 prompt 前用 conversationId 指定记忆空间chatClient.prompt(question) .memory(conversationId) .call() .content();我实际项目的做法是把 conversationId 存到用户会话或请求头里每次调用都显式传进去。这样即使用户同时开两个窗口对话上下文也不会串。5. 把大模型接进业务函数调用与结构化输出5.1 函数调用让模型帮你调 Java 方法函数调用是落地最实用的一个能力。它的原理不神秘你告诉模型有哪些函数可用模型在回答时发现需要实时信息就会在回复里请求调用某个函数框架拿到请求后帮你执行 Java 方法再把结果回传给模型生成最终答案。举个例子用户问“北京今天适合跑步吗”模型如果不调用天气函数给出的回答就是瞎编。我们定义一个天气查询函数Bean Description(查询指定城市的当天天气情况) public FunctionWeatherRequest, WeatherResponse currentWeather() { return req - { // 这里替换为真实的天气服务调用 return new WeatherResponse(req.city(), 晴, 26); }; } public record WeatherRequest(String city) {} public record WeatherResponse(String city, String condition, int temperature) {}然后把这个函数注册到 ChatClient 上ChatClient chatClient ChatClient.builder(chatModel) .defaultFunctions(currentWeather) .build();此时你再问“北京天气怎样”模型会先生成函数调用参数框架调完函数再把结果组织成自然语言回答。关键细节是 Description 一定要写清楚它决定模型什么时候该用这个函数描述越模糊模型越容易乱调。5.2 结构化输出把回复解析成对象直接拿 content 字符串让前端自己去解析 JSON迟早会出事。Spring AI 提供了结构化输出能力只要返回类型能匹配就能把模型输出映射成对象record Recipe(String name, ListString ingredients, int cookMinutes) {} Recipe recipe chatClient.prompt(给我一个番茄炒蛋的家常做法返回 JSON) .call() .entity(Recipe.class); System.out.println(recipe.name());这里背后的逻辑是框架根据目标类生成 JSON Schema 提示引导模型按结构输出再反序列化到对象。需要注意 record 类型是支持的但字段别太复杂嵌套层级一旦超过三层模型输出稳定性会明显下降。5.3 结构化输出失败的兜底策略模型毕竟是概率输出偶尔会返回残缺 JSON。我一般会在业务层加 try-catch捕获解析异常后做一次“修复重试”把模型原始文本重新丢给它附加一句“请只输出合法 JSON”。这个笨办法在实际项目中救了我很多次。如果重试还是失败就降级返回固定错误信息而不是把异常直接抛给前端。这种兜底逻辑应该写进公共的 API 封装里而不是每个业务方法单独处理。6. 多模态扩展文生图、语音合成、私域 RAG6.1 文生图几行代码生成图片Spring AI Alibaba 对图片生成也做了封装。注入 ImageModel 后直接调用ImageResponse response imageModel.call( new ImagePrompt(一只戴着头盔骑公路车的橘猫插画风格) ); String imageUrl response.getResult().getOutput().getUrl();生成的图片 URL 可以直接返回给前端。如果希望返回 Base64 而不是 URL可以在请求参数里设置响应格式。我第一次跑通的时候挺感慨过去想接这类能力得读一整套图片服务商文档现在代码量几乎可以忽略。6.2 语音合成让应用开口说话语音合成的用法和图片生成对称。注入 AudioSpeechModel 后传入文本和语音模型参数返回音频字节流SpeechPrompt prompt new SpeechPrompt( 欢迎使用 Spring AI Alibaba已经可以播放语音了, SpeechOptions.builder() .withModel(cosyvoice-v1) .build() ); SpeechResponse response audioSpeechModel.call(prompt); byte[] audio response.getResult().getOutput().getAudio();拿到字节流后可以写文件、走对象存储、或直接作为接口响应返回给前端播放。我在做语音播报类功能时用这个接口替代了原本的人工录制后续维护提示文案的改动成本大幅降低。6.3 RAG 私域知识库给模型“补课”大模型的知识截止时间有限私域资料它完全不了解。RAG 的思路是先把你的文档拆成小块、转成向量存起来用户提问时先去向量库检索最相关的片段再把这些片段和问题一起交给模型组织答案。这样回答就有了事实依据。一个最小可用的流程如下// 1. 构建文档并切分 Document doc new Document(产品使用手册的正文内容……); var splitter new TokenTextSplitter(); ListDocument chunks splitter.apply(List.of(doc)); // 2. 向量化入库 vectorStore.add(chunks); // 3. 创建检索增强顾问 var advisor new QuestionAnswerAdvisor(vectorStore); // 4. 带知识库回答 String answer chatClient.prompt(这个产品支持导出哪些格式) .advisors(advisor) .call() .content();实际生产环境文档可能来自 PDF、Word、数据库需要先用对应的 DocumentReader 读取再处理切分、Embedding 和入库。这部分代码量并不大真正花时间的是清洗数据和调整切分粒度。切得太碎语义容易丢失切得太长检索命中率下降。我通常按 300 到 500 Token 切分再留一点重叠。6.4 向量库选型的取舍开发调试阶段用内存向量库最方便重启即清空不会污染数据。等到要上生产再根据团队已有设施选有 Redis 就用 Redis 向量检索有专门向量库就接向量库。Spring AI Alibaba 支持几类常见方案选型时可以优先考虑运维成本最低的那个不要为了“技术先进”单独引入一个没人会维护的组件。7. 常见问题与排查实录我踩过的那些坑7.1 版本冲突导致的 NoSuchMethodError这个问题出现频率排第一。现象是启动正常一调用就抛 NoSuchMethodError 或 ClassNotFoundError指向的类名又是 Spring AI 核心包里的。原因是项目中 Spring AI 相关依赖的版本被某个传递依赖覆盖了和 Spring AI Alibaba 内置版本不一致。排查办法很简单先用 Maven 依赖树看版本mvn dependency:tree -Dincludesorg.springframework.ai重点看 spring-ai-core、spring-ai-model 的版本。如果同时出现多个不同版本就检查有没有别的库间接引入了 spring-ai-bom或者手动强行指定了旧版本。删掉重复引入统一由 spring-ai-alibaba-bom 管理版本通常能解决。7.2 401 Unauthorized 与密钥不生效这类问题的表象都一样请求被服务端拒绝。常见原因有两个。第一个是 api-key 压根没读进去。如果你用了环境变量引用 ${DASHSCOPE_API_KEY}确认操作系统环境变量确实存在别只在 IDE 里配了而部署环境没配。第二个是新版本配置前缀变了你写的 spring.ai.dashscope.api-key 没被识别密钥实际为空。这时可以在启动类里临时打印一下属性值确认。还有一个易错点如果密钥的前后残留空格有些服务端不会自动 trim直接在 yml 里复制粘贴时最容易带进去。用代码生成密钥再粘贴能避开这个问题。7.3 超时与限流生产环境必调参数上线后你会发现本地好好的生产环境请求偶尔超时。默认超时时间对复杂模型推理来说可能不够尤其是调用 qwen-max 这类大模型长文本生成的耗时会长不少。Spring AI Alibaba 提供了超时配置项spring: ai: dashscope: api-key: ${DASHSCOPE_API_KEY} connect-timeout: 30s read-timeout: 60sconnect-timeout 管建连read-timeout 管等待响应。长问答场景建议 read-timeout 拉长到 60 秒或更久否则模型还没生成完客户端就先超时断开了。限流方面服务商会返回限流错误码这时单纯加超时没用要在业务层做重试退避指数退避是个稳妥方案。7.4 流式输出中断与结果为空流式输出中断很多情况不是服务端问题而是中间网络代理对 SSE 连接做了缓冲或超时断开。如果你用了 Nginx 这类反向代理需要关闭对 SSE 的缓冲否则前端收不到增量内容只能等所有内容生成完才一次性收到。结果为空的情况先查系统提示词是不是要求模型“如果不知道就不要回答”再看是不是命中了内容安全过滤模型直接输出了空回复。最后再考虑参数问题。诊断顺序从外到内不建议一上来就改 temperature。7.5 多轮记忆丢失明明配置了 ChatMemory 并跑通了示例第二天业务测试时发现上下文还是丢失。最常见的原因是 ChatClient 构建位置不对每次请求都 new 了一个 ChatClient而记忆绑定在 ChatClient 内部相当于每轮对话都是新会话。记住ChatMemory 和 ChatClient 都应该是长期存在的 Bean不是请求级别的临时对象。另外一个容易被忽略的点是异步线程。如果你在多线程环境下调用同一 ChatClient要注意并发修改会话历史的线程安全问题必要时按会话维度加锁或改用支持并发的记忆实现。最后给你点实操建议做第一个 Demo 的时候别一上来就搞流式加函数调用加知识库全家桶先用同步调用把配置、版本、密钥这三关打通再逐步叠加。我见过有人折腾半天最后发现仅仅是版本问题导致所有高级特性全废这种排查成本特别高。还有一个小技巧启动参数加上 --debug 可以打出自动配置的详细决策过程哪条配置被加载、哪条被忽略一目了然。Spring Boot 的机制配合合理的配置覆盖能解决绝大多数“为什么我的配置没生效”的疑问。我自己最大的体会是接入大模型这件事正在从“AI工程师的专属活”变成“普通后端工程师的日常活”。Spring AI Alibaba 降低的不仅是编码量更是整个团队尝试新技术的心理门槛。希望这份教程能让你少走几步弯路。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询