戰(zhàn):Spring AI 2.0 + DeepSeek + LangChain4j 保姆級(jí)教程)
如果你是一個(gè) Java 后端開發(fā)2026 年無(wú)論如何都繞不開這三個(gè)名字Spring AI、LangChain4j、DeepSeek。Spring AI 2.0 把 LLM 接入做成了經(jīng)典的 Spring 風(fēng)格LangChain4j 在 Java 生態(tài)里提供了類似 LangChain 的編排能力DeepSeek 則把大模型 API 的成本和效果拉到了非常有競(jìng)爭(zhēng)力的位置。這篇文章不是概念科普而是一套可以照著敲的保姆級(jí)流程從創(chuàng)建 Spring Boot 項(xiàng)目、接入 DeepSeek API到結(jié)構(gòu)化輸出、RAG 向量檢索、批量任務(wù)和接口化全部跑通。默認(rèn)你的環(huán)境是 JDK 17、Spring Boot 3.x用 Maven 管理依賴。DeepSeek 使用云端 API不需要本地顯卡所以顯存、CUDA 這些在這個(gè)教程里不是門檻如果你要在本地跑 Qwen 或 DeepSeek 的蒸餾模型那才需要關(guān)注 Ollama 和顯存占用。文章會(huì)把云端 API 和本地模型兩條路徑都說(shuō)清楚你按自己的場(chǎng)景選。另外Spring AI Alibaba 值得單獨(dú)拿出來(lái)看。它對(duì) Qwen 系模型、DashScope、以及 Graph 圖編排提供了更完整的支持很多企業(yè)項(xiàng)目在 Spring AI 基礎(chǔ)上直接疊加它來(lái)做私有化 AI 應(yīng)用。下面按照實(shí)際開發(fā)中最常用的接入方式展開每一步都可以直接復(fù)制到你的項(xiàng)目里驗(yàn)證。1. 核心能力速覽能力項(xiàng)說(shuō)明項(xiàng)目類型Java 生態(tài)大模型應(yīng)用開發(fā)框架核心功能對(duì)話、流式輸出、結(jié)構(gòu)化輸出、RAG、向量存儲(chǔ)、Tool Calling、Agent 編排模型接入DeepSeek API、OpenAI 兼容接口、Ollama 本地模型、Qwen/DashScope主要組件Spring AI 2.0、LangChain4j、Spring AI Alibaba推薦環(huán)境JDK 17、Spring Boot 3.x、Maven 或 Gradle啟動(dòng)方式Spring Boot 標(biāo)準(zhǔn)啟動(dòng)內(nèi)嵌 Tomcat接口能力支持將 AI 能力封裝為 REST API供前端、移動(dòng)端或外部系統(tǒng)調(diào)用批量任務(wù)支持異步批處理、任務(wù)隊(duì)列、失敗重試向量庫(kù)支持 Milvus、Elasticsearch、Redis、PGVector 等是否支持本地部署支持可通過(guò) Ollama 部署 Qwen 或 DeepSeek 蒸餾模型適合場(chǎng)景Java 后端接入大模型、私有知識(shí)庫(kù)、智能客服、AI 應(yīng)用服務(wù)化從這張表格能看出來(lái)這套組合解決的不是“怎么調(diào)一個(gè)模型接口”的問(wèn)題而是“怎么把大模型能力工程化地放進(jìn) Java 后端系統(tǒng)”的問(wèn)題。如果你之前只用 Python 寫過(guò) AI 腳本Spring AI 2.0 會(huì)給你一套更貼近企業(yè)項(xiàng)目習(xí)慣的寫法。2. 技術(shù)棧分工Spring AI、LangChain4j、DeepSeek、Spring AI Alibaba 各管什么很多初學(xué)者容易把這四個(gè)概念混在一起。先理清分工后面寫代碼才不會(huì)亂。Spring AI 是 Spring 官方推出的 AI 框架目標(biāo)是讓 Java 開發(fā)者用最小的成本接入大模型。它的核心抽象是ChatClient、EmbeddingModel、VectorStore、ToolCallback等只要配置好模型提供方業(yè)務(wù)代碼基本不用改。Spring AI 2.0 相比 1.x 更強(qiáng)調(diào)模塊化模型接入、向量數(shù)據(jù)庫(kù)、Agent 編排被拆分得更清楚同時(shí)兼容了大量主流模型廠商。LangChain4j 是 Java 生態(tài)里的 LLM 編排框架設(shè)計(jì)靈感來(lái)自 Python 的 LangChain。它擅長(zhǎng)做對(duì)話記憶管理、結(jié)構(gòu)化輸出、RAG、Tool Calling 和 Agent 流程編排。LangChain4j 和 Spring AI 不是對(duì)立關(guān)系兩者在功能上有重疊但在工程集成上各有優(yōu)勢(shì)。Spring AI 更“Spring 原生”適合深度使用 Spring Boot 的項(xiàng)目LangChain4j 更靈活RAG 和 Agent 示例也更豐富。實(shí)際項(xiàng)目里有人只用其中一個(gè)也有人在一個(gè)系統(tǒng)里同時(shí)引入兩者分別承擔(dān)不同模塊。DeepSeek 在這套組合里是模型提供方。DeepSeek 的 API 兼容 OpenAI 協(xié)議這意味著 Spring AI 和 LangChain4j 里現(xiàn)成的 OpenAI 客戶端稍作配置就能對(duì)接。常用的模型名是deepseek-chat和deepseek-reasoner前者適合通用對(duì)話后者支持思考模式但調(diào)用時(shí)要注意處理reasoning_content字段。Spring AI Alibaba 是阿里開源的項(xiàng)目基于 Spring AI 做了大量擴(kuò)展。它的價(jià)值主要有三點(diǎn)第一對(duì) Qwen 通義千問(wèn)系列模型的接入做了封裝包括文本生成、Embedding、語(yǔ)音等第二提供 DashScope 平臺(tái)的適配企業(yè)如果已經(jīng)用阿里云百煉可以直接對(duì)接第三提供了 Graph 圖編排模塊可以用節(jié)點(diǎn)和邊的方式設(shè)計(jì) AI 工作流相當(dāng)于 Java 版的輕量 LangGraph。一句話總結(jié)分工Spring AI 2.0 是主框架LangChain4j 是增強(qiáng)型工具集DeepSeek 是背后的模型引擎Spring AI Alibaba 負(fù)責(zé)把阿里系能力補(bǔ)齊。四個(gè)組件可以組合使用也可以按需取舍。3. 環(huán)境準(zhǔn)備與前置條件在動(dòng)手前先把環(huán)境檢查一遍。以下是這套教程的最小環(huán)境清單每一項(xiàng)如果不滿足后面跑起來(lái)會(huì)出現(xiàn)各種奇怪問(wèn)題。檢查項(xiàng)要求說(shuō)明JDK17 及以上Spring Boot 3.x 強(qiáng)制要求 JDK 17Spring Boot3.2 及以上更高版本兼容性更好推薦 3.3構(gòu)建工具M(jìn)aven 3.6 或 Gradle 7.5本文示例使用 MavenDeepSeek API Key必選在 DeepSeek 開放平臺(tái)創(chuàng)建充值和開通模型服務(wù)網(wǎng)絡(luò)能訪問(wèn) DeepSeek API國(guó)內(nèi)網(wǎng)絡(luò)可以直接訪問(wèn)無(wú)需額外手段可選組件Milvus、Elasticsearch、Ollama只有做 RAG 或本地模型時(shí)才需要磁盤空間2GB 左右主要是 Maven 依賴和日志若本地跑 Ollama 模型需額外預(yù)留 10GBDeepSeek API Key 的申請(qǐng)路徑很簡(jiǎn)單打開 DeepSeek 開放平臺(tái)完成注冊(cè)進(jìn)入控制臺(tái)創(chuàng)建 API Key然后把 Key 保存到本地環(huán)境變量或配置文件中。注意 API Key 只在創(chuàng)建時(shí)完整展示一次之后無(wú)法再次查看只能重新創(chuàng)建。如果你打算在本地跑 Ollama 模型還要提前安裝 Ollama 客戶端并下載對(duì)應(yīng)的模型比如qwen2.5或 DeepSeek 蒸餾版本。顯存占用取決于模型大小7B 級(jí)別模型通常需要 6GB 左右顯存小參數(shù)模型可以用 CPU 跑但速度會(huì)明顯慢。這里不展開具體數(shù)字以你本機(jī)實(shí)際測(cè)試為準(zhǔn)。如果你的目標(biāo)是做 RAG需要準(zhǔn)備向量數(shù)據(jù)庫(kù)。Milvus 是比較流行的選擇本地可以用 Docker 快速起一個(gè)單機(jī)版Elasticsearch 適合已經(jīng)在用 ES 做搜索的公司可以直接把向量索引和業(yè)務(wù)索引統(tǒng)一管理。兩種方案后面我都會(huì)給出接入思路。4. 搭建項(xiàng)目并接入 DeepSeek4.1 創(chuàng)建 Spring Boot 項(xiàng)目最簡(jiǎn)單的方式是去 Spring Initializr 生成一個(gè)基礎(chǔ)項(xiàng)目也可以直接用 IDE 創(chuàng)建。關(guān)鍵點(diǎn)是勾選 web 依賴語(yǔ)言選擇 JavaBoot 版本選擇 3.x。4.2 引入 Spring AI 相關(guān)依賴DeepSeek 兼容 OpenAI 協(xié)議所以 Spring AI 側(cè)不需要單獨(dú)的 DeepSeek starter直接使用 OpenAI 模塊把 base-url 指向 DeepSeek 即可。先引入 BOM 管理版本再引入具體模塊。properties java.version17/java.version spring-ai.version2.0.0/spring-ai.version /properties dependencyManagement dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-bom/artifactId version${spring-ai.version}/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-openai/artifactId /dependency /dependencies上面的spring-ai.version是我給的一個(gè)示例值實(shí)際使用時(shí)請(qǐng)去 Maven 中央倉(cāng)庫(kù)查一下當(dāng)前最新穩(wěn)定版本替換成真實(shí)版本號(hào)。不要把 2.0.0 當(dāng)成固定結(jié)論Spring AI 迭代很快版本之間可能存在 API 差異。如果要用 LangChain4j可以額外引入它的核心包和 OpenAI 模塊。這里建議先跑通 Spring AI再疊加 LangChain4j避免一開始兩個(gè)框架的配置互相干擾。dependency groupIddev.langchain4j/groupId artifactIdlangchain4j/artifactId version${langchain4j.version}/version /dependency dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-open-ai/artifactId version${langchain4j.version}/version /dependencylangchain4j.version同樣需要替換為實(shí)際最新版本。4.3 配置 DeepSeek 連接在application.yml中加入以下配置spring: application: name: spring-ai-demo ai: openai: base-url: https://api.deepseek.com api-key: ${DEEPSEEK_API_KEY} chat: options: model: deepseek-chat temperature: 0.7DEEPSEEK_API_KEY建議通過(guò)環(huán)境變量注入不要硬編碼在配置文件中。如果你的 DeepSeek 賬號(hào)支持/v1路徑也可以把 base-url 寫成https://api.deepseek.com/v1兩種寫法對(duì) OpenAI 兼容客戶端來(lái)說(shuō)通常都能生效。4.4 寫第一個(gè)對(duì)話接口Spring AI 2.0 的核心對(duì)象是ChatClient。在配置類里注入ChatClient.Builder然后構(gòu)建一個(gè)全局的ChatClient實(shí)例。package com.example.demo; import org.springframework.ai.chat.client.ChatClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; Configuration public class AIConfig { Bean public ChatClient chatClient(ChatClient.Builder builder) { return builder.build(); } }接著寫一個(gè) REST 接口把對(duì)話能力暴露出去。package com.example.demo; import org.springframework.ai.chat.client.ChatClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; RestController public class ChatController { private final ChatClient chatClient; public ChatController(ChatClient chatClient) { this.chatClient chatClient; } GetMapping(/chat) public String chat(RequestParam(defaultValue 你好請(qǐng)介紹一下你自己) String message) { return chatClient.prompt(message) .call() .content(); } }啟動(dòng)項(xiàng)目后訪問(wèn)http://127.0.0.1:8080/chat?message你好如果返回一段正常的中文回復(fù)說(shuō)明 Spring AI 2.0 到 DeepSeek 的鏈路已經(jīng)通了。這是整個(gè)教程的“地基”后面所有功能都在這個(gè)基礎(chǔ)上擴(kuò)展。5. 功能測(cè)試對(duì)話、流式輸出與結(jié)構(gòu)化輸出5.1 流式輸出普通接口一次返回全部?jī)?nèi)容適合內(nèi)部工具但做智能客服或前端對(duì)話框時(shí)流式輸出體驗(yàn)更好。Spring AI 的流式輸出返回FluxString前端可以用 SSE 方式接收。import org.springframework.ai.chat.client.ChatClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Flux; RestController public class StreamChatController { private final ChatClient chatClient; public StreamChatController(ChatClient chatClient) { this.chatClient chatClient; } GetMapping(/chat/stream) public FluxString streamChat(RequestParam String message) { return chatClient.prompt(message) .stream() .content(); } }Vue 前端做對(duì)話頁(yè)面時(shí)可以用EventSource或fetch配合ReadableStream接收流式數(shù)據(jù)。這里給一個(gè)簡(jiǎn)單的 fetch 思路具體封裝方式看你的前端框架。const response await fetch(/chat/stream?message encodeURIComponent(text)); const reader response.body.getReader(); const decoder new TextDecoder(utf-8); let result ; while (true) { const { done, value } await reader.read(); if (done) break; result decoder.decode(value, { stream: true }); }5.2 多輪對(duì)話與上下文數(shù)量限制大模型本身是無(wú)狀態(tài)的多輪對(duì)話需要手動(dòng)把歷史消息傳給模型。Spring AI 里有兩個(gè)方案一是自己維護(hù)消息列表二是使用內(nèi)置的ChatMemory。用內(nèi)置方案時(shí)可以限制上下文數(shù)量避免歷史消息無(wú)限增長(zhǎng)導(dǎo)致 token 費(fèi)用過(guò)高。import org.springframework.ai.chat.memory.MessageWindowChatMemory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; Configuration public class ChatMemoryConfig { Bean public MessageWindowChatMemory chatMemory() { return MessageWindowChatMemory.builder() .maxMessages(20) .build(); } }這里的maxMessages(20)就是控制上下文窗口條數(shù)。20 條是一個(gè)保守值具體要看你用的模型上下文長(zhǎng)度。如果業(yè)務(wù)場(chǎng)景需要更長(zhǎng)的記憶可以調(diào)大這個(gè)值但要注意 token 成本會(huì)隨之上升。5.3 結(jié)構(gòu)化輸出默認(rèn)情況下模型返回的是純文本但我們經(jīng)常需要把輸出綁定到實(shí)體類上比如解析一本書的信息、抽取一篇文章的標(biāo)題和作者。Spring AI 支持把回復(fù)直接映射到 Java 對(duì)象。先定義一個(gè)實(shí)體類package com.example.demo; public record BookInfo( String title, String author, String category, String summary ) { }然后在調(diào)用時(shí)指定目標(biāo)類型import org.springframework.ai.chat.client.ChatClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; RestController public class BookController { private final ChatClient chatClient; public BookController(ChatClient chatClient) { this.chatClient chatClient; } GetMapping(/parse-book) public BookInfo parseBook(RequestParam String text) { return chatClient.prompt(請(qǐng)從下面的文本中抽取書籍信息返回 JSON 格式 text) .call() .entity(BookInfo.class); } }結(jié)構(gòu)化輸出的重點(diǎn)在于提示詞要給出明確的格式約束實(shí)體類字段名最好用英文并且加說(shuō)明性注釋這樣模型的召回率更高。如果返回結(jié)果經(jīng)常解析失敗檢查兩個(gè)方向一是實(shí)體類字段是否過(guò)于復(fù)雜且語(yǔ)義模糊二是在提示詞中補(bǔ)充“只返回 JSON不要解釋”之類的約束。LangChain4j 同樣支持結(jié)構(gòu)化輸出并且對(duì)復(fù)雜嵌套對(duì)象的容錯(cuò)性更好后面章節(jié)會(huì)提到。5.4 DeepSeek 思考模式的坑reasoning_content 必須原樣回傳如果你使用deepseek-reasoner模型會(huì)在響應(yīng)里多出一個(gè)reasoning_content字段代表模型內(nèi)部的思考過(guò)程。這是 DeepSeek 的一個(gè)特色但也容易踩坑。在多輪對(duì)話時(shí)如果直接把content拼到歷史消息里把reasoning_content丟了下一次請(qǐng)求可能收到 400 錯(cuò)誤提示思考模式下的reasoning_content必須傳回 API。解決方式是把reasoning_content保存到 assistant 消息中下一輪原樣帶回。在 Spring AI 中可以構(gòu)造一個(gè)通用的消息轉(zhuǎn)換工具import java.util.HashMap; import java.util.Map; public class DeepSeekMessageBuilder { public static MapString, Object assistantMessageWithReasoning(String content, String reasoningContent) { MapString, Object message new HashMap(); message.put(role, assistant); message.put(content, content); if (reasoningContent ! null) { message.put(reasoning_content, reasoningContent); } return message; } }核心原則是reasoning_content和content必須綁定在同一條 assistant 消息里回傳不能拆開也不能省略。這屬于 DeepSeek 協(xié)議層的行為不管用 Spring AI、LangChain4j 還是直接用 HTTP 客戶端調(diào)用都要遵守。6. RAG 實(shí)戰(zhàn)向量存儲(chǔ)、ES 覆蓋策略、Milvus 混合檢索與重排6.1 為什么需要 RAG大模型的知識(shí)截止時(shí)間有限企業(yè)內(nèi)部資料也無(wú)法靠訓(xùn)練塞進(jìn)模型。RAG檢索增強(qiáng)生成的思路是先把文檔切塊、向量化存進(jìn)向量數(shù)據(jù)庫(kù)用戶提問(wèn)時(shí)先檢索最相關(guān)的片段和問(wèn)題一起送給模型回答。這樣既控制成本又能保證最新文檔被回答到。6.2 Qwen Embedding 接入向量化這一步通常用專門的 Embedding 模型。這里以阿里云百煉 DashScope 的text-embedding-v3為例在 Spring AI Alibaba 體系里可以像配置 Chat 模型一樣配置 Embedding 模型。dependency groupIdcom.alibaba.cloud.ai/groupId artifactIdspring-ai-alibaba-starter/artifactId version${spring-ai-alibaba.version}/version /dependencyspring: ai: dashscope: api-key: ${DASHSCOPE_API_KEY} embedding: options: model: text-embedding-v3從材料看這是實(shí)際項(xiàng)目里比較常見的接入路徑。如果你沒有阿里云百煉的 Key也可以使用本地 Ollama 的 embedding 模型例如nomic-embed-text只是召回效果和延遲會(huì)有差異。6.3 文檔寫入 Milvus混合檢索加 RerankLangChain4j 提供了 Milvus 向量存儲(chǔ)實(shí)現(xiàn)基礎(chǔ)用法如下import dev.langchain4j.store.embedding.milvus.MilvusEmbeddingStore; MilvusEmbeddingStore embeddingStore MilvusEmbeddingStore.builder() .host(127.0.0.1) .port(19530) .collectionName(java_knowledge) .dimension(1024) .build();dimension必須和 Embedding 模型輸出維度一致不同模型維度不同寫錯(cuò)了寫入時(shí)就會(huì)報(bào)錯(cuò)。寫入文檔后查詢時(shí)可以做混合檢索同時(shí)用關(guān)鍵詞和向量相似度召回再用 Rerank 模型對(duì)結(jié)果重排把最相關(guān)的片段排到前面這能明顯提升 RAG 答案質(zhì)量。重排階段如果使用 DashScope 服務(wù)注意在查詢鏈路里增加重排 API 調(diào)用并把 TopK 結(jié)果截?cái)嗪笤偎瓦M(jìn) Prompt?;旌蠙z索加重的流程本身不復(fù)雜但每一步的參數(shù)都需要觀察實(shí)際返回結(jié)果來(lái)調(diào)整不是配好就能永遠(yuǎn)最優(yōu)。6.4 ES 向量存儲(chǔ)重復(fù)文檔怎么覆蓋用 Spring AI 把文檔向量化后寫入 Elasticsearch 時(shí)很容易遇到一個(gè)現(xiàn)象同一份 PDF 重復(fù)執(zhí)行導(dǎo)入任務(wù)ES 里會(huì)出現(xiàn)多條重復(fù)記錄。原因在于 Spring AI 默認(rèn)寫入文檔時(shí)如果沒有指定穩(wěn)定 id每次都會(huì)生成一個(gè)新的 UUID重復(fù)導(dǎo)入自然產(chǎn)生新記錄。解決辦法是在寫入前給文檔設(shè)置穩(wěn)定的業(yè)務(wù) id比如用文件路徑、文檔編號(hào)或內(nèi)容哈希。Spring AI 的Document構(gòu)造器允許傳入 idimport org.springframework.ai.document.Document; import java.util.List; public class DocumentService { public ListDocument buildDocs(ListString chunks) { return chunks.stream() .map(chunk - new Document(doc- Integer.toHexString(chunk.hashCode()), chunk)) .toList(); } }當(dāng)同一個(gè) id 再次寫入時(shí)ES 向量庫(kù)會(huì)按 id 執(zhí)行 upsert 語(yǔ)義覆蓋舊文檔而不是追加新文檔。如果某個(gè) id 對(duì)應(yīng)的內(nèi)容已經(jīng)不存在還需要主動(dòng)刪除舊向量避免臟數(shù)據(jù)殘留。另一種更穩(wěn)妥的方案是在批量任務(wù)開頭先按業(yè)務(wù)標(biāo)記刪除本批次對(duì)應(yīng)的舊文檔再執(zhí)行寫入。比如每個(gè)文檔寫入時(shí) metadata 里保存batchId清理時(shí)按batchId批量刪除。這個(gè)策略適合定時(shí)重建知識(shí)庫(kù)的場(chǎng)景。7. 接口 API 化與批量任務(wù)7.1 封裝 REST API前面已經(jīng)寫了一個(gè)/chat接口生產(chǎn)環(huán)境通常還會(huì)加上對(duì)話記錄持久化、用戶維度上下文隔離、token 用量日志。這里給出一個(gè)相對(duì)完整的接口示例查詢參數(shù)和返回結(jié)構(gòu)可以按你公司規(guī)范調(diào)整。RestController public class ChatApiController { private final ChatClient chatClient; public ChatApiController(ChatClient chatClient) { this.chatClient chatClient; } PostMapping(/api/chat) public ChatResponse chat(RequestBody ChatRequest request) { String answer chatClient.prompt(request.messages()) .call() .content(); return new ChatResponse(answer, request.sessionId()); } public record ChatRequest(String sessionId, String message) {} public record ChatResponse(String answer, String sessionId) {} }前端 Vue 項(xiàng)目可以對(duì)接這個(gè)接口也可以對(duì)接前面的流式接口。建議流式接口給終端用戶非流式接口給內(nèi)部系統(tǒng)做異步處理。7.2 批量任務(wù)示例批量任務(wù)常見于文檔解析、商品文案生成、評(píng)論分類等場(chǎng)景。原則是不要在主線程里同步循環(huán)調(diào)用模型那樣既慢又容易觸發(fā) API 限流。正確做法是異步提交 任務(wù)隊(duì)列 失敗重試。import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; Service public class BatchAIService { private final ChatClient chatClient; public BatchAIService(ChatClient chatClient) { this.chatClient chatClient; } Async(aiTaskExecutor) public CompletableFutureString processOne(String prompt) { try { String result chatClient.prompt(prompt).call().content(); return CompletableFuture.completedFuture(result); } catch (Exception e) { return CompletableFuture.failedFuture(e); } } public ListCompletableFutureString processBatch(ListString prompts) { ListCompletableFutureString futures new ArrayList(); for (String prompt : prompts) { futures.add(processOne(prompt)); } return futures; } }線程池建議單獨(dú)配置不要把模型調(diào)用塞進(jìn) Tomcat 的工作線程。線程池大小可以根據(jù)模型 API 的并發(fā)限制來(lái)調(diào)整如果 DeepSeek 并發(fā)額度不高線程數(shù)過(guò)大只會(huì)增加堆積和超時(shí)。8. 資源占用與性能觀察這套組合的性能觀察點(diǎn)和 Python 本地模型不同重點(diǎn)不是顯存而是網(wǎng)絡(luò)、超時(shí)、并發(fā)、token 用量。如果你只用 DeepSeek 云端 API本機(jī)不跑任何模型那么資源占用主要是 JVM 內(nèi)存和少量網(wǎng)絡(luò) IO普通開發(fā)機(jī)完全扛得住。需要在監(jiān)控面板里重點(diǎn)觀察的是API 響應(yīng)延遲DeepSeek 首次 token 時(shí)間是否穩(wěn)定高峰期是否明顯變慢。超時(shí)配置默認(rèn) HTTP 超時(shí)在慢網(wǎng)絡(luò)下容易觸發(fā) SocketTimeoutException建議把連接超時(shí)設(shè)置為 10 秒到 30 秒之間。并發(fā)控制同一賬號(hào)并發(fā)過(guò)高會(huì)觸發(fā)限流返回 429 狀態(tài)碼需要在代碼里做重試和退避。token 用量每次請(qǐng)求的輸入 token 和輸出 token 都建議記錄到日志或數(shù)據(jù)庫(kù)月底對(duì)賬和成本評(píng)估都靠它。如果你在本地用 Ollama 跑 Qwen 或 DeepSeek 蒸餾模型那么資源和顯存占用才是重點(diǎn)。模型加載后顯存會(huì)持續(xù)占用7B 模型大約需要 6GB 左右顯存量化版本會(huì)低一些。降低顯存占用的常見手段包括使用量化模型、關(guān)閉不用的模型、降低上下文長(zhǎng)度、限制并發(fā)數(shù)。在 Ollama 里可以通過(guò)環(huán)境變量控制模型常駐策略具體以 Ollama 文檔為準(zhǔn)。下面是兩個(gè)常用指標(biāo)采集點(diǎn)指標(biāo)采集方式用途調(diào)用耗時(shí)在 ChatClient 調(diào)用前后記時(shí)判斷模型響應(yīng)是否穩(wěn)定token 消耗從響應(yīng)對(duì)象中讀取 usage 信息成本統(tǒng)計(jì)和限流策略429 重試次數(shù)在重試攔截器中累積計(jì)數(shù)判斷并發(fā)額度是否充足JVM 內(nèi)存Spring Boot Actuator Prometheus防止內(nèi)存泄漏9. 常見問(wèn)題與排查方法問(wèn)題現(xiàn)象可能原因排查方式解決方案調(diào)用 DeepSeek 返回 400提示思考模式 reasoning_content 未回傳多輪對(duì)話丟棄了 thinking 內(nèi)容查看請(qǐng)求日志中 assistant 消息結(jié)構(gòu)把 reasoning_content 拼回 assistant 消息后重試啟動(dòng)后接口一直超時(shí)base-url 配置錯(cuò)誤或網(wǎng)絡(luò)不通用 curl 直接測(cè)試 DeepSeek API確認(rèn) base-url 和 api-key 是否正確對(duì)話結(jié)果不穩(wěn)定偶爾返回空內(nèi)容模型參數(shù)配置或提示詞約束不夠查看完整響應(yīng)日志調(diào)整 temperature增強(qiáng)提示詞約束重復(fù)導(dǎo)入文檔后 ES 記錄越來(lái)越多文檔未設(shè)置穩(wěn)定 id檢查 Document id 生成邏輯使用業(yè)務(wù) id 并配合刪除舊批次RAG 檢索結(jié)果相關(guān)度差向量維度不匹配或缺少重排檢查 embedding 維度打印召回結(jié)果校準(zhǔn)維度加入 rerank 環(huán)節(jié)批量任務(wù)跑到一半卡住并發(fā)過(guò)高觸發(fā) API 限流查看 429 響應(yīng)統(tǒng)計(jì)降低線程池并發(fā)增加退避重試JDK 版本過(guò)低導(dǎo)致依賴沖突JDK 8 無(wú)法運(yùn)行 Spring Boot 3執(zhí)行 java -version 查看版本升級(jí)到 JDK 17LangChain4j 和 Spring AI 同時(shí)使用時(shí) Bean 沖突兩個(gè)框架都掃描了 OpenAI 客戶端查看啟動(dòng)日志的 Bean 沖突提示配置不同的包掃描路徑或排除自動(dòng)配置10. 最佳實(shí)踐與合規(guī)建議第一API Key 全部走環(huán)境變量或配置中心不要提交到 Git 倉(cāng)庫(kù)。一旦泄露立刻去平臺(tái)刪除重建。DeepSeek 開放平臺(tái)的后臺(tái)可以查看用量建議設(shè)置額度告警防止異常調(diào)用導(dǎo)致費(fèi)用飛漲。第二批量任務(wù)一定要有日志和任務(wù)表。每次任務(wù)的輸入、輸出、耗時(shí)、token 數(shù)記錄清楚失敗任務(wù)要有重試機(jī)制。批量跑文檔解析時(shí)建議先跑 5 條樣本驗(yàn)證效果再放開全部任務(wù)避免大批量失敗后回滾困難。第三涉及 RAG 的文檔導(dǎo)入必須考慮數(shù)據(jù)版本管理。文檔更新后舊版本向量要及時(shí)清理或覆蓋。不要長(zhǎng)期堆積無(wú)主數(shù)據(jù)否則檢索結(jié)果會(huì)越來(lái)越差最終影響回答準(zhǔn)確性。第四合規(guī)邊界要重視。如果你的業(yè)務(wù)涉及用戶上傳的文檔、圖片、錄音或者要處理他人的人臉、聲音、版權(quán)內(nèi)容必須確認(rèn)有合法授權(quán)。企業(yè)內(nèi)部知識(shí)庫(kù)接入 AI 時(shí)要評(píng)估數(shù)據(jù)是否適合發(fā)送到第三方模型 API敏感數(shù)據(jù)建議本地部署模型或做脫敏處理。部署測(cè)試環(huán)境時(shí)先用脫敏數(shù)據(jù)驗(yàn)證不要直接把生產(chǎn)數(shù)據(jù)導(dǎo)進(jìn)去試。第五任何一個(gè) AI 功能上線前準(zhǔn)備一套固定的驗(yàn)收用例。包括普通問(wèn)答、多輪連續(xù)性、長(zhǎng)文本、異常輸入、空輸入、重復(fù)提交等場(chǎng)景。模型輸出有隨機(jī)性不能依賴一次測(cè)試通過(guò)就認(rèn)定穩(wěn)定至少跑三到五次觀察結(jié)果波動(dòng)。11. 總結(jié)與后續(xù)方向這套組合最值得嘗試的點(diǎn)是把大模型能力變成 Java 項(xiàng)目里的普通依賴從配置 DeepSeek 到跑通對(duì)話接口只需要幾十分鐘。接下來(lái)優(yōu)先驗(yàn)證這幾個(gè)功能流式對(duì)話是否能穩(wěn)定推送、結(jié)構(gòu)化輸出解析是否準(zhǔn)確、RAG 檢索到你自己的文檔時(shí)回答是否靠譜。最容易踩的坑有三個(gè)DeepSeek 思考模式下忘記回傳reasoning_content、ES 寫入時(shí)沒設(shè)置文檔 id 導(dǎo)致重復(fù)數(shù)據(jù)、兩個(gè) AI 框架同時(shí)引入后出現(xiàn) Bean 沖突。后兩者通過(guò)配置隔離和穩(wěn)定 id 就能解決。后續(xù)想繼續(xù)深入可以按這個(gè)順序擴(kuò)展先做 Tool Calling讓模型能調(diào)用你內(nèi)部的查詢接口再用 Spring AI Alibaba Graph 編排復(fù)雜的多步驟任務(wù)最后把批量任務(wù)和定時(shí)任務(wù)結(jié)合起來(lái)做成一個(gè)完整的知識(shí)庫(kù)自動(dòng)更新系統(tǒng)。這篇文章建議直接收藏配置代碼都是可以復(fù)制改的真正用時(shí)拿起來(lái)就能跑。