在大模型快速演进的今天,Java 开发者同样希望“开箱即用”地接入各类模型服务。Spring 官方推出的 Spring AI,已经为 Java / Spring Boot 应用提供了一套统一、优雅的 AI 抽象;而在国内模型生态中,如何更好地对接阿里云通义(Qwen)与灵积平台(DashScope),则是 Spring AI Alibaba 重点解决的问题。
本文基于仓库中的spring_ai_alibaba-demo子项目,从真实代码出发,带你一起拆解:如何用 Spring AI + Spring AI Alibaba 的生态,在本地通过Ollama 跑 Qwen3 模型,并逐步扩展到 RAG、工具调用和 Graph 工作流。
GitHub 项目地址:https://github.com/zhouByte-hub/java-ai/tree/main/spring_ai_alibaba-demo
欢迎 Star、Fork 和关注!文中所有代码都可以在该子项目中找到,更适合边读边跑。
面向读者:
- 已有 Spring Boot 基础,希望快速接入大模型的后端开发;
- 计划在本地或内网环境使用 Qwen3 等模型(通过 Ollama),但又希望未来平滑切到阿里云 DashScope;
- 想了解 Spring AI Alibaba 在 Graph、RAG、工具调用等场景中的作用和优势。
一、项目概览:Spring AI + Spring AI Alibaba 在这个 Demo 里的分工
spring_ai_alibaba-demo是一个多模块示例工程,核心模块包括:
- 根模块
spring_ai_alibaba-demo:- 使用Spring AI的
spring-ai-starter-model-ollama接入本地 Ollama 服务; - 使用
spring-ai-starter-vector-store-pgvector集成 PostgreSQL + PgVector 做向量检索; - 通过
ChatModel/ChatClient演示基础对话、RAG、工具调用和记忆; - 通过依赖管理引入
spring-ai-alibaba-bom,为后续接入阿里云生态(包括 DashScope、Graph 等)奠定基础。
- 使用Spring AI的
- 子模块
alibaba-graph:- 使用
spring-ai-alibaba-graph-core演示基于大模型的有状态流程(StateGraph),依然以 Ollama 的 Qwen3 作为底层模型;
- 使用
- 子模块
alibaba-mcp-server/alibaba-mcp-client:- 使用 Spring AI 的 MCP 能力演示模型调用外部工具 / 资源的模式。
换句话说:
当前 Demo没有直接连阿里云 DashScope,而是选择在本地通过Ollama 运行 Qwen3 模型;
但项目在依赖管理和结构设计上,已经完全站在Spring AI Alibaba 生态之上,随时可以切换到阿里云在线服务。
接下来,我们按“从简单到复杂”的顺序,依次看看各个模块是怎么搭建的。
二、依赖与环境:本地 Qwen3 + PgVector
先看根模块spring_ai_alibaba-demo/pom.xml中的关键部分:
<properties><!-- 项目使用的 JDK 版本 --><java.version>17</java.version><!-- Spring AI Alibaba 相关依赖统一使用的版本 --><spring-ai-alibaba.version>1.1.0.0-M5</spring-ai-alibaba.version><!-- Spring AI 核心依赖统一使用的版本 --><spring-ai.version>1.1.0</spring-ai.version></properties><dependencies><!-- 基础 Web 能力:提供 Spring MVC / 内嵌容器等 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- 通过 Spring AI 访问本地 Ollama 大模型服务 --><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-model-ollama</artifactId></dependency><!-- 向量数据库:Spring AI 对 PgVector 的封装 --><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-vector-store-pgvector</artifactId></dependency><!-- PostgreSQL JDBC 驱动,用于访问数据库 --><dependency><groupId>org.postgresql</groupId><artifactId>postgresql</artifactId></dependency></dependencies><dependencyManagement><dependencies><!-- Spring AI Alibaba 统一版本管理(国内生态相关依赖) --><dependency><groupId>com.alibaba.cloud.ai</groupId><artifactId>spring-ai-alibaba-bom</artifactId><version>${spring-ai-alibaba.version}</version><type>pom</type><scope>import</scope></dependency><!-- Spring AI 官方 BOM(核心抽象与 Starter 的版本对齐) --><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-bom</artifactId><version>${spring-ai.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement>这里体现了几个核心设计理念:
- 通过BOM(
spring-ai-alibaba-bom+spring-ai-bom)统一版本管理,避免各个 Starter 之间的版本地狱; - 实际运行时模型选择Ollama,既方便本地开发调试,又可以在网络受限场景下顺畅运行;
- 未来如果要切到阿里云 DashScope,只需要:
- 打开已经写好的(但当前被注释掉的)
spring-ai-alibaba-starter-dashscope依赖; - 在配置文件里增加
spring.ai.dashscope.*对应配置,不需要改业务代码。
- 打开已经写好的(但当前被注释掉的)
环境配置:Ollama + Qwen3 + PgVector
spring_ai_alibaba-demo/src/main/resources/application.yaml中:
server:port:8081# 应用监听端口servlet:context-path:/alibaba-ai# 统一的服务前缀spring:ai:ollama:base-url:http://localhost:11434# 本地 Ollama 服务地址chat:options:model:qwen3:0.6b# 聊天用的 Qwen3 模型名称temperature:0.8# 采样温度,越高回答越发散embedding:options:model:qwen3-embedding:0.6b# 用于向量化的 embedding 模型vectorstore:pgvector:dimensions:1024# 向量维度,需要与 embedding 模型输出一致distance-type:cosine_distance# 相似度度量方式initialize-schema:true# 启动时自动创建 PgVector 表结构datasource:url:jdbc:postgresql://<your-host>:5432/postgres?serverTimezone=Asia/Shanghai# PostgreSQL 连接串username:postgrespassword:****# 建议通过环境变量或配置中心注入- Ollama 在本机 11434 端口提供服务,加载的是
qwen3:0.6b模型(本质上仍然是阿里云通义家族的模型,只是以本地方式运行); - Embedding 使用
qwen3-embedding:0.6b; - PgVector 存储维度设置为 1024,采用余弦相似度;
- 数据源配置指向 PostgreSQL,用于向量存储和(可选)对话记忆持久化。
三、基础对话:从 ChatModel 到 ChatClient
Demo 中提供了两种对话方式:直接使用ChatModel,以及通过ChatClient封装后的高级用法。
3.1 使用 ChatModel 流式返回
ChatModelController:
@RestController@RequestMapping("/chatModel")publicclassChatModelController{// 注入由 Spring AI 自动装配的 Ollama ChatModelprivatefinalChatModelollamaChatModel;publicChatModelController(ChatModelollamaChatModel){this.ollamaChatModel=ollamaChatModel;}@GetMapping("/chat")publicFlux<String>chat(@RequestParam("message")Stringmessage){// message:用户输入的自然语言问题returnollamaChatModel.stream(newPrompt(message))// 以流式方式调用大模型.map(ChatResponse::getResult)// 提取每个增量响应的结果对象.mapNotNull(result->result.getOutput().getText());// 只保留最终输出的文本内容}}ChatModel由spring-ai-starter-model-ollama自动装配,底层指向本地 Qwen3 模型;.stream(...)返回的是一个响应式 Flux,可以在前端按 token/片段逐步渲染;- 控制器本身和普通 Spring Web 控制器没有本质差别,学习成本非常低。
3.2 使用 ChatClient 提升可用性
ChatClientController:
@RestController@RequestMapping("/chatClient")publicclassChatClientController{// 基于 ChatModel 封装的高级客户端,后续可以挂接 Adviser、工具等能力privatefinalChatClientollamaChatClient;publicChatClientController(ChatClientollamaChatClient){this.ollamaChatClient=ollamaChatClient;}@GetMapping("/chat")publicFlux<String>stream(@RequestParam("message")Stringmessage){// 使用最简单的 Prompt,直接将用户输入交给大模型,并以流式方式返回结果returnollamaChatClient.prompt(newPrompt(message))// 构造 Prompt 对象.stream()// 流式调用.content();// 提取文本内容}@GetMapping("/prompt")publicFlux<String>prompt(){PromptTemplatetemplate=PromptTemplate.builder().template("请用简短中文回答:{question}")// 模板中定义占位符 {question}.variables(Map.of())// 这里可以预先声明变量,也可以在 create 时传入.build();// 使用实际问题填充模板变量Promptprompt=template.create(Map.of("question","Spring AI Alibaba 有什么特点?"));returnollamaChatClient.prompt(prompt).stream().content();}}和ChatModel相比,ChatClient的优势在于:
- 提供链式 API:
.prompt().call()/stream(),更易读; - 更容易挂接 Adviser(记忆、RAG、工具等),形成统一调用入口;
- 在需要多轮交互、上下文管理时更易扩展。
在OllamaConfig中,Demo 还展示了如何为ChatClient挂接记忆 Adviser,后面章节会展开。
四、对话记忆:内存版与可扩展版
实际业务中,一个“傻傻忘记前文”的大模型体验非常差。Demo 中给出了两种记忆实现方式。
4.1 简单内存记忆:SimpleMemories
@ComponentpublicclassSimpleMemoriesimplementsChatMemory{privatestaticfinalMap<String,List<Message>>MEMORIES_CACHE=newHashMap<>();@Overridepublicvoidadd(StringconversationId,List<Message>messages){// conversationId:会话标识;messages:本轮新增的消息列表List<Message>memories=MEMORIES_CACHE.getOrDefault(conversationId,newArrayList<>());if(messages!=null&&!messages.isEmpty()){memories.addAll(messages);}MEMORIES_CACHE.put(conversationId,memories);}@OverridepublicList<Message>get(StringconversationId){// 根据会话 ID 取出该会话的历史消息returnMEMORIES_CACHE.getOrDefault(conversationId,newArrayList<>());}@Overridepublicvoidclear(StringconversationId){// 清空某个会话的记忆List<Message>messages=MEMORIES_CACHE.get(conversationId);if(messages!=null){messages.clear();}}}- 通过
conversationId区分不同会话; - 适合 Demo、PoC 或对可靠性要求不高的场景;
- 结合
MessageChatMemoryAdvisor可以自动把历史消息注入到当前 Prompt 中。
4.2 Adviser 方式:MemoriesAdviser
@ComponentpublicclassMemoriesAdviserimplementsBaseAdvisor{privatestaticfinalMap<String,List<Message>>MEMORIES=newHashMap<>();// 用于在 ChatClient 的上下文中标识当前会话 ID 的 keyprivatestaticfinalStringCHAT_MEMORIES_SESSION_ID="chat_memories_session_id";@OverridepublicChatClientRequestbefore(ChatClientRequestrequest,AdvisorChainchain){// 从上下文中读取会话 ID,并取出其历史消息StringsessionId=request.context().get(CHAT_MEMORIES_SESSION_ID).toString();List<Message>messages=MEMORIES.getOrDefault(sessionId,newArrayList<>());// 当前请求的消息放到历史消息后面,一起交给大模型messages.addAll(request.prompt().getInstructions());Promptprompt=request.prompt().mutate().messages(messages).build();returnrequest.mutate().prompt(prompt).build();}@OverridepublicChatClientResponseafter(ChatClientResponseresponse,AdvisorChainchain){// 把本次大模型回复写回到对应会话的记忆中AssistantMessageoutput=response.chatResponse().getResult().getOutput();StringsessionId=response.context().get(CHAT_MEMORIES_SESSION_ID).toString();List<Message>messages=MEMORIES.getOrDefault(sessionId,newArrayList<>());messages.add(output);MEMORIES.put(sessionId,messages);returnresponse;}}- 在
before中把历史消息 + 当前消息拼成一个新的 Prompt; - 在
after中把模型回复写回内存; - 通过在
ChatClient构建时添加defaultAdvisors(memoriesAdvisor),即可对所有请求启用记忆能力。
进一步,你可以把DataBaseChatMemoryRepository补充完整,将消息写入数据库,实现持久化对话记忆。
五、RAG:Qwen3 + PgVector 的检索增强
RAG(Retrieval Augmented Generation)是典型的企业级能力,本 Demo 通过RagChatClientController进行演示。
5.1 向量入库:TokenTextSplitter + PgVectorStore
@RestController@RequestMapping("/rag")publicclassRagChatClientController{privatefinalChatClientragChatClient;privatefinalPgVectorStorepgVectorStore;publicRagChatClientController(ChatClientragChatClient,PgVectorStorepgVectorStore){this.ragChatClient=ragChatClient;this.pgVectorStore=pgVectorStore;}@GetMapping("/embedding")publicvoidembeddingContent(@RequestParam("message")Stringmessage){// message:待向量化的原始文本内容TokenTextSplittersplitter=TokenTextSplitter.builder().withChunkSize(50)// 每个分片的最大 token 数.withKeepSeparator(true)// 是否保留分隔符(如换行符).withMaxNumChunks(1024)// 单次允许生成的最大分片数.withMinChunkLengthToEmbed(20)// 小于该长度的分片不入库,避免噪声.withMinChunkSizeChars(10)// 切分时的最小字符数,避免切得过碎.build();List<Document>docs=splitter.split(Document.builder().text(message).build());// 将文本切分为多个 DocumentpgVectorStore.add(docs);// 写入 PgVector 向量库}}TokenTextSplitter基于 token 切分文档,避免切得过碎或过长;PgVectorStore.add将切分后的文档写入 PostgreSQL + PgVector;- 真实项目中可把
/embedding换成异步批处理任务。
5.2 RAG 对话:RetrievalAugmentationAdvisor
@ConfigurationpublicclassVectorChatClientConfig{@Bean("ragChatClient")publicChatClientragChatClient(ChatModelchatModel,VectorStorevectorStore){VectorStoreDocumentRetrieverretriever=VectorStoreDocumentRetriever.builder().vectorStore(vectorStore)// 具体使用的向量库实现,这里是 PgVector.topK(3)// 每次检索返回相似度最高的前 3 条文档.similarityThreshold(0.5)// 相似度阈值,小于该值的文档会被过滤掉.build();RetrievalAugmentationAdvisoradvisor=RetrievalAugmentationAdvisor.builder().documentRetriever(retriever)// 指定文档检索器.order(0)// Adviser 执行顺序,越小越先执行.build();returnChatClient.builder(chatModel).defaultAdvisors(advisor)// 默认启用 RAG 能力.build();}}RetrievalAugmentationAdvisor会在每次请求前,先到向量库检索相关文档;- 然后把检索结果作为“系统提示词”或“上下文”塞给 Qwen3 模型;
- 对你来说,只需调用
ragChatClient.prompt().user(question).call(),就能得到“带知识库”的回答。
六、工具调用:用 @Tool 让模型调用你的 Java 方法
在很多场景中,大模型需要调用业务系统的 API 才能完成任务。Spring AI 提供了@Tool注解,Demo 中的ZoomTool便是一个简单示例。
6.1 定义工具:ZoomTool
@ComponentpublicclassZoomTool{@Tool(description="通过时区 ID 获取当前时间")publicStringgetTimeByZone(@ToolParam(description="时区 ID,比如 Asia/Shanghai")Stringzone){// zone:时区 ID,示例:Asia/Shanghai、Europe/BerlinZoneIdzoneId=ZoneId.of(zone);ZonedDateTimenow=ZonedDateTime.now(zoneId);returnDateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(now);// 返回格式化后的时间字符串}}6.2 将工具挂到 ChatClient 上
@ConfigurationpublicclassToolChatClientConfig{@Bean("toolChatClient")publicChatClienttoolChatClient(ChatModelollamaChatModel,ZoomToolzoomTool){// ollamaChatModel:底层使用的 Qwen3 模型;zoomTool:提供获取时间的业务工具returnChatClient.builder(ollamaChatModel).defaultSystem(this.systemPrompt())// 设置默认的系统提示词,统一咖啡馆背景.defaultTools(zoomTool)// 将 ZoomTool 注册为可调用的工具.build();}privateStringsystemPrompt(){Map<String,Object>vars=newHashMap<>();vars.put("AMERICAN","1-3");// 美式咖啡制作时间(分钟)vars.put("LATTE","2");// 拿铁咖啡制作时间(分钟)vars.put("TIME_ZONE","Asia/Shanghai");// 默认时区 IDSystemPromptTemplatetpl=SystemPromptTemplate.builder().template("欢迎光临 ZhouByte咖啡馆,... 默认时区:{TIME_ZONE}")// 系统提示词模板.variables(vars)// 绑定上面的变量.build();returntpl.render();// 渲染出包含具体变量值的系统提示词}}对应的 Controller:
@RestController@RequestMapping("/tool")publicclassToolChatController{privatefinalChatClienttoolChatClient;publicToolChatController(ChatClienttoolChatClient){this.toolChatClient=toolChatClient;}@GetMapping("/chat")publicFlux<String>chat(@RequestParam("message")Stringmessage){returntoolChatClient.prompt()// 创建一次新的对话请求.user(message)// 添加一条用户消息.stream()// 流式调用大模型.content();// 只提取文本内容返回}}- 模型可以在需要时自动调用
getTimeByZone,返回指定时区时间; - 你只需要编写普通的 Java 方法,剩下的交给 Spring AI 的工具调用机制。
七、Alibaba Graph 子项目:有状态工作流编排
spring_ai_alibaba-demo/alibaba-graph子项目使用spring-ai-alibaba-graph-core演示了如何构建大模型工作流。
7.1 定义 Graph:StateGraph + CompiledGraph
GraphConfig中:
@ConfigurationpublicclassGraphConfig{@Bean("quickStartGraph")publicCompiledGraphquickStartGraph()throwsGraphStateException{// "quickStartGraph":图名称;后面的 Map 用于定义状态 key 的合并策略StateGraphgraph=newStateGraph("quickStartGraph",()->Map.of("input",newReplaceStrategy(),// 多次写入时,后写入的值覆盖之前的值"output",newReplaceStrategy()));graph.addNode("node1",AsyncNodeAction.node_async(state->{// node1:设置初始 input 和 outputreturnMap.of("input","graphConfig_addNode","output","graphConfig_output");}));graph.addNode("node2",AsyncNodeAction.node_async(state->{// node2:模拟业务处理,将 input 改为 ZhouBytereturnMap.of("input","ZhouByte","output","EMPTY");}));// 定义执行顺序:START -> node1 -> node2 -> ENDgraph.addEdge(StateGraph.START,"node1").addEdge("node1","node2").addEdge("node2",StateGraph.END);returngraph.compile();}}StateGraph描述节点、边和状态合并策略;AsyncNodeAction封装每个节点的执行逻辑;compile()得到可执行的CompiledGraph。
7.2 调用 Graph:WebFlux + 流式输出
GraphController:
@RestController@RequestMapping("/v1")publicclassGraphController{@ResourceprivateCompiledGraphquickStartGraph;@GetMapping("/graph")publicFlux<String>startGraph(){// 这里传入空的初始状态 Map,按定义好的 StateGraph 顺序执行returnquickStartGraph.stream(Map.of()).map(NodeOutput::toString);// 将每个节点的输出对象转换为字符串返回}}- 使用 WebFlux +
Flux<NodeOutput>将节点执行结果流式返回; - 通过
RunnableConfig.builder().threadId(conversationId)还可以实现“带会话 ID 的工作流”,类似有状态 Agent。
7.3 quickStartGraph 执行流程图
结合上面的GraphConfig和GraphController,/v1/graph接口整体执行流程可以用下面这张流程图来表示(以 GitHub 为例,可以直接渲染 Mermaid):
- 从
GraphController.startGraph()开始,调用CompiledGraph.stream(Map.of())启动图的执行; - 图从
StateGraph.START出发,依次流经node1、node2,最终到达StateGraph.END; - 每个节点都会向全局状态写入
input/output等字段,并以Flux<NodeOutput>的形式逐步返回给调用方。
7.4 多条件分支 Graph 示例(addConditionalEdges)
在实际业务中,Graph 往往不只是线性顺序,还会根据状态进行分支判断。spring-ai-alibaba-graph-core提供了addConditionalEdges,可以基于当前OverAllState计算「条件标签」,再根据标签跳转到不同节点。
下面是一个简化的「评分决策」示例,根据score分数分别走向通过 / 复核 / 拒绝三条路径:
@ConfigurationpublicclassConditionalGraphConfig{@Bean("scoreDecisionGraph")publicCompiledGraphscoreDecisionGraph()throwsGraphStateException{StateGraphgraph=newStateGraph("scoreDecisionGraph",()->Map.of("score",newReplaceStrategy(),// 保存当前评分"result",newReplaceStrategy()// 保存决策结果));// 读取或设置评分(示例中从 state 中读取,实际可由外部请求传入)graph.addNode("checkScore",AsyncNodeAction.node_async(state->{Integerscore=(Integer)state.value("score").orElse(75);// 默认 75 分returnMap.of("score",score);}));// 三个业务分支节点:通过 / 复核 / 拒绝graph.addNode("pass",AsyncNodeAction.node_async(state->Map.of("result","PASS")));graph.addNode("review",AsyncNodeAction.node_async(state->Map.of("result","REVIEW")));graph.addNode("reject",AsyncNodeAction.node_async(state->Map.of("result","REJECT")));// 起点先进入评分检查节点graph.addEdge(StateGraph.START,"checkScore");// 多条件边:根据 score 返回不同的“标签”,再由 mappings 决定下一跳节点graph.addConditionalEdges("checkScore",AsyncEdgeAction.edge_async(state->{intscore=(Integer)state.value("score").orElse(0);if(score>=80){return"PASS";}if(score>=60){return"REVIEW";}return"REJECT";}),Map.of("PASS","pass","REVIEW","review","REJECT","reject"));// 三个结果节点最终都指向 ENDgraph.addEdge("pass",StateGraph.END);graph.addEdge("review",StateGraph.END);graph.addEdge("reject",StateGraph.END);returngraph.compile();}}这段代码中,addConditionalEdges的三个参数含义是:
sourceId:条件边的源节点 ID,这里是"checkScore";AsyncEdgeAction:根据当前OverAllState计算条件标签,这里返回"PASS"/"REVIEW"/"REJECT";mappings:标签与目标节点 ID 的映射,例如"PASS" -> "pass",即当标签为"PASS"时跳到pass节点。
对应的执行流程,可以画成如下多分支流程图:
在真实项目中,你可以把score换成「风控评分」「召回结果命中情况」「用户画像标签」等任意业务信号,通过addConditionalEdges把复杂分支逻辑从代码 if/else 中抽离出来,统一放在 Graph 层管理。
在这个子项目中,Graph 本身是“流程层”,可以在节点里调用 Spring AI / Spring AI Alibaba 的各种模型与工具,实现复杂的多步推理与业务编排。
八、如何从本地 Ollama 平滑切到阿里云 DashScope
虽然当前 Demo 主要跑在本地 Ollama 上,但由于使用了 Spring AI + Spring AI Alibaba 的统一抽象,切换到阿里云 DashScope 十分简单:
- 在
pom.xml中启用 DashScope Starter(示例中已给出注释代码):
<dependency><groupId>com.alibaba.cloud.ai</groupId><artifactId>spring-ai-alibaba-starter-dashscope</artifactId><version>1.1.0.0-M5</version></dependency>- 在配置文件中增加 DashScope 的配置(示例):
spring:ai:dashscope:api-key:${DASHSCOPE_API_KEY}# 从环境变量或配置中心读取 DashScope 的 API Keyendpoint:https://dashscope.aliyuncs.comchat:options:model:qwen-plus# 使用的通义千问在线模型temperature:0.8# 采样温度max-tokens:2048# 单次回答的最大 token 数- 将原来的
ChatModel注入点从 Ollama 替换为 DashScope 对应的 Bean(通常只需要调整配置,不改业务代码)。
凭借 Spring AI 的抽象层,你可以:
- 开发阶段:本地跑 Qwen3(Ollama),成本低、调试快;
- 生产阶段:切到云上 DashScope(Qwen-Max / Qwen-Plus 等),享受更强算力和更高可用性;
- 中长期:在 Spring AI Alibaba 的生态内同时兼容多家国内模型厂商。
九、实践建议与最佳实践
配置管理
- API Key 使用环境变量或配置中心(Nacos、KMS 等),避免硬编码;
- Ollama、DashScope 的模型名称、温度等参数尽量抽到配置文件中。
错误处理与重试
- 针对网络异常、超时、限流等场景做兜底和重试策略;
- 对外暴露的接口统一封装错误返回,避免直接把底层错误抛给前端。
性能与成本
- 在高并发场景建议优先使用流式输出 + 前端增量渲染;
- RAG 中控制 TopK、相似度阈值和切分策略,避免向量库“爆炸”。
代码结构
- 将
ChatClient配置、Graph 配置等放在独立的config包中,业务层只关心接口调用; - 工具方法使用
@Tool暴露,便于模型统一管理和调用。
- 将
版本与升级
- Spring AI Alibaba 当前仍以 Milestone 版本为主(如
1.1.0.0-M5),升级前建议阅读 release notes; - 保持对
spring-ai-bom/spring-ai-alibaba-bom的依赖,让升级尽量在 BOM 层完成。
- Spring AI Alibaba 当前仍以 Milestone 版本为主(如
十、总结与展望
基于spring_ai_alibaba-demo子项目,我们实际体验了一次:
- 如何用Spring AI+Spring AI Alibaba BOM快速接入本地 Qwen3(Ollama);
- 如何在同一套抽象下串联起对话、记忆、RAG、工具调用;
- 如何通过
spring-ai-alibaba-graph-core构建基于大模型的有状态工作流; - 以及如何在不改业务代码的前提下,为未来切换到阿里云 DashScope 留出空间。
对 Spring 开发者来说,这套体系最大的价值在于:
- 统一抽象:不同模型供应商之间切换成本极低;
- 生态完善:兼容 Spring Boot、WebFlux、向量库、MCP、Graph 等丰富组件;
- 本地 + 云端双模:既能在本地快速迭代,又能无缝迁移到云上生产环境。
再次附上示例子项目 GitHub 地址,欢迎你亲手跑一跑代码、提 Issue、点 Star:
GitHub 项目地址:https://github.com/zhouByte-hub/java-ai/tree/main/spring_ai_alibaba-demo