news 2026/1/30 11:36:26

Spring AI Alibaba + Ollama 实战:基于本地 Qwen3 的 Spring Boot 大模型应用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring AI Alibaba + Ollama 实战:基于本地 Qwen3 的 Spring Boot 大模型应用

在大模型快速演进的今天,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 AIspring-ai-starter-model-ollama接入本地 Ollama 服务;
    • 使用spring-ai-starter-vector-store-pgvector集成 PostgreSQL + PgVector 做向量检索;
    • 通过ChatModel/ChatClient演示基础对话、RAG、工具调用和记忆;
    • 通过依赖管理引入spring-ai-alibaba-bom,为后续接入阿里云生态(包括 DashScope、Graph 等)奠定基础。
  • 子模块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>

这里体现了几个核心设计理念:

  • 通过BOMspring-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());// 只保留最终输出的文本内容}}
  • ChatModelspring-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 执行流程图

结合上面的GraphConfigGraphController/v1/graph接口整体执行流程可以用下面这张流程图来表示(以 GitHub 为例,可以直接渲染 Mermaid):

HTTP 请求:GET /alibaba-graph/v1/graph
GraphController.startGraph()
CompiledGraph.stream(Map.of())
StateGraph.START
节点 node1
input = graphConfig_addNode
output = graphConfig_output
节点 node2
input = ZhouByte
output = EMPTY
StateGraph.END
Flux 流式返回
  • GraphController.startGraph()开始,调用CompiledGraph.stream(Map.of())启动图的执行;
  • 图从StateGraph.START出发,依次流经node1node2,最终到达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 十分简单:

  1. 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>
  1. 在配置文件中增加 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 数
  1. 将原来的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-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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/1/29 22:45:06

DTIIA 5.0 输送机系统设计说明

单台输送机IIA 手册 第2章~第4章 介绍了单台输送机 从 整机设计、设计计算、部件选型的设计过程。多台输送机但是&#xff0c;在实际应用中&#xff0c;输送机作为单台设备运转的情况是比较少见的。一般都是 多台输送机 联合运转 或是 与工艺设备组合完成某种工艺生产过程&…

作者头像 李华
网站建设 2026/1/29 22:45:08

JavaEE进阶——SpringBoot统一功能处理实战指南

目录 Spring Boot统一功能处理详解&#xff08;新手完整版&#xff09; 1. 拦截器详解 1.1 什么是拦截器 1.2 完整代码实现&#xff08;逐行注释&#xff09; 1.2.1 定义登录拦截器&#xff08;传统Session方式&#xff09; 1.2.3 定义登录拦截器&#xff08;现代Token方…

作者头像 李华
网站建设 2026/1/25 5:37:12

leetcode 2110. 股票平滑下跌阶段的数目 中等

给你一个整数数组 prices &#xff0c;表示一支股票的历史每日股价&#xff0c;其中 prices[i] 是这支股票第 i 天的价格。 一个 平滑下降的阶段 定义为&#xff1a;对于 连续一天或者多天 &#xff0c;每日股价都比 前一日股价恰好少 1 &#xff0c;这个阶段第一天的股价没有…

作者头像 李华
网站建设 2026/1/28 7:47:11

15、智能平台管理接口驱动与直接内存访问技术解析

智能平台管理接口驱动与直接内存访问技术解析 1. 智能平台管理接口(IPMI)驱动案例分析 IPMI驱动在系统管理中起着重要作用,下面我们将对其核心函数进行详细分析。 1.1 ipmi2_pci_probe函数 该函数用于判断设备是否为PCI总线上的通用IPMI设备。以下是其代码实现: stat…

作者头像 李华
网站建设 2026/1/27 2:20:02

Ability Kit(程序框架服务)Stage模型

应用模型 应用模型是系统为开发者提供的应用程序所需能力的抽象提炼&#xff0c;它提供了应用程序必备的组件和运行机制。有了应用模型&#xff0c;开发者可以基于一套统一的模型进行应用开发&#xff0c;使应用开发更简单、高效。 应用模型的构成要素包括&#xff1a; 应用组…

作者头像 李华
网站建设 2026/1/29 9:15:14

JVM内存结构与Java内存模型的区别

我们在讨论java语言的内存问题时经常会听到一个词叫“JVM内存模型”&#xff0c;这个词在实际使用中容易产生歧义&#xff0c;因为它通常可能指代两个密切相关但不同的概念&#xff1a;Java内存模型 (Java Memory Model, JMM)&#xff1a;这是一个并发概念&#xff0c;定义了Ja…

作者头像 李华