☰
Spring AI Alibaba实战指南:Java开发者的通义千问接入与生产落地
2026/10/20 5:11:11 网站建设 项目流程

Spring AI Alibaba 这段时间在 Java 圈子里讨论度确实高,尤其 1.0.0 正式版发布之后,很多原本在观望的人开始往项目里引。我自己的感觉是,这东西最大的价值不是“又多了一个 AI 框架”,而是把大模型接入这件事,真正拉回到了 Java 开发者熟悉的轨道上。你不用去啃 Python 的那套 AI 生态,也不用折腾 HTTP 调用、JSON 拼装、鉴权签名这些琐碎事,Spring 那一套依赖注入、自动配置、模板抽象,直接平移过来就能用。

这篇文章我不打算写成官方文档的复述版,而是按照我自己从零接入、跑通、踩坑、优化这条线来整理。内容覆盖基础概念、快速上手、核心功能拆解、工程化落地的思路,以及我在实际项目中遇到过的问题和解决办法。无论你是刚接触 Spring AI,还是已经在用但想深入一点,应该都能从里面找到有用的东西。

1. 项目初印象:Spring AI Alibaba 到底解决了什么问题

1.1 Spring AI 生态里它处在什么位置

要说清楚 Spring AI Alibaba,得先理解 Spring AI 本身。Spring AI 是 Spring 社区推出的 AI 应用开发框架,目标是把大语言模型的接入做成像 Spring JDBC 操作数据库那样标准化。它定义了一套统一接口,对话模型、嵌入模型、向量存储、结构化输出这些能力都有对应的抽象层。开发者写业务代码时,不直接面对某个厂商的 SDK,而是面向 Spring AI 的接口编程,底层换哪家模型,改动成本被压得很低。

Spring AI Alibaba 就是这套体系里的“国产模型适配层”。它基于 Spring AI 的 SPI 机制,把阿里系的大模型服务封装成了 Spring Boot 的 Starter。你引入依赖、配置几个参数,就能在项目里用通义千问系列模型,比如 qwen-plus、qwen-turbo,以及配套的文本嵌入模型。更关键的是,它还带了一个非常重要的能力:把 Spring Cloud Alibaba 的生态整合进来。这意味着 AI 能力在微服务架构里不是孤岛,而是可以作为像注册中心、配置中心、网关那样的基础设施去使用。

我最初接触这个项目是在它 0.x 版本时期,那时候功能还不完整,文档也少。等到 1.0.0 发布,API 稳定了,自动配置也完善了,才真正适合拿到生产环境里用。

1.2 为什么需要 Alibaba 这个分支

有人可能会问,Spring AI 官方不是已经支持 OpenAI、Azure 这些了吗,直接用不行吗?理论上行,但在实际业务场景里,问题不少。

第一,模型服务的可访问性。海外模型的 API 服务在国内网络环境下调用延迟很高,而且不稳定。就算能用,面向国内用户的产品也不适合把请求打到海外服务上,合规和数据安全层面都有隐患。走国产模型服务,请求链路完全在境内,延迟和稳定性都可控。

第二,生态整合的深度。Spring AI 官方对阿里系模型的支持是“通用适配”,而 Spring AI Alibaba 是“深度适配”。举个例子,它在自动配置里内置了通义千问的模型列表,你配置 model 名称时直接写 qwen-plus 就行,不用查文档去记那些内部 ID。又比如,它对流式输出的处理做了优化,适配了 DashScope 的流式协议格式,你拿到的事件流是已经被解析好的对象,而不是原始报文。

第三,也是很多人容易忽略的:Alibaba 分支不是简单绑定阿里云。它的设计里留有接入其他模型服务商的扩展点,也就是说你可以用 Spring AI Alibaba 的抽象,去接任意一个兼容 OpenAI 协议的模型服务,包括自建的私有化模型。这一点在后面的自定义接入部分我会详细展开。

2. 快速上手:五分钟搭一个能聊天的 Spring Boot 服务

2.1 工程准备与依赖引入

我用的是 Spring Boot 3.2.5 + Spring AI Alibaba 1.0.0 的组合,JDK 建议 17 以上。Spring AI 这个框架对 Spring Boot 版本有要求,太老的版本会有兼容问题,所以直接上 3.x 是最省事的。

