恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
Spring AI Alibaba实战:用Java快速构建通义千问AI应用
首页
资讯中心
/
Spring AI Alibaba实战:用Java快速构建通义千问AI应用
Spring AI Alibaba实战:用Java快速构建通义千问AI应用
发布时间:2026/9/20 3:29:51
做后端这么多年我有个特别明显的感受Java圈子上手大模型应用的速度始终比Python圈子慢半拍。不是Java不行是那时候合适的落地框架太少。直到Spring AI正式进入Spring家族阿里又在它上面开源了Spring AI Alibaba这个局面才算真正被打破。如果你是一个习惯了Spring Boot写业务的老Java想用通义千问这类大模型快速搭出AI接口但又不想从零啃Python的请求封装、会话管理、流式输出那Spring AI Alibaba就是为你准备的。这篇教程我会从实际开发者的视角来写不空谈概念。先把它和底层模型服务的关系捋清楚然后带你把第一个对话接口跑通接着用真实代码演示流式输出、多轮对话、函数调用和结构化输出最后顺手解决几个我踩过的高频坑。整个教程读完你应该能独立把一个基于通义千问的Java AI应用拆出来还能知道进展到生产环境时要注意什么。1. Spring AI Alibaba是什么为什么值得上手1.1 它和Spring AI、阿里云模型服务到底什么关系很多第一次接触的人会被三个名词绕晕Spring AI、Spring AI Alibaba、DashScope阿里云百炼。我用一句大白话解释Spring AI是规范Spring AI Alibaba是阿里对这套规范的具体实现和增强DashScope是底层真正跑模型的服务。Spring AI不是阿里搞的它是Spring生态官方的AI集成项目目标是把大模型能力抽象成Spring风格API。你只要写ChatClient对着接口调用它帮你封装了剩下百分之七八十的工程逻辑比如HTTP通信、消息组装、流式解析、Token统计。但它本身不绑定任何一家模型厂商。你既可以用OpenAI也可以用通义千问只需要换一个starter依赖和配置。Spring AI Alibaba就是在这个抽象层之下做的国内落地方案。它把DashScope上的通义千问、通义万相、嵌入模型等接进了Spring AI的模型体系。换句话说你过去用Spring AI的API现在不用自己写一堆兼容层直接用阿里提供的starter内部自动转成DashScope的API请求。如果你所在的公司又要求部署在阿里云上那就更省事认证、网络、生态都顺理成章。还有一层关系很重要Spring AI Alibaba不只是一个对接DashScope的驱动。它还涉及模型网关、函数调用、向量数据库、Agent编排等扩展能力。虽然基础教程我们主要讲对话和工具调用但你要知道这个项目的前景不只是聊天。1.2 核心概念速览花十分钟建立框架在写代码前我先带你过一遍会用到的核心概念。这些概念你会反复碰到提前建立印象后面看代码会快很多。ChatClient面向用户的主入口。类似于Spring Web里的RestTemplate你用它发起Prompt接收模型的回复。ChatModel底层模型调用的抽象接口。ChatClient内部会持有ChatModel由starter自动装配。Prompt你发给模型的完整请求包含用户消息、系统消息、模型参数选项。Message一条消息。常见的有UserMessage、SystemMessage、AssistantMessage。Tool Calling函数调用允许模型在回复时携带一个结构化请求调用你提前注册好的Java函数。Structured Output结构化输出让模型按JSON Schema返回结果并自动反序列化成Java对象。Vector Store / Embedding向量化模型与向量数据库相关在RAG场景使用基础教程先不展开。我见过不少人一上来就去看Agent、RAG这类高级功能结果基础Prompt都调不稳最后全变成玄学调参。我的建议是先把握ChatClient、Message、Tool Calling这三件事把对话链路跑通再往上层走。2. 环境准备10分钟跑通第一个对话接口2.1 版本、依赖和密钥准备本机环境我用的是JDK 17、Maven 3.9、Spring Boot 3.2.5。Spring AI Alibaba目前对JDK 17的要求比较常见如果你还在用JDK 8建议先别挣扎直接升级。做AI应用会大量处理字符串和JSON新版JDK在性能、虚拟线程方面都有天然优势。依赖引入有两种方式。第一种是直接引用Spring AI Alibaba自家的BOM统一管理版本dependencyManagement dependencies dependency groupIdcom.alibaba.cloud.ai/groupId artifactIdspring-ai-alibaba-bom/artifactId version1.0.0-M2/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement然后引入starterdependency groupIdcom.alibaba.cloud.ai/groupId artifactIdspring-ai-alibaba-starter/artifactId /dependency第二种是直接用Spring Initializr生成项目时在AI分类里选DashScope相关依赖。如果你不会配就首选第一种清晰直接。接下来去阿里云百炼控制台开通DashScope服务创建一个API Key。把API Key保存好建议直接通过环境变量注入不要硬编码进代码仓库。后面配置里我会用${DASHSCOPE_API_KEY}这种占位符。注意买模型前先确认企业账号是不是走专属资源池如果走标准API默认的通义千问qwen-plus对绝大多数场景够用不一定要上最高规格的模型。2.2 配置文件与最小Demo在application.yml里做基础配置spring: application: name: spring-ai-alibaba-demo ai: dashscope: api-key: ${DASHSCOPE_API_KEY} chat: options: model: qwen-plus temperature: 0.7这里面的spring.ai.dashscope是Spring AI Alibaba的自动配置前缀。chat.options.model是默认对话模型temperature控制答案随机性。如果你要更保守的结果可以调低到0.2如果做创意文案再往0.8以上走。接着建一个Controller最简实现RestController public class ChatController { private final ChatClient chatClient; public ChatController(ChatClient.Builder builder) { this.chatClient builder.builder() .defaultSystem(你是一个Java开发助手回答尽量简洁、准确。) .build(); } GetMapping(/chat) public String chat(String message) { return chatClient.prompt(message).call().content(); } }启动项目访问http://localhost:8080/chat?message用一句话介绍Spring AI Alibaba如果配置没问题几秒钟内就能收到模型回复。走到这一步你的第一个大模型接口就跑通了。这里有个特别爽的点你完全没手写HTTP请求、没拼接JSON、没处理鉴权头全部由starter自动搞定。这背后就是Spring Boot的AutoConfiguration功劳。你写的代码只有业务部分非常干净。2.3 为什么用ChatClient而不是直接注入ChatModel初学者常看到一个ChatModel的Bean就想直接chatModel.call(prompt)。不是不行但我不推荐。ChatModel属于底层API你得自己处理Prompt选项、消息转换、结果提取代码能写但很难维护。ChatClient更像一个友好的门面对象把常见的动作压缩成链式调用。你可以先不用理解它内部所有方法把它当作一个能发消息并能拿到回复的客户端”来看待。我自己的习惯是在Service层注入ChatClientController层只负责参数校验和结果返回。这样后面如果要加多轮记忆、Tracing不需要改Controller。3. 核心功能实战从单轮问答到流式输出3.1 ChatClient的三种调用方式ChatClient最基础的用法是调用.call()它代表同步等待模型把整段回答返回。适合后端逻辑需要完整答案后再做处理的场景比如内容提取、数据分析、生成摘要。下面这段代码演示了带系统消息和参数的调用String result chatClient.prompt() .system(你是专业客服回答语气要礼貌。) .user(我有一个问题...) .temperature(0.5) .call() .content();需要注意的是temperature不仅是随机参数也影响模型输出的稳定性。如果你在做一个规则性很强的任务比如抽取JSON建议把它调到贴近0。如果是头脑风暴再调高。第二种是.stream()用于流式输出。模型每生成一段内容后端就通过响应式流把你推给前端。聊天场景几乎必须用这个用户不用等十几秒才有反应。GetMapping(value /chat/stream, produces text/plain; charsetUTF-8) public FluxString chatStream(String message) { return chatClient.prompt(message).stream().content(); }启动后直接访问这个接口在浏览器里你能看到文字像打字机一样一段段出现。前端如果是React直接接一个EventSource解析文本流很方便。第三种是.chat()? 严格来说Spring AI早期版本有该方法新版本已经统一收敛到call()和stream()。如果你在网上看到老代码建议直接按你当前依赖版本来。API改动是Java AI框架的常态依赖版本不同很多方法都不一样遇到编译错误先查版本。3.2 流式输出背后的原理以及前端配合方式很多人用流式接口时会遇到一个问题返回的数据不是标准JSON而是每行一段文本。这不是Bug是SSEServer-Sent Events的典型格式。我举个例子前端用axios直接请求可能需要加几行代码处理text/event-stream格式。你如果用的是服务端渲染的Thymeleaf或WebSocket也可以把FluxString通过WebSocket转发体验更好。更简单的方法是用SseEmitterGetMapping(/chat/sse) public SseEmitter chatSse(String message) { SseEmitter emitter new SseEmitter(0L); FluxString content chatClient.prompt(message).stream().content(); content.subscribe( data - emitter.send(data), emitter::completeWithError, emitter::complete ); return emitter; }如果你只想快速做原型直接用.stream()配text/event-stream就足够。我的经验是生产项目不要在前端绕太多层最好的方案是后端做SSE网关前端只管收消息后端的模型切换、负载、鉴权全部挡在网关后面。3.3 多轮对话别傻傻手工拼接历史模型本身没有记忆。你每发出一轮请求它看到的就是你这次发过去的内容。要实现多轮对话核心思路是把历史消息一并发给模型。最原始的方式是手动维护一个ListListMessage messages new ArrayList(); messages.add(new SystemMessage(你是一个AI助手)); messages.add(new UserMessage(第一句)); messages.add(new AssistantMessage(第一句回复)); messages.add(new UserMessage(第二句));把全部消息塞进Prompt丢给模型。这种方式在小Demo里能跑但一旦用户多了消息管理、Token超限、上下文裁剪全成问题。Spring AI里面提供了ChatMemory和Advisor机制你可以把它理解成给ChatClient装上一块记忆插件。最简单的方式是使用MessageChatMemoryAdvisor它会把会话历史自动附加到下一次请求中同时不会让你的业务代码里出现一堆List。ChatClient chatClient ChatClient.builder(chatModel) .defaultAdvisors(new MessageChatMemoryAdvisor(new InMemoryChatMemory())) .build();然后你在请求时指定会话IDString answer chatClient.prompt() .user(你还记得我之前问你什么吗) .advisors(a - a.param(ChatMemoryAdvisor.CONVERSATION_ID, session-001)) .call() .content();这样同一个会话ID的消息会自动串联起来。要注意的是内存版InMemoryChatMemory只适合开发环境生产环境建议换成Redis实现否则实例重启对话就没了而且多实例部署时会话会分散。4. 让模型真正干活Function Calling与结构化输出4.1 函数调用从“聊天玩具”走向业务系统聊到第四部分我觉得是整个教程的分水岭。纯聊天好写难的是让AI真正触达你的业务数据。比如用户问北京现在热吗如果你只让模型自由发挥它可能会瞎编温度。正确做法是让模型识别用户意图然后调用你提供的一个天气函数拿到真实数据后再组织回答。这个机制在Spring AI里叫Tool Calling在阿里这套实现里同样支持。我使用的方式是注册一个Function类型的Bean再用Description告诉模型这个函数是干什么用的。Bean Description(根据城市名获取当前天气参数格式: {\city\: \北京\}) public FunctionWeatherRequest, WeatherResponse currentWeather() { return new WeatherFunction(); }WeatherFunction内部你直接调用气象服务API返回一个结构清晰的对象public class WeatherFunction implements FunctionWeatherRequest, WeatherResponse { Override public WeatherResponse apply(WeatherRequest request) { // 调用真实天气接口这里省略实现 return new WeatherResponse(request.city(), 28, 晴); } }在调用ChatClient时通过.functions(currentWeather)把这个工具挂上去String answer chatClient.prompt() .system(你是天气助手回答用户问题前先调用工具。) .user(北京现在热吗) .functions(currentWeather) .call() .content();模型看到北京这个实体时会认为需要调用currentWeather于是返回一个特殊消息。Spring AI框架会自动帮你执行这个Java函数再把结果回传给模型最后由模型生成自然语言回答。整个过程对业务代码是透明的你只需要保证函数描述足够清楚尤其写清楚参数结构。函数返回结果不要带无关字段模型会依据返回内容生成回答。不要暴露任何敏感数据和危险操作函数AI调用的入口和用户输入是同一路径要当接口对待。我踩过的坑是参数名用了中文或命名不够直观模型经常生成不了正确的JSON。后来我统一用英文参数名并在Description里给一个示例JSON效果立刻好了很多。这是工具调用最实用的小技巧。4.2 结构化输出让模型返回可以直接入库的JSON传统开发中我们通常需要从模型回答里提取实体、情感、分类标签。如果靠正则去解析大段文本很容易碎。Spring AI提供了结构化输出你可以直接让模型返回Vo对象。比如我有一个用户信息类public record UserInfo(String name, Integer age, String city) {}然后调用UserInfo user chatClient.prompt() .user(从这段话里提取用户信息我叫张伟今年28岁家在杭州。) .call() .entity(UserInfo.class);最终user.name()是张伟user.age()是28user.city()是杭州。它会自动完成从自然语言到Java对象的映射不需要你手写JSON解析。这套能力背后的逻辑是框架根据你传入的Java类型自动生成JSON Schema模型按这个Schema输出框架再把它反序列化。所以你需要注意业务字段尽量用基本类型避免复杂泛型嵌套。如果字段允许为空建议使用包装类型或Optional防止反序列化NPE。模型输出偶尔会不遵循Schema生产代码里要做异常兜底把失败的回答降级为人工处理。还有一点非常实用你可以用ParameterizedTypeReference处理列表结构比如一批新闻标题的提取。这个API我用过几次在处理Excel导入、信息清洗场景时特别能提效。5. 生产环境避坑指南5.1 常见问题排查表我整理了一张速查表都是平时聊过最多的几类问题现象大概率原因解决办法401 UnauthorizedAPI Key无效或没走环境变量检查DASHSCOPE_API_KEY重启应用确认配置生效404 model not exists当前账号未开通对应模型去百炼控制台开通模型权限或换用qwen-plus等默认模型接口响应很慢同步等待完整输出改用流式接口必要时调低maxTokens偶发超时模型队列繁忙或网络抖动增加重试机制设置合理超时时间多轮对话串上下文未用会话ID隔离使用ChatMemoryAdvisor并传唯一会话ID返回JSON格式错乱温度太高或Schema约束不足temperature调低使用结构化输出生产环境内存暴涨上下文无限累积给会话历史设置窗口上限定期清理排查的时候有个技巧先确认是不是网络层问题再确认是不是配置问题最后才怀疑模型问题。不要一上来就调temperature那只会让结果更玄学。5.2 模型选型与量化建议聊天原型阶段用qwen-plus是最省心的速度快、效果均衡。如果你的任务偏专业要求高可以试试qwen-max但成本会上升。如果做短文本分类、向量化、标题生成也可以考虑qwen-turbo延迟低便宜很多。我建议在应用里把模型名做成配置项不要写死在代码。通过spring.ai.dashscope.chat.options.model去配这样上测试环境用turbo生产环境用max一个配置就能切换。对于嵌入模型RAG场景里我会用text-embedding-v3维度适中效果还不错。不过如果你已经开始做RAG建议同时选好向量数据库。Spring AI Alibaba生态里对Milvus、PostgreSQL等都有对接但我一般小白阶段只推荐先用本地SimpleVectorStore把流程跑通再说。5.3 Spring AI Alibaba Admin像运维一样管理AI应用聊到热词Spring AI Alibaba Admin有人可能以为有个独立后台管理系统。其实在官方生态里admin更多强调的是一套可观测和可管控能力。你不需要一上来就搭一个复杂的运营后台但至少要从应用侧把模型调用看住。我自己的做法分三层第一层暴露Actuator端点监控应用存活和基础指标。只要引入dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-actuator/artifactId /dependency然后配置management.endpoints.web.exposure.include*Spring AI Alibaba的自动配置会带上模型调用相关的指标Prometheus可以直接抓。第二层在代码里统一记录Token消耗。你拿到ChatResponse后从response.getMetadata().getUsage()拿到输入Token、输出Token入库。时间长了你就知道哪个部门、哪个应用在烧钱。这部分我通常会做成一个切面不侵入业务代码。第三层做一个轻量Admin接口。真正到团队协作时你需要一套页面去维护模型路由和API Key这就是我理解的Spring AI Alibaba Admin实践方向。不需要很强能支持以下功能就行查看每个应用的调用次数、Token消耗和失败率。在线修改指定业务的模型名和temperature。建立团队维度的预算告警。根据我的经验一个小团队自己写个几十行代码的管理接口完全够用。重点不是管理界面做得多花哨而是要有数据有告警出了问题能快速定位。5.4 把基础教程变成生产落地的心得最后分享一点我的判断。Spring AI Alibaba目前的迭代节奏相当快API还在不断演进。你在网上搜到的代码可能一个月后就过时所以学习它的核心不是背API而是抓住三层思维模型第一层用ChatClient和Prompt操作模型替代自己写HTTP接口。第二层用Function Calling把AI接进现有业务系统让模型学会调用你的能力。第三层用Token监控、模型配置和异常兜底把AI应用当作正式业务系统来运营。我个人实际做项目时会在项目里写一个AiAssistantService把所有ChatClient调用集中在一个类不散落各处。这样以后要从qwen-plus换到别的模型或者加上RAG、Agent只需要在这一个类上做扩展其他代码不动。如果你正要上手建议先别追求高大上的Agent框架老老实实把今天讲的几个点都练一遍。你会发现从“调通接口”到“能落地到业务系统”其实就差一次函数调用和结构化输出的门槛。迈过去路就顺了。