官網(wǎng)整站優(yōu)化山東響應式網(wǎng)站開發(fā)
鶴壁市浩天電氣有限公司
2026/01/24 08:27:50
官網(wǎng)整站優(yōu)化,山東響應式網(wǎng)站開發(fā),好的案例展示網(wǎng)站,五家渠建設局網(wǎng)站在大模型快速演進的今天#xff0c;Java 開發(fā)者同樣希望“開箱即用”地接入各類模型服務。Spring 官方推出的 Spring AI#xff0c;已經(jīng)為 Java / Spring Boot 應用提供了一套統(tǒng)一、優(yōu)雅的 AI 抽象#xff1b;而在國內(nèi)模型生態(tài)中#xff0c;如何更好地對接阿里云通義#…在大模型快速演進的今天Java 開發(fā)者同樣希望“開箱即用”地接入各類模型服務。Spring 官方推出的 Spring AI已經(jīng)為 Java / Spring Boot 應用提供了一套統(tǒng)一、優(yōu)雅的 AI 抽象而在國內(nèi)模型生態(tài)中如何更好地對接阿里云通義Qwen與靈積平臺DashScope則是 Spring AI Alibaba 重點解決的問題。本文基于倉庫中的spring_ai_alibaba-demo子項目從真實代碼出發(fā)帶你一起拆解如何用 Spring AI Spring AI Alibaba 的生態(tài)在本地通過Ollama 跑 Qwen3 模型并逐步擴展到 RAG、工具調(diào)用和 Graph 工作流。GitHub 項目地址https://github.com/zhouByte-hub/java-ai/tree/main/spring_ai_alibaba-demo歡迎 Star、Fork 和關注文中所有代碼都可以在該子項目中找到更適合邊讀邊跑。面向讀者已有 Spring Boot 基礎希望快速接入大模型的后端開發(fā)計劃在本地或內(nèi)網(wǎng)環(huán)境使用 Qwen3 等模型通過 Ollama但又希望未來平滑切到阿里云 DashScope想了解 Spring AI Alibaba 在 Graph、RAG、工具調(diào)用等場景中的作用和優(yōu)勢。一、項目概覽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、工具調(diào)用和記憶通過依賴管理引入spring-ai-alibaba-bom為后續(xù)接入阿里云生態(tài)包括 DashScope、Graph 等奠定基礎。子模塊alibaba-graph使用spring-ai-alibaba-graph-core演示基于大模型的有狀態(tài)流程StateGraph依然以 Ollama 的 Qwen3 作為底層模型子模塊alibaba-mcp-server/alibaba-mcp-client使用 Spring AI 的 MCP 能力演示模型調(diào)用外部工具 / 資源的模式。換句話說當前 Demo沒有直接連阿里云 DashScope而是選擇在本地通過Ollama 運行 Qwen3 模型但項目在依賴管理和結(jié)構設計上已經(jīng)完全站在Spring AI Alibaba 生態(tài)之上隨時可以切換到阿里云在線服務。接下來我們按“從簡單到復雜”的順序依次看看各個模塊是怎么搭建的。二、依賴與環(huán)境本地 Qwen3 PgVector先看根模塊spring_ai_alibaba-demo/pom.xml中的關鍵部分properties!-- 項目使用的 JDK 版本 --java.version17/java.version!-- Spring AI Alibaba 相關依賴統(tǒng)一使用的版本 --spring-ai-alibaba.version1.1.0.0-M5/spring-ai-alibaba.version!-- Spring AI 核心依賴統(tǒng)一使用的版本 --spring-ai.version1.1.0/spring-ai.version/propertiesdependencies!-- 基礎 Web 能力提供 Spring MVC / 內(nèi)嵌容器等 --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependency!-- 通過 Spring AI 訪問本地 Ollama 大模型服務 --dependencygroupIdorg.springframework.ai/groupIdartifactIdspring-ai-starter-model-ollama/artifactId/dependency!-- 向量數(shù)據(jù)庫Spring AI 對 PgVector 的封裝 --dependencygroupIdorg.springframework.ai/groupIdartifactIdspring-ai-starter-vector-store-pgvector/artifactId/dependency!-- PostgreSQL JDBC 驅(qū)動用于訪問數(shù)據(jù)庫 --dependencygroupIdorg.postgresql/groupIdartifactIdpostgresql/artifactId/dependency/dependenciesdependencyManagementdependencies!-- Spring AI Alibaba 統(tǒng)一版本管理國內(nèi)生態(tài)相關依賴 --dependencygroupIdcom.alibaba.cloud.ai/groupIdartifactIdspring-ai-alibaba-bom/artifactIdversion${spring-ai-alibaba.version}/versiontypepom/typescopeimport/scope/dependency!-- Spring AI 官方 BOM核心抽象與 Starter 的版本對齊 --dependencygroupIdorg.springframework.ai/groupIdartifactIdspring-ai-bom/artifactIdversion${spring-ai.version}/versiontypepom/typescopeimport/scope/dependency/dependencies/dependencyManagement這里體現(xiàn)了幾個核心設計理念通過BOMspring-ai-alibaba-bomspring-ai-bom統(tǒng)一版本管理避免各個 Starter 之間的版本地獄實際運行時模型選擇Ollama既方便本地開發(fā)調(diào)試又可以在網(wǎng)絡受限場景下順暢運行未來如果要切到阿里云 DashScope只需要打開已經(jīng)寫好的但當前被注釋掉的spring-ai-alibaba-starter-dashscope依賴在配置文件里增加spring.ai.dashscope.*對應配置不需要改業(yè)務代碼。環(huán)境配置Ollama Qwen3 PgVectorspring_ai_alibaba-demo/src/main/resources/application.yaml中server:port:8081# 應用監(jiān)聽端口servlet:context-path:/alibaba-ai# 統(tǒng)一的服務前綴spring:ai:ollama:base-url:http://localhost:11434# 本地 Ollama 服務地址chat:options:model:qwen3:0.6b# 聊天用的 Qwen3 模型名稱temperature:0.8# 采樣溫度越高回答越發(fā)散embedding:options:model:qwen3-embedding:0.6b# 用于向量化的 embedding 模型vectorstore:pgvector:dimensions:1024# 向量維度需要與 embedding 模型輸出一致distance-type:cosine_distance# 相似度度量方式initialize-schema:true# 啟動時自動創(chuàng)建 PgVector 表結(jié)構datasource:url:jdbc:postgresql://your-host:5432/postgres?serverTimezoneAsia/Shanghai# PostgreSQL 連接串username:postgrespassword:****# 建議通過環(huán)境變量或配置中心注入Ollama 在本機 11434 端口提供服務加載的是qwen3:0.6b模型本質(zhì)上仍然是阿里云通義家族的模型只是以本地方式運行Embedding 使用qwen3-embedding:0.6bPgVector 存儲維度設置為 1024采用余弦相似度數(shù)據(jù)源配置指向 PostgreSQL用于向量存儲和可選對話記憶持久化。三、基礎對話從 ChatModel 到 ChatClientDemo 中提供了兩種對話方式直接使用ChatModel以及通過ChatClient封裝后的高級用法。3.1 使用 ChatModel 流式返回ChatModelControllerRestControllerRequestMapping(/chatModel)publicclassChatModelController{// 注入由 Spring AI 自動裝配的 Ollama ChatModelprivatefinalChatModelollamaChatModel;publicChatModelController(ChatModelollamaChatModel){this.ollamaChatModelollamaChatModel;}GetMapping(/chat)publicFluxStringchat(RequestParam(message)Stringmessage){// message用戶輸入的自然語言問題returnollamaChatModel.stream(newPrompt(message))// 以流式方式調(diào)用大模型.map(ChatResponse::getResult)// 提取每個增量響應的結(jié)果對象.mapNotNull(result-result.getOutput().getText());// 只保留最終輸出的文本內(nèi)容}}ChatModel由spring-ai-starter-model-ollama自動裝配底層指向本地 Qwen3 模型.stream(...)返回的是一個響應式 Flux可以在前端按 token/片段逐步渲染控制器本身和普通 Spring Web 控制器沒有本質(zhì)差別學習成本非常低。3.2 使用 ChatClient 提升可用性ChatClientControllerRestControllerRequestMapping(/chatClient)publicclassChatClientController{// 基于 ChatModel 封裝的高級客戶端后續(xù)可以掛接 Adviser、工具等能力privatefinalChatClientollamaChatClient;publicChatClientController(ChatClientollamaChatClient){this.ollamaChatClientollamaChatClient;}GetMapping(/chat)publicFluxStringstream(RequestParam(message)Stringmessage){// 使用最簡單的 Prompt直接將用戶輸入交給大模型并以流式方式返回結(jié)果returnollamaChatClient.prompt(newPrompt(message))// 構造 Prompt 對象.stream()// 流式調(diào)用.content();// 提取文本內(nèi)容}GetMapping(/prompt)publicFluxStringprompt(){PromptTemplatetemplatePromptTemplate.builder().template(請用簡短中文回答{question})// 模板中定義占位符 {question}.variables(Map.of())// 這里可以預先聲明變量也可以在 create 時傳入.build();// 使用實際問題填充模板變量Promptprompttemplate.create(Map.of(question,Spring AI Alibaba 有什么特點));returnollamaChatClient.prompt(prompt).stream().content();}}和ChatModel相比ChatClient的優(yōu)勢在于提供鏈式 API.prompt().call()/stream()更易讀更容易掛接 Adviser記憶、RAG、工具等形成統(tǒng)一調(diào)用入口在需要多輪交互、上下文管理時更易擴展。在OllamaConfig中Demo 還展示了如何為ChatClient掛接記憶 Adviser后面章節(jié)會展開。四、對話記憶內(nèi)存版與可擴展版實際業(yè)務中一個“傻傻忘記前文”的大模型體驗非常差。Demo 中給出了兩種記憶實現(xiàn)方式。4.1 簡單內(nèi)存記憶SimpleMemoriesComponentpublicclassSimpleMemoriesimplementsChatMemory{privatestaticfinalMapString,ListMessageMEMORIES_CACHEnewHashMap();Overridepublicvoidadd(StringconversationId,ListMessagemessages){// conversationId會話標識messages本輪新增的消息列表ListMessagememoriesMEMORIES_CACHE.getOrDefault(conversationId,newArrayList());if(messages!null!messages.isEmpty()){memories.addAll(messages);}MEMORIES_CACHE.put(conversationId,memories);}OverridepublicListMessageget(StringconversationId){// 根據(jù)會話 ID 取出該會話的歷史消息returnMEMORIES_CACHE.getOrDefault(conversationId,newArrayList());}Overridepublicvoidclear(StringconversationId){// 清空某個會話的記憶ListMessagemessagesMEMORIES_CACHE.get(conversationId);if(messages!null){messages.clear();}}}通過conversationId區(qū)分不同會話適合 Demo、PoC 或?qū)煽啃砸蟛桓叩膱鼍敖Y(jié)合MessageChatMemoryAdvisor可以自動把歷史消息注入到當前 Prompt 中。4.2 Adviser 方式MemoriesAdviserComponentpublicclassMemoriesAdviserimplementsBaseAdvisor{privatestaticfinalMapString,ListMessageMEMORIESnewHashMap();// 用于在 ChatClient 的上下文中標識當前會話 ID 的 keyprivatestaticfinalStringCHAT_MEMORIES_SESSION_IDchat_memories_session_id;OverridepublicChatClientRequestbefore(ChatClientRequestrequest,AdvisorChainchain){// 從上下文中讀取會話 ID并取出其歷史消息StringsessionIdrequest.context().get(CHAT_MEMORIES_SESSION_ID).toString();ListMessagemessagesMEMORIES.getOrDefault(sessionId,newArrayList());// 當前請求的消息放到歷史消息后面一起交給大模型messages.addAll(request.prompt().getInstructions());Promptpromptrequest.prompt().mutate().messages(messages).build();returnrequest.mutate().prompt(prompt).build();}OverridepublicChatClientResponseafter(ChatClientResponseresponse,AdvisorChainchain){// 把本次大模型回復寫回到對應會話的記憶中AssistantMessageoutputresponse.chatResponse().getResult().getOutput();StringsessionIdresponse.context().get(CHAT_MEMORIES_SESSION_ID).toString();ListMessagemessagesMEMORIES.getOrDefault(sessionId,newArrayList());messages.add(output);MEMORIES.put(sessionId,messages);returnresponse;}}在before中把歷史消息 當前消息拼成一個新的 Prompt在after中把模型回復寫回內(nèi)存通過在ChatClient構建時添加defaultAdvisors(memoriesAdvisor)即可對所有請求啟用記憶能力。進一步你可以把DataBaseChatMemoryRepository補充完整將消息寫入數(shù)據(jù)庫實現(xiàn)持久化對話記憶。五、RAGQwen3 PgVector 的檢索增強RAGRetrieval Augmented Generation是典型的企業(yè)級能力本 Demo 通過RagChatClientController進行演示。5.1 向量入庫TokenTextSplitter PgVectorStoreRestControllerRequestMapping(/rag)publicclassRagChatClientController{privatefinalChatClientragChatClient;privatefinalPgVectorStorepgVectorStore;publicRagChatClientController(ChatClientragChatClient,PgVectorStorepgVectorStore){this.ragChatClientragChatClient;this.pgVectorStorepgVectorStore;}GetMapping(/embedding)publicvoidembeddingContent(RequestParam(message)Stringmessage){// message待向量化的原始文本內(nèi)容TokenTextSplittersplitterTokenTextSplitter.builder().withChunkSize(50)// 每個分片的最大 token 數(shù).withKeepSeparator(true)// 是否保留分隔符如換行符.withMaxNumChunks(1024)// 單次允許生成的最大分片數(shù).withMinChunkLengthToEmbed(20)// 小于該長度的分片不入庫避免噪聲.withMinChunkSizeChars(10)// 切分時的最小字符數(shù)避免切得過碎.build();ListDocumentdocssplitter.split(Document.builder().text(message).build());// 將文本切分為多個 DocumentpgVectorStore.add(docs);// 寫入 PgVector 向量庫}}TokenTextSplitter基于 token 切分文檔避免切得過碎或過長PgVectorStore.add將切分后的文檔寫入 PostgreSQL PgVector真實項目中可把/embedding換成異步批處理任務。5.2 RAG 對話RetrievalAugmentationAdvisorConfigurationpublicclassVectorChatClientConfig{Bean(ragChatClient)publicChatClientragChatClient(ChatModelchatModel,VectorStorevectorStore){VectorStoreDocumentRetrieverretrieverVectorStoreDocumentRetriever.builder().vectorStore(vectorStore)// 具體使用的向量庫實現(xiàn)這里是 PgVector.topK(3)// 每次檢索返回相似度最高的前 3 條文檔.similarityThreshold(0.5)// 相似度閾值小于該值的文檔會被過濾掉.build();RetrievalAugmentationAdvisoradvisorRetrievalAugmentationAdvisor.builder().documentRetriever(retriever)// 指定文檔檢索器.order(0)// Adviser 執(zhí)行順序越小越先執(zhí)行.build();returnChatClient.builder(chatModel).defaultAdvisors(advisor)// 默認啟用 RAG 能力.build();}}RetrievalAugmentationAdvisor會在每次請求前先到向量庫檢索相關文檔然后把檢索結(jié)果作為“系統(tǒng)提示詞”或“上下文”塞給 Qwen3 模型對你來說只需調(diào)用ragChatClient.prompt().user(question).call()就能得到“帶知識庫”的回答。六、工具調(diào)用用 Tool 讓模型調(diào)用你的 Java 方法在很多場景中大模型需要調(diào)用業(yè)務系統(tǒng)的 API 才能完成任務。Spring AI 提供了Tool注解Demo 中的ZoomTool便是一個簡單示例。6.1 定義工具ZoomToolComponentpublicclassZoomTool{Tool(description通過時區(qū) ID 獲取當前時間)publicStringgetTimeByZone(ToolParam(description時區(qū) ID比如 Asia/Shanghai)Stringzone){// zone時區(qū) ID示例Asia/Shanghai、Europe/BerlinZoneIdzoneIdZoneId.of(zone);ZonedDateTimenowZonedDateTime.now(zoneId);returnDateTimeFormatter.ofPattern(yyyy-MM-dd HH:mm:ss).format(now);// 返回格式化后的時間字符串}}6.2 將工具掛到 ChatClient 上ConfigurationpublicclassToolChatClientConfig{Bean(toolChatClient)publicChatClienttoolChatClient(ChatModelollamaChatModel,ZoomToolzoomTool){// ollamaChatModel底層使用的 Qwen3 模型zoomTool提供獲取時間的業(yè)務工具returnChatClient.builder(ollamaChatModel).defaultSystem(this.systemPrompt())// 設置默認的系統(tǒng)提示詞統(tǒng)一咖啡館背景.defaultTools(zoomTool)// 將 ZoomTool 注冊為可調(diào)用的工具.build();}privateStringsystemPrompt(){MapString,ObjectvarsnewHashMap();vars.put(AMERICAN,1-3);// 美式咖啡制作時間分鐘vars.put(LATTE,2);// 拿鐵咖啡制作時間分鐘vars.put(TIME_ZONE,Asia/Shanghai);// 默認時區(qū) IDSystemPromptTemplatetplSystemPromptTemplate.builder().template(歡迎光臨 ZhouByte咖啡館... 默認時區(qū){TIME_ZONE})// 系統(tǒng)提示詞模板.variables(vars)// 綁定上面的變量.build();returntpl.render();// 渲染出包含具體變量值的系統(tǒng)提示詞}}對應的 ControllerRestControllerRequestMapping(/tool)publicclassToolChatController{privatefinalChatClienttoolChatClient;publicToolChatController(ChatClienttoolChatClient){this.toolChatClienttoolChatClient;}GetMapping(/chat)publicFluxStringchat(RequestParam(message)Stringmessage){returntoolChatClient.prompt()// 創(chuàng)建一次新的對話請求.user(message)// 添加一條用戶消息.stream()// 流式調(diào)用大模型.content();// 只提取文本內(nèi)容返回}}模型可以在需要時自動調(diào)用getTimeByZone返回指定時區(qū)時間你只需要編寫普通的 Java 方法剩下的交給 Spring AI 的工具調(diào)用機制。七、Alibaba Graph 子項目有狀態(tài)工作流編排spring_ai_alibaba-demo/alibaba-graph子項目使用spring-ai-alibaba-graph-core演示了如何構建大模型工作流。7.1 定義 GraphStateGraph CompiledGraphGraphConfig中ConfigurationpublicclassGraphConfig{Bean(quickStartGraph)publicCompiledGraphquickStartGraph()throwsGraphStateException{// quickStartGraph圖名稱后面的 Map 用于定義狀態(tài) key 的合并策略StateGraphgraphnewStateGraph(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模擬業(yè)務處理將 input 改為 ZhouBytereturnMap.of(input,ZhouByte,output,EMPTY);}));// 定義執(zhí)行順序START - node1 - node2 - ENDgraph.addEdge(StateGraph.START,node1).addEdge(node1,node2).addEdge(node2,StateGraph.END);returngraph.compile();}}StateGraph描述節(jié)點、邊和狀態(tài)合并策略AsyncNodeAction封裝每個節(jié)點的執(zhí)行邏輯compile()得到可執(zhí)行的CompiledGraph。7.2 調(diào)用 GraphWebFlux 流式輸出GraphControllerRestControllerRequestMapping(/v1)publicclassGraphController{ResourceprivateCompiledGraphquickStartGraph;GetMapping(/graph)publicFluxStringstartGraph(){// 這里傳入空的初始狀態(tài) Map按定義好的 StateGraph 順序執(zhí)行returnquickStartGraph.stream(Map.of()).map(NodeOutput::toString);// 將每個節(jié)點的輸出對象轉(zhuǎn)換為字符串返回}}使用 WebFlux FluxNodeOutput將節(jié)點執(zhí)行結(jié)果流式返回通過RunnableConfig.builder().threadId(conversationId)還可以實現(xiàn)“帶會話 ID 的工作流”類似有狀態(tài) Agent。7.3 quickStartGraph 執(zhí)行流程圖結(jié)合上面的GraphConfig和GraphController/v1/graph接口整體執(zhí)行流程可以用下面這張流程圖來表示以 GitHub 為例可以直接渲染 MermaidHTTP 請求GET /alibaba-graph/v1/graphGraphController.startGraph()CompiledGraph.stream(Map.of())StateGraph.START節(jié)點 node1input graphConfig_addNodeoutput graphConfig_output節(jié)點 node2input ZhouByteoutput EMPTYStateGraph.ENDFlux 流式返回從GraphController.startGraph()開始調(diào)用CompiledGraph.stream(Map.of())啟動圖的執(zhí)行圖從StateGraph.START出發(fā)依次流經(jīng)node1、node2最終到達StateGraph.END每個節(jié)點都會向全局狀態(tài)寫入input/output等字段并以FluxNodeOutput的形式逐步返回給調(diào)用方。7.4 多條件分支 Graph 示例addConditionalEdges在實際業(yè)務中Graph 往往不只是線性順序還會根據(jù)狀態(tài)進行分支判斷。spring-ai-alibaba-graph-core提供了addConditionalEdges可以基于當前OverAllState計算「條件標簽」再根據(jù)標簽跳轉(zhuǎn)到不同節(jié)點。下面是一個簡化的「評分決策」示例根據(jù)score分數(shù)分別走向通過 / 復核 / 拒絕三條路徑ConfigurationpublicclassConditionalGraphConfig{Bean(scoreDecisionGraph)publicCompiledGraphscoreDecisionGraph()throwsGraphStateException{StateGraphgraphnewStateGraph(scoreDecisionGraph,()-Map.of(score,newReplaceStrategy(),// 保存當前評分result,newReplaceStrategy()// 保存決策結(jié)果));// 讀取或設置評分示例中從 state 中讀取實際可由外部請求傳入graph.addNode(checkScore,AsyncNodeAction.node_async(state-{Integerscore(Integer)state.value(score).orElse(75);// 默認 75 分returnMap.of(score,score);}));// 三個業(yè)務分支節(jié)點通過 / 復核 / 拒絕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)));// 起點先進入評分檢查節(jié)點graph.addEdge(StateGraph.START,checkScore);// 多條件邊根據(jù) score 返回不同的“標簽”再由 mappings 決定下一跳節(jié)點graph.addConditionalEdges(checkScore,AsyncEdgeAction.edge_async(state-{intscore(Integer)state.value(score).orElse(0);if(score80){returnPASS;}if(score60){returnREVIEW;}returnREJECT;}),Map.of(PASS,pass,REVIEW,review,REJECT,reject));// 三個結(jié)果節(jié)點最終都指向 ENDgraph.addEdge(pass,StateGraph.END);graph.addEdge(review,StateGraph.END);graph.addEdge(reject,StateGraph.END);returngraph.compile();}}這段代碼中addConditionalEdges的三個參數(shù)含義是sourceId條件邊的源節(jié)點 ID這里是checkScoreAsyncEdgeAction根據(jù)當前OverAllState計算條件標簽這里返回PASS/REVIEW/REJECTmappings標簽與目標節(jié)點 ID 的映射例如PASS - pass即當標簽為PASS時跳到pass節(jié)點。對應的執(zhí)行流程可以畫成如下多分支流程圖在真實項目中你可以把score換成「風控評分」「召回結(jié)果命中情況」「用戶畫像標簽」等任意業(yè)務信號通過addConditionalEdges把復雜分支邏輯從代碼 if/else 中抽離出來統(tǒng)一放在 Graph 層管理。在這個子項目中Graph 本身是“流程層”可以在節(jié)點里調(diào)用 Spring AI / Spring AI Alibaba 的各種模型與工具實現(xiàn)復雜的多步推理與業(yè)務編排。八、如何從本地 Ollama 平滑切到阿里云 DashScope雖然當前 Demo 主要跑在本地 Ollama 上但由于使用了 Spring AI Spring AI Alibaba 的統(tǒng)一抽象切換到阿里云 DashScope 十分簡單在pom.xml中啟用 DashScope Starter示例中已給出注釋代碼dependencygroupIdcom.alibaba.cloud.ai/groupIdartifactIdspring-ai-alibaba-starter-dashscope/artifactIdversion1.1.0.0-M5/version/dependency在配置文件中增加 DashScope 的配置示例spring:ai:dashscope:api-key:${DASHSCOPE_API_KEY}# 從環(huán)境變量或配置中心讀取 DashScope 的 API Keyendpoint:https://dashscope.aliyuncs.comchat:options:model:qwen-plus# 使用的通義千問在線模型temperature:0.8# 采樣溫度max-tokens:2048# 單次回答的最大 token 數(shù)將原來的ChatModel注入點從 Ollama 替換為 DashScope 對應的 Bean通常只需要調(diào)整配置不改業(yè)務代碼。憑借 Spring AI 的抽象層你可以開發(fā)階段本地跑 Qwen3Ollama成本低、調(diào)試快生產(chǎn)階段切到云上 DashScopeQwen-Max / Qwen-Plus 等享受更強算力和更高可用性中長期在 Spring AI Alibaba 的生態(tài)內(nèi)同時兼容多家國內(nèi)模型廠商。九、實踐建議與最佳實踐配置管理API Key 使用環(huán)境變量或配置中心Nacos、KMS 等避免硬編碼Ollama、DashScope 的模型名稱、溫度等參數(shù)盡量抽到配置文件中。錯誤處理與重試針對網(wǎng)絡異常、超時、限流等場景做兜底和重試策略對外暴露的接口統(tǒng)一封裝錯誤返回避免直接把底層錯誤拋給前端。性能與成本在高并發(fā)場景建議優(yōu)先使用流式輸出 前端增量渲染RAG 中控制 TopK、相似度閾值和切分策略避免向量庫“爆炸”。代碼結(jié)構將ChatClient配置、Graph 配置等放在獨立的config包中業(yè)務層只關心接口調(diào)用工具方法使用Tool暴露便于模型統(tǒng)一管理和調(diào)用。版本與升級Spring AI Alibaba 當前仍以 Milestone 版本為主如1.1.0.0-M5升級前建議閱讀 release notes保持對spring-ai-bom/spring-ai-alibaba-bom的依賴讓升級盡量在 BOM 層完成。十、總結(jié)與展望基于spring_ai_alibaba-demo子項目我們實際體驗了一次如何用Spring AISpring AI Alibaba BOM快速接入本地 Qwen3Ollama如何在同一套抽象下串聯(lián)起對話、記憶、RAG、工具調(diào)用如何通過spring-ai-alibaba-graph-core構建基于大模型的有狀態(tài)工作流以及如何在不改業(yè)務代碼的前提下為未來切換到阿里云 DashScope 留出空間。對 Spring 開發(fā)者來說這套體系最大的價值在于統(tǒng)一抽象不同模型供應商之間切換成本極低生態(tài)完善兼容 Spring Boot、WebFlux、向量庫、MCP、Graph 等豐富組件本地 云端雙模既能在本地快速迭代又能無縫遷移到云上生產(chǎn)環(huán)境。再次附上示例子項目 GitHub 地址歡迎你親手跑一跑代碼、提 Issue、點 StarGitHub 項目地址https://github.com/zhouByte-hub/java-ai/tree/main/spring_ai_alibaba-demo