创建工程可以用 Spring Initializr,选 Java 17,依赖先只加 Spring Web。然后手动往 pom.xml 里加 Spring AI Alibaba 的依赖:

<dependency> <groupId>com.alibaba.cloud.ai</groupId> <artifactId>spring-ai-alibaba-starter</artifactId> <version>1.0.0</version> </dependency>

这里有个细节:Spring AI Alibaba 的 groupId 是 com.alibaba.cloud.ai,不是 org.springframework.ai。很多人第一次找依赖会找错,去 Spring 官方仓库翻了半天没看到,实际上它在 Maven 中央仓库的路径跟 Spring Cloud Alibaba 那套是并列的。

如果你用的是 Gradle,对应写法:

implementation 'com.alibaba.cloud.ai:spring-ai-alibaba-starter:1.0.0'

2.2 配置中心:密钥与模型参数怎么填

依赖引进来之后,接下来就是配置。在 application.yml 里加:

spring: ai: dashscope: api-key: ${DASHSCOPE_API_KEY} base-url: https://dashscope.aliyuncs.com/api/v1

这里我要多说一句:api-key 一定不要硬编码在配置文件里,用环境变量或者配置中心去管理。我自己见过好几个项目,密钥写在 application.yml 里直接提交到 Git 仓库,这是特别危险的习惯。DashScope 的密钥泄露意味着别人能用你的额度去调模型,账单会让你欲哭无泪。

然后配置对话模型:

spring: ai: dashscope: chat: options: model: qwen-plus temperature: 0.8

temperature 这个参数控制随机性,范围一般是 0 到 1。做创意写作、文案生成可以调高一点,比如 0.8;做信息抽取、分类、代码生成这类确定性要求高的任务,调到 0.2 以下更合适。

配置好之后,Spring 容器里会自动注册好 ChatModel、EmbeddingModel 这些 Bean,你不需要自己 new。

2.3 编写第一个对话接口

接下来写一个最小可用的接口来验证整个链路通不通:

@RestController @RequestMapping("/api/chat") public class ChatController { private final ChatModel chatModel; public ChatController(ChatModel chatModel) { this.chatModel = chatModel; } @PostMapping public String chat(@RequestBody String prompt) { return chatModel.call(prompt); } }

ChatModel 的 call 方法是同步调用,传入字符串直接返回模型生成的文本。启动服务,用 curl 试一下:

curl -X POST http://localhost:8080/api/chat \ -H "Content-Type: application/json" \ -d '"请用一句话介绍你自己"'

返回结果就是通义千问的输出文本。整个链路跑通的那一刻,你就能感受到这套设计的直接:没有复杂的 SDK 初始化,没有手工管理 HTTP 连接,Spring 的自动配置把所有脏活都干完了。

2.4 流式输出改造

同步调用在演示的时候可以,生产环境几乎都要用流式输出。原因很简单:大模型生成文本是按 token 来的,整段内容可能要好几秒甚至十几秒才生成完。如果同步等着,用户看到的就是一个长时间转圈的页面,体验很差。流式输出让用户看到内容一个字一个字地出来,体验完全不同。

Spring AI Alibaba 对流式的支持很到位。改造起来也不复杂:

@PostMapping(value = "/stream", produces = "text/plain;charset=UTF-8") public Flux<String> stream(@RequestBody String prompt) { return chatModel.stream(prompt); }

返回类型从 String 改成 Flux ,Spring WebFlux 会以 SSE(Server-Sent Events)的方式把内容推给前端。前端用 fetch 或者 EventSource 就能逐段接收。

这里有一个我踩过的坑:如果 produces 没有指定 text/event-stream,浏览器端可能会把整个响应当作普通文本一次性接收,完全看不到流式效果。正确的写法是:

@PostMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux<String> stream(@RequestBody String prompt) { return chatModel.stream(prompt); }

前端接 SSE 也简单:

