
從 2024 年開始AI 應用開發幾乎成了 Python 開發者專屬賽道LangChain、LlamaIndex 各種框架層出不窮。Java 開發者想在自己的 Spring Boot 項目里接一個大模型要么寫裸 HTTP 請求調用 OpenAI 兼容接口要么硬套 Python 生態的思路代碼風格割裂維護成本極高。到了 2026 年這個局面的答案已經非常明確Spring AI 2.0。這篇文章要把 Spring AI 2.0 里最重要的五件事——多模型、Tools、MCP、Skills、Agent——完整串起來講一遍。不是單純介紹概念而是從一個小型實戰項目出發把每一步的配置、代碼、踩坑點全部鋪開。如果你是一個 Java 工程師正在糾結怎么在公司項目里落地 AI 能力這篇文章可以直接當參考手冊用。先說結論Spring AI 2.0 真正解決的問題是把“和大模型打交道”這件事變成了符合 Spring 編程模型的普通后端開發。它不追求把 LangChain 那套 Python 生態搬過來而是用 Spring 自己的依賴注入、自動配置、約定優于配置把多模型切換、工具調用、協議接入、Agent 編排統一成一個標準范式。讀完這篇文章你能獨立搭出一個支持多模型切換、能調用自定義工具、能通過 MCP 接入外部服務、帶記憶和會話的客服 Agent 原型。1. 這篇文章真正要解決的問題很多 Java 開發者學 AI 編程的第一反應是先去學 Python。這個認知正在變成一種路徑依賴。如果項目底層是 Java團隊是 Java 團隊業務邏輯都在 Spring Boot 服務里那用 Python 重寫一套 AI 應用等于把整個工程體系復制了一份。Spring AI 2.0 的價值就在這里。它處理的不只是“調一次大模型接口”這種小事而是一整套企業級 AI 應用開發中必須面對的問題同一個業務要支持多家模型供應商比如線上用 GPT-4o本地開發用 Ollama 里的開源模型怎么做到切換模型而不改業務代碼模型返回的是 JSON 文本怎么穩定地映射成 Java 對象而不是靠正則硬解析大模型不知道你的訂單數據、用戶數據怎么安全地讓它調用你已有的 Service 方法外部工具生態已經約定了統一接入協議比如數據庫 MCP Server、文件系統 MCP ServerSpring 項目怎么接入最省事一個智能客服 Agent 需要有角色設定、工具列表、會話記憶、多輪上下文這些怎么工程化管理這些問題的答案就是 Spring AI 2.0 的核心抽象體系。它跟 Python 的 LangChain 解決的問題高度重合但實現思路完全是 Java 式的自動配置、Bean 管理、類型安全、Starter 依賴。從實用角度看這篇文章適合四類讀者還沒接觸過 Spring AI但項目里已經有 Spring Boot 3.x 基礎想快速上手已經在用 Spring AI 1.x想知道 2.0 在 Tools、MCP、Skills、Agent 這幾個方向有什么變化被“多模型”“MCP”“Agent”這些概念繞暈需要一個能跑通的最小案例準備把 AI 能力集成進企業系統的架構師或技術負責人需要判斷技術選型和工程邊界。有 Spring Boot 基礎的人今天就能把鏈路跑通。2. Spring AI 2.0 核心概念從 ChatModel 到 Agent2.1 最核心的抽象ChatModelSpring AI 對 LLM 的抽象核心就是ChatModel接口。不管底層是 OpenAI、Anthropic、通義千問、DeepSeek 還是 Ollama 里的本地模型對上層業務代碼來說暴露出來的都是同一個接口。public interface ChatModel { ChatResponse call(Prompt prompt); }這個設計的價值在業務方不在實現方。你寫的 Service 層不需要關心當前接的是哪個大模型。以后要從 GPT 切到本地模型只改配置不碰 Java 代碼。這就是多模型支持的第一層含義。圍繞ChatModelSpring AI 還提供了幾個配套抽象ChatClient更面向業務的流式調用入口支持 system prompt、user prompt、工具注冊、結構化輸出。這是最常用的對象。EmbeddingModel負責把文本轉成向量用于 RAG、語義搜索等場景。StructuredOutputConverter將模型輸出解析為指定 Java 類型。2.2 Tools讓大模型調用你的函數大模型本身不持有你的業務數據它只能“說出”一個新的 JSON 結構表達“我想調用某個函數”。Tools 就是把這層機制封裝成了 Spring 風格的工具方法。在 Spring AI 中只需要在方法上標記Tool注解框架自動完成“模型生成函數調用參數 → 框架反射調用方法 → 把結果回傳給模型 → 模型基于結果繼續生成”的循環。這里真正容易踩坑的地方在于模型是否真的會調用你的 Tool取決于你寫的description是否足夠清晰。描述寫得含糊模型就會跳過函數調用直接憑幻覺回答。2.3 MCP模型上下文協議MCPModel Context Protocol是 Anthropic 在 2024 年底提出的開放協議目標是標準化“模型如何發現并調用外部工具/數據源”。它把工具、資源、提示詞統一成一套標準接口。一個團隊只要實現了 MCP Server任何支持 MCP 的客戶端都能復用。Spring AI 2.0 對 MCP 的支持是完整的可以作為 MCP Client連接現成的 MCP Server比如文件系統、數據庫、藍湖設計稿、GitHub 等也可以作為 MCP Server把 Spring 服務里的能力暴露給其他 AI 應用。MCP 和 Tools 的關系不是二選一。Tools 是 Spring AI 內部的函數調用機制MCP 是跨應用、跨語言的工具發現與傳輸標準。MCP Server 在遠端提供的工具最終會被 Spring AI 包裝成本地 Tool 參與模型對話。2.4 Skills更貼近業務的 Agent 能力封裝如果說 Tools 解決的是“單個函數”的調用那么 Skills 解決的是“一組能力”的復用。一個 Skill 通常包含多部分內容清晰的技能描述、可能用到的多個工具方法、提示詞模板、輸入校驗規則甚至內部的異常處理邏輯。從 Spring AI 2.0 的演進方向看Skill 就是為 Agent 誕生的“能力包”。舉個例子一個“訂單查詢技能”可以包含“按訂單號查狀態”“按手機號查訂單列表”“查詢物流軌跡”三個工具并統一處理參數校驗和返回格式。Agent 只需要知道“有一個訂單查詢技能”就能在合適的時候調用它。2.5 Skill 和 MCP 的區別這是很多初學者最暈的地方。用一句話概括它們的差異MCP 是標準與協議Skills 是業務封裝。MCP 解決的是“怎么連接、傳什么格式”的問題比如你用 npx 啟動一個 filesystem MCP Server客戶端連上它就能列出可用的工具列表。Skill 解決的是“以什么方式參與 Agent 編排”的問題它更像是一個高層的業務抽象背后既可以封裝本地 Tools也可以封裝對 MCP 工具的調用。打個比方MCP 像是 USB-C 接口標準任何設備只要按這個標準生產就能互聯Skill 則像一個“即插即用的功能包”比如一個“高清投屏技能”它可能包含了軟件、驅動和推薦配置。兩者不在同一個抽象層。2.6 Agent用對話能力編排一切Agent 不是一個新框架而是ChatModel Tools Skills 記憶 多輪編排的組合產物。在 Spring AI 2.0 中一個 Agent 的編程模型非常簡單準備好一個ChatClient給它配置系統角色、工具列表和會話記憶剩下的循環推理全部交給框架。Agent 內部會反復執行“模型生成 → 決定是否調用工具 → 拿到結果 → 繼續生成”的流程直到它能給出最終回答。不過簡單不代表沒有難點。真正考驗工程能力的是 Agent 的安全邊界、工具權限、會話存儲、失敗降級這些外圍問題。后面會專門用一整節說清楚。3. 環境準備與前置條件3.1 JDK 與構建工具Spring AI 2.x 基于 Spring Framework 6.x 和 Spring Boot 3.x要求 JDK 17 及以上。推薦直接使用 JDK 21理由很實際虛擬線程、更完善的 ZGC 行為以及 Spring Boot 對 JDK 21 的完整官方支持。構建工具用 Maven 或 Gradle 都可以。本文示例以 Maven 為主因為國內 Java 項目里 Maven 還是絕對主流。版本方面Spring AI 的版本更新速度比較快不建議把具體版本號寫死在文章里。正確做法在pom.xml里通過spring-ai-bom做依賴管理版本統一放到屬性里使用 Maven Central 上的最新穩定版。!-- 文件路徑pom.xml 片段 -- properties java.version21/java.version spring-boot.version3.4.x/spring-boot.version spring-ai.version2.0.x/spring-ai.version /properties實際使用中把x替換成發布時的具體小版本號即可。3.2 Spring Boot 項目初始化先在 Spring Initializr 上生成一個基礎工程或者直接在 IDEA 里用 Spring Initializr 創建。需要選擇的依賴如下Spring WebSpring AI OpenAISpring AI OllamaLombok可選Spring AI MCP Client WebMVC如果你需要把 Spring 服務本身暴露成 MCP Server還需要加Spring AI MCP Server WebMVC。本文的示例會先做 MCP Client 接入。3.3 模型 API Key 準備至少準備一個可用的大模型 API Key。如果公司有統一的模型網關也可以把 base-url 指向網關地址。本地開發優先推薦 Ollama 方式下載 Ollama再拉一個支持 function calling 的模型比如qwen2.5系列。這樣即使沒有公網 API Key也可以完成 Tools 和 Agent 的全流程測試。這里補充一個重要約定任何 API Key 都不要硬編碼到application.yml里更不要提交到 Git 倉庫。用環境變量注入例如${OPENAI_API_KEY:}。如果你有配置中心例如 Apollo、Nacos Config應該走配置中心統一管理。4. 核心流程拆解從配置到 Agent 的六步鏈路4.1 第一步配置多模型目標是一個 Spring Boot 項目里同時存在多個ChatModelBean。Spring AI 的自動配置會為每個已引入的模型 Starter 創建對應的ChatModelBean比如引入spring-ai-openai會自動創建OpenAiChatModel引入spring-ai-ollama會自動創建OllamaChatModel。但問題來了如果項目里同時有多個ChatModelBean注入ChatClient.Builder時 Spring 會由于類型不唯一而報錯。解決辦法就是顯式聲明一個多模型路由服務用 Map 按名稱保存所有模型。這個設計本質上是“多模型策略模式”后續切換模型時業務層只面向ChatModel接口編程選誰用誰由配置或路由邏輯決定。這是 Spring AI 多模型落地最實用的架構。4.2 第二步搞定結構化輸出大模型返回的是自然語言但業務系統需要的是FlightReservation、UserInfo這樣的 Java 對象。Spring AI 的ChatClient.entity()方法幫你做了類型轉換。實際操作時不要在實體里放太多復雜嵌套類型。大模型不是 JSON Schema 解析器越復雜的類型越容易解析失敗。先用扁平化的 record跑通后再逐步增加字段。4.3 第三步讓模型能調用工具定義一個繼承自Component的類在業務方法上標注Tool描述要寫到“模型一聽就懂”的程度。然后用ChatClient.Builder.defaultTools()把工具傳進去。驗證這一步是否成功最直接的辦法是問一個必須靠工具才能回答的問題比如“北京今天天氣怎么樣”。如果模型準確返回了天氣說明函數調用鏈路已經通了。4.4 第四步接入 MCP引入 MCP Client 依賴在配置里聲明要連的 stdio MCP ServerSpring AI 會自動把這個服務器提供的工具合并到模型對話中。如果公司內部有 HTTP 方式的 MCP Server也可以走 SSE 或 WebMVC 配置。接入方式和 stdio 略有不同但核心思想一致遠程工具被包裝成本地 Tool不需要業務代碼感知。4.5 第五步封裝 Skills把“散裝工具提示詞規則”收斂成一個高內聚的類。Skill 通常是普通 Spring Service內部依賴多個 Tool 方法再通過構造器注入到ChatClient。這里的一個工程建議每個 Skill 類都寫清楚Description讓 Agent 知道這個技能在什么場景下使用。Agent 判斷“該不該用這個技能”依賴的就是這個描述。4.6 第六步用 Agent 編排落地把系統角色、工具列表、Skills、會話記憶整合到一個ChatClientBean 里對外暴露一個chat(userMessage, conversationId)方法。這個 Bean 就是你的客服 Agent。會話記憶的實現方式依賴于ChatClient的id(conversationId)參數框架會把同一 id 的多輪對話保存到ChatMemory。生產環境應該替換成 Redis 或數據庫存儲避免單機內存丟失。現在整條鏈路就通了。下面用可運行代碼過一遍。5. 完整示例代碼實現本節的工程結構如下src/main/java/com/example/ai/ ├── AiApplication.java ├── config/ │ └── ChatClientConfig.java ├── controller/ │ ├── ChatController.java │ ├── StructuredOutputController.java │ └── MultiModelController.java ├── service/ │ ├── MultiModelService.java │ └── OrderQuerySkill.java ├── tool/ │ └── WeatherTools.java ├── agent/ │ └── CustomerServiceAgent.java └── entity/ └── FlightReservation.java5.1 新增 Maven 依賴先更新pom.xml加入 Spring AI BOM 以及所需的 Starter!-- 文件路徑pom.xml -- 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-openai/artifactId /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-ollama/artifactId /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-mcp-client-webmvc/artifactId /dependency /dependenciesspring-ai-bom的作用是統一管理所有 Spring AI 模塊的版本號避免手動逐個對齊版本。5.2 配置文件在application.yml中配置多模型和 MCP Client# 文件路徑src/main/resources/application.yml server: port: 8080 spring: application: name: spring-ai-demo ai: openai: base-url: ${OPENAI_BASE_URL:https://api.openai.com} api-key: ${OPENAI_API_KEY:} chat: options: model: gpt-4o-mini temperature: 0.7 ollama: base-url: http://localhost:11434 chat: options: model: qwen2.5:7b temperature: 0.7 mcp: client: stdio: servers: filesystem: command: npx args: -y,modelcontextprotocol/server-filesystem,/tmp/data這里最需要注意的地方是args的寫法。Spring AI 的 MCP 配置要求 args 是一個數組不同版本對分隔符的處理略有差異。如果啟動時 MCP Server 沒有連上第一優先排查的就是這個參數里的逗號分隔是否正確以及本機是否安裝并可以使用 npx。5.3 多模型調用用具體的ChatModel實現類型做構造器注入這樣不會被多 Bean 問題干擾// 文件路徑src/main/java/com/example/ai/service/MultiModelService.java Service public class MultiModelService { private final OpenAiChatModel openAiChatModel; private final OllamaChatModel ollamaChatModel; public MultiModelService(OpenAiChatModel openAiChatModel, OllamaChatModel ollamaChatModel) { this.openAiChatModel openAiChatModel; this.ollamaChatModel ollamaChatModel; } public String chatWith(String provider, String message) { ChatModel chatModel switch (provider) { case openai - openAiChatModel; case ollama - ollamaChatModel; default - throw new IllegalArgumentException(未知模型: provider); }; return chatModel.call(new Prompt(message)) .getResult() .getOutput() .getText(); } }這段代碼的關鍵點是“面向接口編程”。業務方拿到的是ChatModel具體實現可以隨時替換。以后新增模型供應商只需要增加一個 Starter 依賴再在 switch 里加一行分支。5.4 結構化輸出定義一個實體類用 record 保持簡潔// 文件路徑src/main/java/com/example/ai/entity/FlightReservation.java public record FlightReservation( String flightNumber, String from, String to, String departureTime, String price ) { }不推薦在這個 record 里放LocalDateTime、BigDecimal這類需要強類型轉換的字段。大模型返回的 JSON 字符串在解析成本地類型時一旦格式不匹配會直接拋出類型轉換異常。先用字符串類型跑通是結構化輸出最容易成功的路徑。再寫一個 Controller 展示如何使用// 文件路徑src/main/java/com/example/ai/controller/StructuredOutputController.java RestController RequestMapping(/api/structured) public class StructuredOutputController { private final ChatClient chatClient; public StructuredOutputController(ChatClient.Builder builder) { this.chatClient builder.build(); } GetMapping(/parse-reservation) public FlightReservation parseReservation(RequestParam String text) { return chatClient.prompt() .system(你是航班信息解析助手。請從用戶文本中抽取航班編號、出發地、目的地、出發時間和價格。) .user(text) .call() .entity(FlightReservation.class); } }5.5 自定義 Tools定義一個天氣工具類。這是本文最典型的Tool用法// 文件路徑src/main/java/com/example/ai/tool/WeatherTools.java Component public class WeatherTools { Tool(description 根據城市名稱查詢當前天氣) public String getWeatherByCity(String city) { // 實際項目里替換為天氣服務 API 調用 if (北京.equals(city)) { return 北京晴25℃東南風2級; } return city 多云22℃東北風1級; } Tool(description 根據城市名稱查詢未來三天天氣預報需要傳入城市和天數) public String getForecast(String city, int days) { return city 未來 days 天晴轉多云最低18℃最高27℃; } }注意Tool的描述寫清楚“需要傳入什么參數”這直接影響模型生成參數的成功率。5.6 MCP 客戶端接入前面已經在application.yml里配置了 filesystem 這個 stdio 服務。當 Spring AI 檢測到 MCP Client 依賴時會自動連接該服務并把它暴露出的工具合并到工具注冊表。如果不想使用 stdio 方式也可以把spring-ai-mcp-client-webmvc換成或互補使用 HTTP 方式spring: ai: mcp: client: url: http://localhost:8081url方式適合連接已經部署為獨立服務的 MCP Server。這里強調一個重要過程MCP 工具是“動態發現”的。你在代碼里看不到 filesystem 工具的 Java 類但它會在運行期被注冊成一個ToolCallback。排查 MCP 工具是否生效看啟動日志里是否打印了 MCP 工具調用的注冊信息即可。5.7 Skill 定義用一個高內聚的 Skill 類封裝“訂單查詢”能力。它不僅包含工具方法還包含面向 Agent 的描述和參數校驗邏輯// 文件路徑src/main/java/com/example/ai/service/OrderQuerySkill.java Service public class OrderQuerySkill { Tool(description 根據訂單號查詢訂單狀態和物流信息訂單號為數字字符串) public String queryOrderStatus(String orderId) { if (orderId null || !orderId.matches(\\d{6,})) { return 訂單號格式不正確; } // 實際項目里注入 OrderRepository 查詢數據庫 return 訂單 orderId 狀態已發貨預計 3 天內送達; } Tool(description 根據用戶手機號查詢最近三個月訂單列表) public String listRecentOrders(String mobile) { if (mobile null || !mobile.matches(1\\d{10})) { return 手機號格式不正確; } return 最近訂單2026030101已簽收、2026021502已發貨; } }所謂 Skill 和普通 Tool 類的差別更多體現在設計意圖上。一個 Skill 可以包含多個 Tool并負責它們之間的業務規則。Agent 只需要注入這一個類就能獲得整套能力。5.8 Agent 編排最后把所有能力整合到一個客服 Agent 中// 文件路徑src/main/java/com/example/ai/agent/CustomerServiceAgent.java Component public class CustomerServiceAgent { private final ChatClient chatClient; public CustomerServiceAgent(ChatClient.Builder builder, WeatherTools weatherTools, OrderQuerySkill orderQuerySkill) { this.chatClient builder .defaultSystem(你是企業智能客服回答要簡潔、準確、友好。當用戶詢問天氣時必須使用天氣工具 當用戶查詢訂單時必須使用訂單查詢技能不要編造訂單數據。) .defaultTools(weatherTools, orderQuerySkill) .build(); } public String chat(String userMessage, String conversationId) { return chatClient.prompt() .id(conversationId) .user(userMessage) .call() .content(); } public String chatWithSystem(String systemPrompt, String userMessage, String conversationId) { return chatClient.prompt() .system(systemPrompt) .id(conversationId) .user(userMessage) .call() .content(); } }sytem提示詞里明確寫了“必須使用天氣工具”“不要編造訂單數據”這種約束是 Agent 工程質量的重要來源。模型有概率忽略模糊指令但你把指令寫進系統提示詞輔助工具描述清晰成功率會大幅提高。再提供一個入口 Controller// 文件路徑src/main/java/com/example/ai/controller/ChatController.java RestController RequestMapping(/api/agent) public class ChatController { private final CustomerServiceAgent customerServiceAgent; private final MultiModelService multiModelService; public ChatController(CustomerServiceAgent customerServiceAgent, MultiModelService multiModelService) { this.customerServiceAgent customerServiceAgent; this.multiModelService multiModelService; } GetMapping(/chat) public String chat(RequestParam String message, RequestParam(defaultValue default) String conversationId) { return customerServiceAgent.chat(message, conversationId); } GetMapping(/multi) public String multi(RequestParam String provider, RequestParam String message) { return multiModelService.chatWith(provider, message); } }到這里一個支持多模型、自定義 Tools、MCP 外部工具、Skill 能力封裝、多輪會話記憶的 Agent 原型已經完整落地。下面看看怎么驗證它。6. 運行結果與效果驗證啟動項目mvn spring-boot:run如果本地Ollama已經拉取了qwen2.5:7b啟動日志里會同時出現 OpenAI 和 Ollama 的模型初始化信息。MCP Client 啟動時會嘗試執行npx -y modelcontextprotocol/server-filesystem /tmp/data日志里會出現 MCP Server connected 之類的記錄。依次驗證幾個核心能力基礎對話curl http://localhost:8080/api/agent/chat?message你好conversationIdtest-001預期輸出一句問候語說明ChatClient鏈路正常。結構化輸出curl http://localhost:8080/api/structured/parse-reservation?text幫我訂明天從北京到上海的MU5111航班價格850元提醒我上午十點出發預期返回 JSON{flightNumber:MU5111,from:北京,to:上海,departureTime:10:00,price:850元}工具調用聯動curl http://localhost:8080/api/agent/chat?message北京今天天氣怎么樣conversationIdtest-001如果模型沒有調工具可能只會回答“我無法獲取實時天氣”。如果正確調用了WeatherTools.getWeatherByCity會返回“北京晴25℃”等相關信息。多輪會話驗證curl http://localhost:8080/api/agent/chat?message我的手機號是13800138000幫我查一下最近訂單conversationIdtest-001 curl http://localhost:8080/api/agent/chat?message再看看第一單的物流conversationIdtest-001第二次提問依賴第一次的上下文。如果返回結果包含第一單的訂單號或狀態說明會話記憶已生效。判斷 Agent 是否正常不能只看是否返回結果還要看它是不是在正確的步驟調用了正確的工具。建議在本地開發時打開 Spring AI 的調試日志logging: level: org.springframework.ai: DEBUG這樣可以在控制臺看到完整的工具調用鏈模型請求 → 工具調用 → 工具返回 → 模型最終回答。如果失敗優先看這幾個位置啟動階段MCP Server 是否連接成功Ollama 服務是否可用調用階段模型返回是否超時工具階段Tool方法是否有日志返回內容是否被模型正確消費。7. 常見問題與排查思路問題現象可能原因排查方式解決方案啟動報錯說存在多個 ChatModel Bean同時引入了多個模型 Starter自動配置創建了多個同類型 Bean查看啟動日志中 Bean 創建記錄用Qualifier或顯式配置指定使用的模型或封裝多模型路由服務請求時模型長時間無響應模型 API Key 無效、網絡不通、本地 Ollama 沒有啟動先 curl 模型供應商接口查看 Ollama 是否在 11434 端口監聽修正 API Key / base-url啟動 Ollama 并確認模型已拉取工具沒有被調用模型直接瞎回答Tool的描述不夠清晰或 system prompt 沒有強制要求檢查工具描述打開 DEBUG 日志確認模型請求里是否包含 tool_calls重寫描述加入“必須使用工具回答”等約束結構化輸出解析失敗拋類型轉換異常模型返回文本格式不匹配 Java 類型查看實際返回的 JSON簡化實體字段統一使用 String逐步增加字段MCP Server 連接失敗npx 未安裝、args 參數格式錯誤、服務端地址不通在終端手動執行npx -y modelcontextprotocol/server-filesystem /tmp/data檢查啟動日志 MCP 部分修正 args 寫法安裝 npx改用可訪問的 HTTP MCP Server多輪對話上下文丟失conversationId傳遞不一致或沒有配置持久化 ChatMemory檢查每次請求是否傳同一個 id查看內存存儲的日志用 Redis/數據庫實現 ChatMemory統一會話 id 生成規則本地模型不支持 function callingOllama 拉取的模型版本較老或本身不支持工具調用查詢模型文檔確認是否支持 tools更換支持 function calling 的模型例如qwen2.5系列這些問題是獨立開發者在完整跑通鏈路時最容易遇到的。嚴格按照排查路徑走大多數問題會在十分鐘內定位。8. 最佳實踐與工程建議8.1 模型接入層統一路由隔離供應商不要把模型供應商的 SDK 直接散落在業務代碼里。所有模型訪問統一走ChatModel接口模型路由邏輯收斂到一個服務中。這樣才能做到“線上用商業模型、測試用本地模型”而不修改業務代碼。8.2 提示詞管理模板化、版本化System prompt 不要散落在 Controller 里。建議用提示詞模板文件配合 Spring 的Resource加載放到系統資源目錄下。提示詞實際上是需要評審和版本管理的“代碼”它直接影響模型行為質量。8.3 工具安全最小權限原則Tool方法本質上是把內部能力暴露給外部模型調用。必須遵守最小權限原則工具方法只做自己該做的事不要聲明一個大而全的方法例如“執行任意 SQL”。所有涉及數據庫、文件、外部 API 的工具都要做參數校驗就像對待用戶輸入一樣。8.4 會話記憶生產環境不要用默認內存實現ChatClient的默認記憶是內存級的應用重啟即丟失。生產環境應該把ChatMemory替換為 Redis 或數據庫實現。會話 ID 必須由后端統一生成不要信任前端傳入的任意 key否則容易出現會話串臺問題。8.5 MCP 生命周期管理MCP stdio 服務本質上是啟動一個子進程它的生命周期需要被關注。不要在生產環境用 npx 臨時拉取 MCP Server盡量構建成獨立服務用 HTTP 方式接入這樣便于監控和擴縮容。8.6 Agent 可觀測性Agent 是一個多步決策系統每一步都可能出錯。生產環境必須記錄用戶問題原文模型是否發起了工具調用調用了哪個工具、參數是什么工具返回結果最終回答內容。這些日志鏈路是排查問題的唯一依據。建議在Tool方法和 Agent 調用層都加上結構化日志而不是只靠框架默認日志。8.7 成本與限流多模型配置帶來成本控制能力的同時也帶來新的風險工具循環次數過多會導致 Token 消耗膨脹。建議給 Agent 調用設置超時時間、最大工具調用輪數并針對不同模型配置不同的限流策略。8.8 版本升級策略Spring AI 版本迭代快API 偶有調整。升級前先看官方遷移指南并且保留一個小范圍的兼容層。比如你寫一個AgentChatService包裝ChatClient未來內部 API 變化時只改這個類業務層不受影響。9. 總結與后續學習方向Spring AI 2.0 給 Java 生態帶來的價值不只是一套可以調大模型的 Starter而是一整套符合 Spring 編程模型的 AI 應用開發范式。本文把這條鏈路完整拆解了一遍多模型解決了供應商鎖定問題Tool讓模型具備調用業務方法的能力MCP 把外部工具生態標準化Skills 讓能力封裝更貼近業務Agent 則把這一切組合成了可交付的智能服務。建議你按順序完成三個練習先跑通多模型切換和結構化輸出再實現一個包含兩個Tool的客服助手最后接入一個外部 MCP Server例如文件系統或數據庫服務。這三步做完Spring AI 2.0 的主要能力就算真正掌握。接下來值得深入的方向包括RAG 與向量數據庫的集成、Agent 與業務流程引擎的結合、基于 MCP Server 暴露公司內部服務給 AI 應用、以及多 Agent 協作模式。每一條都比單純調大模型接口更有工程價值也是 Java 工程師在 AI 時代不可替代的底牌。