
从 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 时代不可替代的底牌。