const res = await fetch('/api/chat/stream', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(prompt) }); const reader = res.body.getReader(); const decoder = new TextDecoder(); while (true) { const { done, value } = await reader.read(); if (done) break; const text = decoder.decode(value); console.log(text); // 逐段处理 }

3. 进阶功能拆解:向量库、结构化输出与多模态

3.1 接入文本向量模型做知识库

搭好对话基础之后,大多数人下一步就会想:怎么让模型回答我自己的私有数据。这就要用到 RAG(检索增强生成)。RAG 的流程是:把文档切块,每块转成向量存进向量数据库,用户提问时把问题也转成向量,在库里做相似度检索,找到最相关的几块内容,和问题一起拼进提示词发给模型。

Spring AI Alibaba 里,嵌入模型这块也是开箱即用的:

spring: ai: dashscope: embedding: options: model: text-embedding-v3

注入 EmbeddingModel,调用 embed 方法就能拿到向量:

@Autowired private EmbeddingModel embeddingModel; public float[] embed(String text) { return embeddingModel.embed(text); }

不过实际做 RAG 的时候,很少有人手动调 EmbeddingModel 去管理向量,更多是直接用 Spring AI 的向量存储抽象。框架支持 Redis、Milvus、PGVector 等,通过 VectorStore 接口统一操作。

@Autowired private VectorStore vectorStore; // 写入 vectorStore.add(List.of(new Document("Spring AI Alibaba 使用教程..."))); // 相似度检索 var results = vectorStore.similaritySearch( SearchRequest.builder().query("什么是Spring AI Alibaba").topK(3).build() );

在这一步我特别想提醒一件事:向量化只是知识库的一半,文本切块的质量直接影响检索效果。切得太碎,语义信息不完整;切得太大,检索到的内容噪音太多,还容易超出模型输入窗口。我自己的经验是,中文技术文档按 500-800 字的块切,重叠 80-100 字,效果相对稳定。切块这些问题框架不会替你解决,得自己根据业务场景去调。

3.2 用 OutputParser 拿结构化 JSON

很多业务场景需要的不是一段流畅的自然语言,而是规范化、结构化的数据。比如让模型从客户反馈里抽取“产品名称、问题类型、紧急程度”,或者把一段会议纪要整理成“待办事项列表”。这时候 OutputParser 就派上用场了。

Spring AI 里有个 BeanOutputConverter,可以把模型输出自动映射到 Java 对象上。

先定义一个 POJO:

public record TodoItem(String task, String owner, int priority) {}

然后通过 PromptTemplate 配合 OutputConverter 让模型按 JSON 格式返回:

var converter = new BeanOutputConverter<>(new TypeReference<List<TodoItem>>() {}); var promptTemplate = new PromptTemplate(""" 请将下面的会议纪要整理为待办事项列表。 每个待办需包含:任务描述(task)、负责人(owner)、优先级(priority,1-5整数)。 会议纪要:{meetingNotes} """);

构建 Prompt 时把 converter.getFormat() 拼到模板里,模型就会严格按 JSON 结构返回:

var prompt = promptTemplate.create( Map.of("meetingNotes", notes, "format", converter.getFormat())); var response = chatModel.call(prompt); var todos = converter.convert(response.getResult().getOutput().getContent());

这个方法对比手工调 JSON 的好处是:框架把“让模型输出合法 JSON”这件事标准化了,而且格式描述是动态生成的,模型返回时基本不会串格式。我在项目里用这个方法解析发票信息、抽取工单字段,准确性比让模型自由发挥高很多。

3.3 多模态输入:图像理解

通义千问的 qwen-vl-max 系列支持图像输入。Spring AI Alibaba 里通过 Message 体系来支持,给 Prompt 传入用户消息和媒体内容,模型就能根据图回答问题。

var userMessage = new UserMessage("描述一下这张图片的内容", List.of(new Media("image/png", urlBytes))); var response = chatModel.call(new Prompt(userMessage));

Media 可以传 URL 地址或者二进制字节流。我在一个实际需求里用它做商品图片审核,让模型判断图片中的商品是否与描述一致,准确率能顶上一个简单的视觉模型服务。

多模态这里要注意:带图片的请求消耗的输入 token 比纯文本大得多,成本会明显上升。做正式项目前,先拿几组典型样本算一下单次请求的成本,再决定这个方案值不值得。别等月底账单来了才醒悟。

4. 工程落地:生产环境下怎么用

4.1 与 Spring Cloud Alibaba 生态组合

单独一个服务调用大模型不算难,真正考验技术方案的是在微服务架构里怎么把 AI 能力组织好。Spring AI Alibaba 跟 Spring Cloud Alibaba 生态天然契合,可以组合出比较优雅的架构。

我推荐的做法是把 AI 能力封装成一个独立的“AI 服务”,通过 Nacos 注册到微服务网关后面。业务服务不直接依赖 Spring AI Alibaba,而是通过 OpenFeign 或 HTTP 调用 AI 服务的接口。这样做的原因有几个:

模型配置集中管理。密钥、模型名、温度这些参数只维护在 AI 服务一处,业务服务不需要关心底层用的什么模型。

流量控制更清晰。AI 服务作为独立实例,可以根据调用量单独扩缩容,不会把算力压力扩散到整个业务集群。

Token 成本可审计。所有请求都经过 AI 服务,日志里能记录每个业务方调用了多少 token,核算成本的时候心里有底。

配置上,AI 服务跟普通 Spring Cloud Alibaba 服务没什么区别:

spring: cloud: nacos: discovery: server-addr: ${NACOS_SERVER_ADDR} ai: dashscope: api-key: ${DASHSCOPE_API_KEY}

4.2 自定义接入其他模型服务商

Spring AI Alibaba 虽然默认对接 DashScope,但它并不封闭。如果你想接一个自建的模型服务,或者换成其他厂商的 OpenAI 兼容接口,可以通过自定义 ChatModel 的 Bean 来实现。

方法很简单:实现 Spring AI 的 ChatModel 接口,或者继承框架自带的 OpenAiChatModel,然后把 base-url 指向你自己的服务地址:

@Bean public ChatModel customChatModel() { var config = OpenAiApi.builder() .baseUrl("http://your-model-server:8000/v1") .apiKey("empty") // 自建服务通常不需要鉴权 .build(); return new OpenAiChatModel(config); }

这样做的价值在于:你的业务代码抽象不变,但底层模型服务可以随时切换。今天用云端大模型,明天换成私有化部署的小模型,都只是改配置和切换 Bean 的问题。

我实际遇到过一种情况:项目里有一部分场景需要极低延迟,云端大模型做不到,就把一个 7B 的小模型部署在推理服务器上,通过这种方式接进来。两条模型链路并存,业务侧通过枚举去选择用哪个,用的就是这套机制。

4.3 配置管理与成本控制

聊到生产落地,就绕不开成本这个话题。大模型 API 是典型的按量付费,token 没长眼睛,楼上楼下都在烧钱。我从项目上线到现在的体会是,下面几个动作能做到显著省钱。

第一个动作是设置上下文长度上限。很多新手接入时直接让模型“记住全部对话”,结果 system prompt 加历史消息,每轮调用都要把所有历史重发一遍,token 消耗随对话轮数线性增长。合理做法是只保留最近 N 轮消息:

var messages = new ArrayList<Message>(); // 只取最近10轮对话 for (int i = Math.max(0, history.size() - 10); i < history.size(); i++) { messages.add(history.get(i)); }

第二个动作是模型分级。不是所有请求都需要 qwen-max 这个档位。简单分类、抽取任务用 qwen-turbo,创意生成、复杂推理才用 qwen-plus 或 qwen-max。我用一个配置枚举来控制业务方选模型,从命令行工具到在线问答都这么处理。

第三个动作是缓存。对于请求频繁、结果稳定不变的场景,比如商品描述的文案生成,加一层 Redis 缓存,key 用输入内容的 hash。实测下来,这类场景的缓存命中率能做到 40% 左右,省下的 token 就是纯利润。

5. 踩坑实录与排查技巧

5.1 高频报错与解决办法

使用 Spring AI Alibaba 的过程中,我遇到过的报错不算少,挑几个高频的说一下,大家可以对照自查。

报错一:HttpClientErrorException$Unauthorized: 401 Unauthorized。这通常是 api-key 配错或者过期了。检查环境变量 DASHSCOPE_API_KEY 是否设置正确,这个错误在控制台日志里非常明显,定位也容易。

报错二:Http 429 Too Many Requests。这是触发了 DashScope 的限流。解决方向有两个:提高账号的 QPS 配额,或者在前端/服务端做退避重试。我自己用了 Spring 的 RetryTemplate 来做指数退避,重试三次,间隔分别是 1 秒、2 秒、4 秒。

报错三:IllegalArgumentException: Invalid model name。这多半是 option 里的 model 名写错了,比如把 qwen-plus 写成了 qwenplus。一定要去查通义千问当前支持的模型列表,别凭记忆写。

报错四:WARN 日志里有 "no dashscope chat model found" 但项目能启动。这通常是因为你故意没有引入 DashScope 的聊天模型配置,Spring 容器里也没有自定义 ChatModel Bean。当时我用在纯向量计算的场景里,就没配 chat 模型,日志吓了我一跳,查了一圈才发现不是问题。

5.2 容易被忽略的细节坑

第一个是字符编码问题。Spring AI 返回的中文内容,如果你在响应头里没有明确 charset=UTF-8,某些 HTTP 客户端会按 ISO-8859-1 去解码,导致中文变成乱码。我建议 Response 统一设置 Content-Type 里带 charset=UTF-8。

第二个是流式输出时的内存占用。如果通过 Flux 把流透传给前端,没有做背压处理,高并发下响应式流可能会积压大量未发送数据,内存就爆了。解决方式:控制并发量,给连接加超时,或者用限流组件保护。

第三个是日志里会打印完整请求内容。我在排查问题时发现,Spring AI 的 Debug 日志会把 prompt 原文打出来,如果业务里有敏感信息,日志级别别开 Debug,或者配置日志过滤器脱敏。

第四个坑我只遇到过一次但印象深刻:温度参数设置过高导致返回内容不稳定。以前做一次分类任务时 temperature 忘调低,同一个输入在十次调用里结果都不一样。把 temperature 调到 0.1 之后,输出才稳定。这类确定性任务一定要用低温度。

5.3 性能调优建议

最后说几个性能方面的体会。

连接复用。Spring AI Alibaba 基于 WebClient 实现,默认连接池配置比较保守。在高并发场景下,可以调大 maxConnections,减少连接建立的消耗:

@Bean public DashscopeConnectionFactory dashscopeConnectionFactory() { return DashscopeConnectionFactory.builder() .maxConnections(500) .build(); }

异步化改造。同步接口在 Gateway 线程里阻塞等待模型返回,本身没问题,但如果吞吐要求高,建议全面用异步 API。Spring AI Alibaba 的 ChatModel 支持 asyncCall,配合 CompletableFuture 可以显著提升并发处理能力。

模型响应超时设置。默认超时时间在有些场景下可能过短,长文本生成时容易触发超时。调大响应超时时间可以避免误超时,但也要防着模型服务真正卡住的情况。建议设置成 60 秒,并对超时的任务做重试,以免影响用户体验。

6. 一些真心话

如果用一句话总结 Spring AI Alibaba,我觉得是“让 Java 开发者用最低的学习成本进入 AI 应用开发”。它的抽象设计确实花了心思,跟 Spring 生态的亲和度非常高。虽然现在版本还在快速迭代,但 1.0.0 之后的稳定性已经足够支撑生产项目。

我个人在实际使用中的体会是:框架的简单是表层的简单,它帮你省掉了接入的琐碎,但 AI 应用真正的难点——提示词设计、上下文管理、输出质量控制、成本控制——这些还是得靠你自己的业务理解去解决。别指望框架能替你思考业务,它只是让“把大模型接进来”这件事从能用到好用。

最后分享一个小技巧:Spring AI Alibaba 的官方示例工程很值得花时间读一遍。它不只是演示怎么调用,还展示了流式、结构化输出、向量检索这些能力在一个完整工程里是怎么组织的。我第一次读的时候收获很大,很多配置上的细节都是从这里学会的。这个项目后续的使用边界,我觉得会越来越宽,值得持续关注。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询