師的 AI 工程化落地(一):當(dāng) Java 微服務(wù)開始「思考」——把大模型裝進(jìn) Spring Cloud 的第一堂課)
這是一個(gè)寫給 Java 架構(gòu)師的連載專欄。我們不談煉丹、不講 Transformer 原理只回答一個(gè)問題手上這套跑了多年的 Spring Cloud怎么穩(wěn)穩(wěn)當(dāng)當(dāng)?shù)匕汛竽P陀闷饋?、用到生產(chǎn)里。全篇按順序推進(jìn)今天是第一堂課給微服務(wù)裝上「大腦」的最小閉環(huán)。〇、先說清楚這個(gè)系列要帶你做什么過去兩年我見過太多團(tuán)隊(duì)把 AI 用成了「高級(jí)玩具」某個(gè)后端同學(xué)在 Controller 里偷偷塞了一段 HTTP 調(diào)用大模型的代碼Demo 效果驚艷然后呢API Key 散落在三四個(gè)服務(wù)里、高峰期一次調(diào)用 40 秒拖垮線程池、賬單月底超支三倍、模型偶爾胡說八道還被客戶截圖投訴。問題不在大模型而在沒有人用架構(gòu)師的方式對(duì)待它。這個(gè)系列一共十篇路線是這樣的服務(wù)接入(本篇) → RAG 知識(shí)庫 → Function Calling 工具調(diào)用 → 多模型路由與成本 → 穩(wěn)定性治理 → 可觀測(cè)性 → 智能體編排 → 安全 → 企業(yè)級(jí) AI 中臺(tái)全景跟著走完你會(huì)得到一套能直接搬進(jìn)公司項(xiàng)目的工程化方案。準(zhǔn)備好的話上課。一、心智轉(zhuǎn)換大模型是一個(gè)「又貴、又慢、還會(huì)犯錯(cuò)」的服務(wù)架構(gòu)師的第一課永遠(yuǎn)不是寫代碼而是給新東西定性。請(qǐng)先把對(duì)大模型的三個(gè)錯(cuò)誤幻想扔掉幻覺現(xiàn)實(shí)它是無所不知的大腦它是一個(gè)按 token 計(jì)費(fèi)的外部 HTTP 服務(wù)它很快像查 Redis同步調(diào)用秒級(jí)起步長(zhǎng)文本 10s必須按高延遲依賴設(shè)計(jì)它說的都對(duì)它會(huì)一本正經(jīng)地編造幻覺輸出只能當(dāng)「建議」不能當(dāng)「事實(shí)」定性之后結(jié)論就清晰了大模型在你的架構(gòu)圖里和 MySQL、Redis、支付接口一樣是一個(gè)被依賴的下游資源——只不過它貴、慢、不穩(wěn)定且返回的是自然語言。你過去積累的所有微服務(wù)治理經(jīng)驗(yàn)超時(shí)、熔斷、隔離、降級(jí)、監(jiān)控、成本核算對(duì)它全部適用只是參數(shù)要重新標(biāo)定。這是整個(gè)系列的基石后面每一篇都是在這張「定性表」上做展開。二、架構(gòu)定位AI 能力必須收斂成一個(gè)獨(dú)立服務(wù)最忌諱的做法是讓訂單、客服、內(nèi)容三個(gè)微服務(wù)各自直連大模型。三周后你一定會(huì)收獲三套 Key、三套超時(shí)配置、三份無法合并的 token 賬單以及一次「模型供應(yīng)商漲價(jià)全團(tuán)隊(duì)加班改代碼」的慘痛經(jīng)歷。正確姿勢(shì)是新增一個(gè)基礎(chǔ)設(shè)施類服務(wù)ai-service┌─────────┐ ┌─────────┐ ┌─────────┐ │order-svc│ │ticket-svc│ │content-svc│ 業(yè)務(wù)服務(wù)保持純粹 └────┬────┘ └────┬────┘ └────┬────┘ └───────────┼───────────┘ Feign / HTTP ▼ ┌─────────────────┐ │ ai-service │ 統(tǒng)一密鑰/限流/路由/記賬/降級(jí) │ (Spring AI) │ └────────┬────────┘ ▼ ┌──────────┬──────────┐ LLM 廠商 向量庫 內(nèi)部工具(Function Calling)它的職責(zé)邊界也很明確只做五件事統(tǒng)一持有各家大模型的密鑰與配置對(duì)內(nèi)暴露領(lǐng)域化接口總結(jié)、抽取、問答、分類而不是裸的/chat統(tǒng)一做超時(shí)、熔斷、限流與成本統(tǒng)計(jì)屏蔽底層模型差異——今天用 A 廠明天換 B 廠業(yè)務(wù)方零感知后續(xù)承載 RAG 知識(shí)庫與工具調(diào)用本系列第 2~4 篇會(huì)逐步往里加。技術(shù)選型上Java 生態(tài)目前就兩個(gè)主流選擇Spring AI 與 LangChain4j。我們整個(gè)系列統(tǒng)一用Spring AI1.0 GA 版本理由很簡(jiǎn)單你團(tuán)隊(duì)已經(jīng)會(huì) Spring Boot自動(dòng)裝配、yaml 配置、Micrometer 監(jiān)控全是現(xiàn)成的學(xué)習(xí)成本最低這對(duì)要在團(tuán)隊(duì)里推動(dòng)落地的架構(gòu)師來說是決定性因素。三、最小閉環(huán)30 分鐘跑通第一個(gè)對(duì)話接口環(huán)境要求JDK 17、Spring Boot 3.2、Spring AI 1.0.x。注意這是硬性門檻Spring AI 不支持 JDK 8還在 JDK 8 的團(tuán)隊(duì)需要先規(guī)劃升級(jí)路徑這也是架構(gòu)師要提前向團(tuán)隊(duì)說明的成本。3.1 引入依賴與配置dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-openai/artifactId /dependency國(guó)內(nèi)團(tuán)隊(duì)不想折騰網(wǎng)絡(luò)可以直接用兼容 OpenAI 協(xié)議的國(guó)產(chǎn)模型只改地址即可spring: ai: openai: api-key: ${LLM_API_KEY} # 密鑰走環(huán)境變量嚴(yán)禁提交到 Git base-url: ${LLM_BASE_URL:https://api.openai.com} chat: options: model: deepseek-chat temperature: 0.3 # 業(yè)務(wù)場(chǎng)景默認(rèn)壓低減少胡言亂語3.2 用 ChatClient 而不是 ChatModelSpring AI 有兩套 API。ChatModel是底層原語ChatClient是帶 System 預(yù)設(shè)、支持流式的 fluent API——業(yè)務(wù)代碼一律用后者Service public class AiChatService { private final ChatClient chatClient; // 構(gòu)造時(shí)設(shè)定「人設(shè)」所有對(duì)話共享統(tǒng)一的系統(tǒng)提示詞 public AiChatService(ChatClient.Builder builder) { this.chatClient builder .defaultSystem( 你是企業(yè)內(nèi)部技術(shù)助手回答必須 1. 基于用戶提供的信息不確定就明說禁止編造 2. 先給結(jié)論再給不超過三點(diǎn)理由 3. 涉及操作步驟時(shí)給出可執(zhí)行命令。 ) .build(); } /** 同步問答適合后臺(tái)批處理類場(chǎng)景 */ public String ask(String question) { return chatClient.prompt() .user(question) .call() .content(); } /** 流式問答面向前端交互token 逐個(gè)返回首字延遲從 10s 降到 1s 內(nèi) */ public FluxString askStream(String question) { return chatClient.prompt() .user(question) .stream() .content(); } }3.3 暴露 SSE 接口并扛住網(wǎng)關(guān)這一關(guān)流式是 AI 應(yīng)用體驗(yàn)的分水嶺用 WebFlux 輸出 SSERestController RequestMapping(/api/ai) public class AiChatController { private final AiChatService aiChatService; public AiChatController(AiChatService aiChatService) { this.aiChatService aiChatService; } GetMapping(value /chat, produces MediaType.TEXT_EVENT_STREAM_VALUE) public FluxString chat(RequestParam String q) { return aiChatService.askStream(q); } }到這里代碼能跑了但架構(gòu)師的工作才剛開始——這個(gè)接口一旦經(jīng)過 Nginx/Spring Cloud Gateway大概率會(huì)出現(xiàn)「轉(zhuǎn)圈十秒答案瞬間全部吐出」的詭異現(xiàn)象。原因是響應(yīng)緩沖。兩處必須改Nginx 層關(guān)閉緩沖location /api/ai/ { proxy_pass http://ai-service/; proxy_buffering off; proxy_read_timeout 120s; add_header X-Accel-Buffering no; }GatewayWebFlux 版本身是響應(yīng)式透?jìng)鞑粫?huì)緩沖但要記得把全局超時(shí)調(diào)到大于模型最長(zhǎng)響應(yīng)時(shí)間并對(duì) SSE 路徑關(guān)閉重試——重試一個(gè)流式接口用戶會(huì)收到兩遍答案token 也扣兩遍錢。用 curl 驗(yàn)證curl -N http://localhost:8080/api/ai/chat?q微服務(wù)里如何設(shè)計(jì)超時(shí)看到文字逐字蹦出來最小閉環(huán)就完成了業(yè)務(wù)服務(wù) → ai-service → 大模型 → SSE 回前端。四、第一堂課的作業(yè)兩個(gè)必須現(xiàn)在就想清楚的問題我不給玩具代碼當(dāng)終點(diǎn)留兩個(gè)生產(chǎn)環(huán)境必答題你可以在評(píng)論區(qū)寫下你的方案第 5、6 篇我會(huì)給出完整實(shí)現(xiàn)線程池怎么隔離ai-service 里一個(gè) 40 秒不返回的模型調(diào)用會(huì)不會(huì)把 Tomcat 工作線程占滿拖垮同進(jìn)程的其他接口提示信號(hào)量隔離、響應(yīng)式棧、超時(shí)預(yù)算三選一或組合。用戶看到「服務(wù)繁忙」時(shí)怎么辦模型超時(shí)或限流后前端不能一直轉(zhuǎn)圈。降級(jí)策略是返回固定話術(shù)、轉(zhuǎn)人工還是切換到更小更快的備用模型這兩個(gè)問題想不明白AI 功能就只能停在內(nèi)網(wǎng) Demo 階段——它們正是本系列中段穩(wěn)定性治理篇的核心內(nèi)容。五、本講小結(jié)與下講預(yù)告今天我們只做了一件事但它是后面所有內(nèi)容的地基把大模型定性為一個(gè)貴、慢、會(huì)犯錯(cuò)的外部服務(wù)并用一個(gè)獨(dú)立的 ai-service 把它收斂進(jìn) Spring Cloud 體系最后跑通了同步與流式兩個(gè)接口。下一篇我們進(jìn)入真正讓 AI「懂業(yè)務(wù)」的關(guān)鍵技術(shù)——RAG檢索增強(qiáng)生成為什么把公司文檔直接塞給大模型是死路文檔怎么切片、怎么生成 Embedding、Redis 和 Milvus 到底怎么選我們下周四晚同一時(shí)間動(dòng)手搭一個(gè)能回答內(nèi)部知識(shí)庫問題的問答服務(wù)。系列導(dǎo)航一服務(wù)接入 · 本篇 下一篇二RAG 落地上——文檔切片、Embedding 與向量庫選型如果這個(gè)系列對(duì)你有幫助點(diǎn)個(gè)關(guān)注每周更新不掉隊(duì)。運(yùn)行環(huán)境說明本文代碼基于 JDK 17 Spring Boot 3.3 Spring AI 1.0.x 實(shí)測(cè)國(guó)產(chǎn)兼容模型可直接運(yùn)行。