过去一年很多 Java 团队开始尝试把大模型接进业务系统但真正落地的项目并不多。原因不是“不会调 API”而是卡在更现实的问题上模型能聊天却查不了航班、订不了票对话一长就丢上下文业务方今天要接这个模型、明天要换那个模型代码改起来想砸键盘。如果你也在这个阶段这篇文章值得看完。本文要讲的是 Spring AI 2.x 下的企业级 AI Agent 开发思路并从一个智能航空助手项目切入完整拆解多模型接入、Tools 工具调用、MCP 协议集成、Skills 能力封装、多层记忆管理等全链路环节。读完你会得到三样东西一张可落地的 Agent 技术地图、一套能跑通的项目骨架、一份避坑清单。先给一个明确判断在 Java 生态里做 Agent最值得投入的方向不是去重复造模型调用框架而是把“让模型能调用真实系统”这件事工程化Spring AI 正好提供了这条路。整篇文章会按照“核心概念 - 业务场景 - 环境搭建 - 关键实现 - 验证排错 - 工程建议”的顺序展开。如果你已经在 LangChain 或自研框架上踩过坑看这篇文章会更有体感如果你是第一次接触 Agent 开发我会尽量把抽象概念落到代码和流程上。1. 这篇文章真正要解决的问题先说痛点。企业级 Agent 开发表面看是“接入一个大模型”实际难在三件事。第一模型和业务的连接。大模型只认识文字不认识你数据库里的订单表、航班表、库存表。你要让它帮你查明天的航班就必须给模型提供一种“触发外部系统”的机制。这个机制在 Spring AI 里叫 Tools在行业标准里叫 Function Calling。Tools 写不好Agent 就是个聊天机器人。第二多模型切换的成本。很多企业在实际项目里不会只用一个模型。推理用 A 模型成本敏感的场景用 B 模型甚至同一个功能需要 A/B 对比。如果上层的业务代码绑死了某个厂商的 SDK每换一个模型都是一次大重构。Spring AI 的价值之一是把不同模型的接入统一成一套抽象。第三Agent 状态的管理。一个真正的 Agent 不只是“一问一答”它需要记住用户之前说过什么、知道用户的偏好、能够跨多轮对话完成一个复杂任务。这里就涉及多层记忆的设计也是很多自研项目做到后期最容易失控的部分。这篇文章要解决的核心问题就是这三件事。它适合正在做 AI 应用落地的 Java 工程师、准备设计 Agent 架构的技术负责人以及想把 Spring AI 应用在实际项目的同学。如果你期望的是“几天内写一个生产级 Agent”那要把心态放平本文给的是路线图和最小闭环生产级还涉及监控、权限、灰度等工程细节我会在最后聊。2. 基础概念与核心原理很多人看到 Spring AI、MCP、Agent、Tools、Skills 这些词就头大总觉得是五座大山。其实拆开来看它们解决的问题都很具体。2.1 Spring AI 是什么Spring AI 是 Spring 官方推出的 AI 应用开发框架对标的是 Python 生态里 LangChain 那套东西但它的底层架构和 Java/Spring 生态深度绑定。它的核心目标不是训练模型而是让开发者用一套统一的 API 去对接不同大模型然后把模型能力接入 Spring 的 Bean、配置、事务等基础设施。从 1.x 到 2.xSpring AI 在模型接入、ChatClient 调用、工具调用、可观测性等方面成熟了很多。最值得关注的一个变化是官方把 Agent 能力的建设放到了更重要的位置包括更稳定的工具调用链路、内存管理、以及与 MCP 协议的集成。本文中的代码以 Spring AI 2.x 为语境具体版本以官方文档为准。2.2 Agent 是什么一个被用烂了的词。为了不过度抽象我从工程角度给它下一个定义Agent 是一个能够自主决策和调用工具的大模型应用它会根据任务目标循环执行“理解意图 - 选择工具 - 执行工具 - 观察结果 - 生成回复”的流程。对比传统程序传统程序是“用户点按钮代码执行固定逻辑”Agent 是“用户说一句话模型决定调用哪个函数、怎么调、然后基于结果回答”。所以 Agent 的本质不是变得更聪明而是把“决策权”从程序员转移给了模型。这也带来了不可控性后面讲工程建议时会展开。2.3 Tools 是什么Tools也叫工具函数、Function Calling。它的作用是给模型一把“钥匙”让模型在对话过程中调用外部系统。例如用户问“明天北京到上海有几趟航班”模型本身不知道航班数据但它知道有个工具叫searchFlights于是它生成一个调用参数框架帮你执行真正的航班查询方法再把结果返回给模型模型最终组织成自然语言回答。在 Spring AI 里最简单的方式是用Tool注解标注一个 Java 方法框架会自动把方法描述、参数结构注册给模型。2.4 MCP 是什么MCP 是 Model Context Protocol模型上下文协议。可以把它理解成“模型的 USB-C 接口”。在没有 MCP 之前每个 Agent 接入一个数据源都要写一套自定义集成。比如接入数据库写一套、接入文件系统写一套、接入第三方 API 又写一套。MCP 的出现是为了统一这种连接方式服务端把能力暴露成标准化工具客户端通过 MCP 协议调用。在 Spring AI 中你可以把 MCP Server 暴露的工具注册为 Agent 可用的 Tools。这样做的好处是一个 MCP Server 可以被多个 Agent 复用也让 Agent 与外部工具的连接过程标准化了。2.5 Skills 是什么Skills 是比 Tools 更高一层的能力封装。一个 Skill 可以包含提示词模板、工具调用逻辑、输出格式约定甚至是一套完整的处理流程。举个例子一个“航班改签 Skill”不只是调用“改签 API”它还可能在改签前先检查用户身份、查询可改签航班、计算差价再给出一段符合话术的回复。把这一整套封装成 Skill 后Agent 能更稳定地执行这类任务而不是每次从零推理。2.6 多层记忆是什么记忆决定 Agent 的“连续感”。如果没记忆用户上一句说“我是金卡会员”下一句问“那我有什么权益”模型完全想不起来。多层记忆通常包含三部分会话记忆保存当前对话轮次、用户记忆保存用户长期偏好、应用记忆保存业务系统里的历史数据。有的架构还会引入向量数据库做长期记忆通过语义检索找到与当前问题相关的历史信息。下面用一个表格对比几个核心概念的差异方便后面阅读。概念解决什么问题和 Agent 的关系类比Spring AIJava 生态统一接入大模型Agent 开发的底层框架类似 Web 开发里的 Spring BootAgent自主决策完成任务核心编排入口一个会思考的调用中枢Tools / Function Calling让模型调用外部函数是 Agent 的“手”给模型装上的插件接口MCP标准化外部工具接入协议是 Tools 的一种来源模型的 USB-C 接口Skills将提示词工具流程封装成能力Agent 调用的“技能包”可复用的工作流模板多层记忆让 Agent 记住上下文和长期状态Agent 的“文件系统”会话状态 用户画像3. 智能航空项目场景与业务设计在动手写代码之前先明确我们的业务场景。假设要构建一个航空出行助手它要能处理下面的请求查询航班用户提供日期、出发地、目的地查询可用航班。查天气查询目的地天气方便用户决定出行时间。预订机票根据用户选择的航班生成订单。改签 / 退票在符合规则的情况下完成变更。会员权益查询查询用户会员等级和相关权益。一个完整的 Agent 调用链路长这样用户提问“明天从北京去上海的航班有哪些上海天气怎么样”——Agent 收到问题后拆解出两个意图查航班、查天气。先调用航班查询工具拿到航班列表再调用天气查询工具拿到上海天气信息最后把两种结果整合成一段自然语言回复给用户。这个例子看起来简单但它是理解 Agent 全链路最好的入口。它同时用到了多模型接入、工具调用、上下文组织和外部服务集成。后面扩展到“预订机票”时才需要引入用户记忆、多轮确认、订单状态管理等逻辑。为了不让本文变成空谈我建议项目结构按下图方式组织air-agent-demo ├── pom.xml ├── src │ ├── main │ │ ├── java │ │ │ └── com/example/aiagent │ │ │ ├── AiAgentApplication.java │ │ │ ├── config │ │ │ ├── controller │ │ │ ├── agent │ │ │ ├── memory │ │ │ ├── skill │ │ │ └── tool │ │ └── resources │ │ └── application.ymltool包放航班、天气等工具方法skill包放可复用的技能模板memory包放记忆管理agent包放 Agent 编排逻辑。这个结构不是标准答案但足够支撑后续扩展。4. 环境准备与基础配置进入实操阶段。先列一下本文使用的环境JDK 17 及以上。Spring Boot 3.x。Maven 3.6。Spring AI 2.x 版本以当前官方稳定版为准。一个兼容 OpenAI 协议的大模型 API Key例如 DeepSeek 开放平台。这里要说明一点不同版本的 Spring AI 依赖坐标可能不同本文重点是链路设计依赖版本请以实际项目为准。4.1 创建 Maven 工程你可以通过 Spring Initializr 创建项目然后手动加入 Spring AI 相关依赖。!-- pom.xml -- parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version3.3.x/version relativePath/ /parent properties java.version17/java.version spring-ai.version2.0.0/spring-ai.version /properties dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- Spring AI 核心 -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-openai/artifactId version${spring-ai.version}/version /dependency !-- 工具调用与 Agent 支持 -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-openai/artifactId version${spring-ai.version}/version /dependency /dependencies提醒一下不同版本的 Spring AI 可能会有 BOM 管理更推荐在dependencyManagement里引入官方 BOM避免版本冲突。上面这段依赖只是让概念落地生产项目请参照官方文档调整。4.2 配置 DeepSeek 模型DeepSeek 提供了 OpenAI 兼容的 API因此可以通过 Spring AI 的 OpenAI Starter 接入。核心配置如下。# src/main/resources/application.yml spring: application: name: air-agent-demo ai: openai: base-url: https://api.deepseek.com api-key: ${DEEPSEEK_API_KEY} chat: options: model: deepseek-chat temperature: 0.3这里的关键点有三个。第一base-url要指向兼容 OpenAI 协议的地址DeepSeek 这类平台通常会给一个 OpenAI 兼容端点。第二api-key不要写死在配置文件里推荐用环境变量${DEEPSEEK_API_KEY}代替避免 Key 泄露。第三temperature在 Agent 场景不要设置太高。Agent 任务追求的是稳定执行工具而不是天马行空的创意回答。航班查询、机票预订这类任务temperature设置在 0.3 以下更可控。4.3 理解 Spring AI 的 ChatClientSpring AI 2.x 推荐使用ChatClient作为对话入口。它和RestClient、WebClient的风格很像你可以通过流式 API 构建请求。Configuration public class AiConfig { Bean ChatClient chatClient(ChatClient.Builder builder) { return builder .defaultSystem(你是一个航空出行助手负责查询航班、天气并协助用户完成机票预订。) .build(); } }这个 Bean 会被注入到后续的 Service 中。defaultSystem设置了系统提示词让模型在整个对话中保持角色设定。5. 多模型接入与统一调用企业项目里多模型是常态。可能的原因有很多不同模型在不同任务上表现不同。成本控制简单任务用小模型复杂推理用大模型。容灾某个模型服务不稳定需要无损切换。Spring AI 对这类需求的支持方式是定义统一的ChatModel抽象。当你引入 openai、anthropic、ollama 等 starter 时Spring 容器里会生成对应的ChatModelBean。如果你引入了多家模型可以在代码里通过Qualifier选择要使用的模型。先做一个最简单的多模型配置。spring: ai: openai: base-url: https://api.deepseek.com api-key: ${DEEPSEEK_API_KEY} chat: options: model: deepseek-chat ollama: base-url: http://localhost:11434 chat: options: model: qwen2.5:7b然后在代码里定义两个 BeanConfiguration public class ModelConfig { Bean Primary ChatClient deepSeekChatClient(ChatClient.Builder builder) { return builder .defaultSystem(你是一个严谨的航空助手) .build(); } Bean(localChatClient) ChatClient localChatClient(ChatClient.Builder builder) { return builder .defaultSystem(你是本地部署的轻量级助手) .build(); } }这里有两个ChatClientBean一个走 DeepSeek一个走本地 Ollama。业务代码中默认场景注入主 Bean需要本地模型时用Qualifier(localChatClient)区分。Service public class FlightAgentService { private final ChatClient chatClient; private final ChatClient localChatClient; public FlightAgentService( Qualifier(deepSeekChatClient) ChatClient chatClient, Qualifier(localChatClient) ChatClient localChatClient) { this.chatClient chatClient; this.localChatClient localChatClient; } public String ask(String userMessage) { return chatClient.prompt() .user(userMessage) .call() .content(); } }这段代码的意义在于业务代码不再关心底层是 DeepSeek 还是 OpenAI只依赖ChatClient这个统一抽象。以后接新模型只需要新增配置和 Bean业务层不用动。这就是多模型接入的核心价值不是炫技而是把模型切换成本从“重构”降为“配置”。6. Tools 与 MCP让 Agent 真正触发业务动作单纯的对话模型不能完成业务所以要让模型具备工具调用能力。Spring AI 提供了两种常用方式一种是Tool注解方法直接注册为模型可调用的工具另一种是通过 MCP 接入外部标准化工具。6.1 用 Tool 实现航班查询先定义一个航班查询方法用Tool注解描述它的功能。package com.example.aiagent.tool; import org.springframework.ai.tool.annotation.Tool; import org.springframework.stereotype.Component; import java.time.LocalDate; import java.util.List; import java.util.Map; Component public class FlightTools { Tool(description 查询指定日期从出发地到目的地的航班列表) public ListMapString, String searchFlights(String from, String to, String date) { // 实际项目中这里会调用航班服务接口或数据库 if (北京.equals(from) 上海.equals(to)) { return List.of( Map.of(flightNo, CA1519, airline, 国航, departTime, 08:00, arriveTime, 10:15, price, 1280), Map.of(flightNo, MU5102, airline, 东航, departTime, 09:30, arriveTime, 11:45, price, 980) ); } return List.of(); } }Tool注解里的description非常关键。模型并不知道这个 Java 方法背后有什么它完全靠方法名、参数描述、注解描述来判断“什么时候调用这个工具”。描述越准确Agent 的调用准确率越高。6.2 让 Agent 使用 Tools要使用上面的FlightTools只需要在生成ChatClient时把它注册进去。Configuration public class AiConfig { Bean ChatClient chatClient(ChatClient.Builder builder, FlightTools flightTools) { return builder .defaultSystem(你是一个航空出行助手可以查询航班。) .defaultTools(flightTools) .build(); } }这里有个容易踩坑的地方项目里有多个ToolBean 时如果全部注册进ChatClient会把模型的工具列表变得很庞大影响推理效果和响应速度。更推荐的做法是分场景注入。例如“航班查询”场景只注册航班相关工具“会员服务”场景只注册会员相关工具。6.3 通过 MCP 接入天气查询如果天气服务是一个独立的 MCP Server那么 Spring AI 可以通过 MCP 客户端自动发现并注册它的工具而不用手动编写Tool方法。配置层面大致是这样的spring: ai: mcp: client: enabled: true type: sync connections: weather-server: url: http://localhost:8081/mcp在代码里MCP 暴露的工具会像普通 Tools 一样出现在ChatClient中。这对中大型团队非常有价值航班系统、天气系统、订单系统各自维护 MCP ServerAgent 服务只需要订阅即可不需要关心内部接口细节。注意MCP Server 本身是一个独立服务Java 里可以用 Spring AI MCP Server 能力快速暴露一个工具端点。这里不展开全部代码重点理解MCP 是工具接入层的“标准化方案”而Tool是轻量化的“进程内方案”。6.4 Tools 与 MCP 怎么选很多初学者会纠结到底用 Tools 还是 MCP。我的判断是如果工具只在本服务内使用不存在跨团队、跨语言复用需求用Tool就够了。如果工具要被多个 Agent、多种技术栈共享或者需要独立升级、独立部署上 MCP 更合适。如果团队还在初期探索阶段先用Tool跑通闭环后续再抽 MCP Server完全来得及。对比维度Tool 直接注册MCP 接入实现成本低一个注解一个方法中需要部署 Server 或客户端复用性限于当前应用跨应用、跨语言复用部署耦合进程内独立服务可独立扩展适合阶段原型、单体应用中大型团队、平台化阶段7. Skills 与多层记忆让 Agent 拥有可复用能力和长期状态有了 ToolsAgent 能干活了但离“企业级”还差两步经验沉淀和状态记忆。7.1 Skills 的本质Skills 不是 Spring AI 强制规定的 API它更像一种工程组织方式。一个 Skill 可以包含固定的 System Prompt 片段。一组相关的 Tools。输出格式模板。异常处理逻辑。举个例子“机票改签 Skill”大概是这样的流程先查询乘客订单再判断是否满足改签条件然后查询可选航班计算差价最后生成改签确认话术。如果你把这些逻辑拆开放在 Service 里Agent 每次都要靠模型自己推理组合如果封装成 SkillAgent 只需要识别“用户要改签”然后进入这个 Skill。7.2 Skill 的 Java 实现思路一个简单但实用的 Skill 可以这样设计package com.example.aiagent.skill; import org.springframework.ai.chat.client.ChatClient; import org.springframework.stereotype.Component; Component public class FlightChangeSkill { private final ChatClient chatClient; public FlightChangeSkill(ChatClient chatClient) { this.chatClient chatClient; } public String execute(String userMessage, String userId) { return chatClient.prompt() .system(你是机票改签助手。执行改签时必须遵循以下步骤 1. 先查询用户订单 2. 判断是否满足免费改签条件 3. 查询可改签航班 4. 计算差价 5. 输出改签确认信息。) .user(userMessage) .call() .content(); } }这个例子展示的是“提示词级别的 Skill”。更复杂的 Skill 还可以把ChatClient的调用封装成对象方法便于单元测试。总之Skills 的核心价值是让 Agent 在重复任务上有稳定表现不依赖模型每次的随机发挥。7.3 多层记忆会话、用户、长期Agent 开发里记忆是最容易失控的部分。先做分层第一层会话记忆。保存当前会话的对话历史这是保证多轮对话连贯的基础。Spring AI 提供了ChatMemory接口可以基于内存、Redis 等实现。配置方式类似spring: ai: chat: memory: enabled: true window-size: 20第二层用户记忆。保存用户长期信息比如“用户是金卡会员”“用户常飞北京-上海航线”。这些数据可以从业务系统同步到 Agent 上下文。在航空项目里这就意味着每次对话前Agent 会把用户偏好注入系统提示词。第三层长期记忆。适合用向量数据库存储历史交互记录。当新问题进来时先做语义检索找到用户过去问过的类似问题、处理过的历史订单再交给模型回答。这个能力在 Spring AI 里通常与向量存储组件配合本文先不展开。下面给出一个简单的用户记忆注入示例Service public class UserMemoryService { public String buildUserContext(String userId) { // 实际项目从数据库或缓存查询用户信息 return 用户ID: userId 会员等级: 金卡常飞航线: 北京-上海偏好靠窗座位。; } }然后在 Agent 调用前把这段用户画像拼进系统提示词String userContext userMemoryService.buildUserContext(userId); String answer chatClient.prompt() .system(以下是用户画像信息请结合这些信息为用户服务 userContext) .user(userMessage) .call() .content();这里的要点是记忆不是越大越好。把所有历史都塞进上下文会让模型“注意力稀释”响应变慢成本变高还容易出错。更务实的做法是短期记忆保留关键对话轮次长期记忆只选择与当前问题最相关的片段注入。8. 跑通全链路运行验证与效果演示现在把前面的模块串起来实现一个简单但完整的 Agent 请求入口。RestController RequestMapping(/agent) public class AgentController { private final ChatClient chatClient; public AgentController(ChatClient chatClient) { this.chatClient chatClient; } PostMapping(/chat) public String chat(RequestParam String userId, RequestBody String userMessage) { return chatClient.prompt() .system(你是一个航空出行助手。如果用户询问航班使用航班查询工具如果询问天气使用天气工具。回答要简洁准确。) .user(userMessage) .call() .content(); } }启动项目后可以发一个请求测试。curl -X POST http://localhost:8080/agent/chat?userId1001 \ -H Content-Type: text/plain \ -d 明天北京到上海的航班有哪些预期输出是一个航班列表比如根据查询结果明天北京到上海有以下航班 1. CA1519国航08:00 起飞10:15 到达票价 1280 元。 2. MU5102东航09:30 起飞11:45 到达票价 980 元。判断成功的关键不是输出是否“漂亮”而是下面三点模型是否识别出了正确意图。是否真正调用了searchFlights工具。返回内容是否基于真实工具结果而不是模型编造。如果你在日志里看到类似“Calling tool: searchFlights”的记录说明工具调用链路已经打通。如果请求返回“我不清楚明天的航班”但日志里没有工具调用记录优先排查Tool是否注册成功、工具描述是否清晰。9. 常见问题与排查思路下面整理几类在 Spring AI Agent 开发中经常遇到的问题按“现象 - 原因 - 排查 - 方案”的格式给出。问题现象可能原因排查方式解决方案模型返回 content 为空没有业务回复使用了兼容 OpenAI 协议的服务但响应字段不标准或模型把输出放到了其他字段打印完整响应体查看实际返回的 JSON 结构确认模型名称是否正确换用 OpenAI 兼容模型如果空 content 与 DeepSeek 兼容接口有关重点检查模型响应中的content与reasoning_content字段必要时调整模型参数或版本Agent 没有调用任何工具直接回答工具未注册进 ChatClient工具描述不清晰系统提示词没有引导检查 ChatClient Bean 是否通过defaultTools注册查看工具描述测试时要求模型“使用工具回答”补充defaultTools优化Tool的 description在系统提示词中加入工具使用规则MCP 工具连接超时或不可用MCP Server 未启动地址错误网络策略限制用 curl 或浏览器访问 MCP Server 地址查看服务日志确认 Server 启动成功检查地址和端口确认网络连通性多模型切换后效果明显下降新模型的 Function Calling 能力较弱参数差异提示词不兼容单独测试新模型的工具调用对比响应内容调整模型参数为不同模型准备不同的提示词模板上下文太长导致超限或费用飙升每轮对话把全部历史都传给模型打印实际发送的 prompt观察 token 消耗使用窗口化记忆摘要历史只注入关键记忆工具返回数据格式复杂模型回答混乱工具返回值结构不友好查看模型看到的工具返回结果简化工具返回值在工具中预先把数据整理成自然语言片段其中“content 为空”这个问题在接入部分 OpenAI 兼容接口时比较容易出现因为不同平台的响应结构存在差异。调试时不要只看content()要打印完整的响应体确认数据是真正为空还是只是被框架过滤了。10. 最佳实践与工程建议到这里项目已经能跑通了。但如果要从 Demo 走向生产还有一些工程层的事情必须做好。10.1 配置与密钥管理大模型 API Key 是核心敏感信息绝不能提交到 Git 仓库。推荐使用环境变量、配置中心或密钥管理平台。在 CI 流程中还要加上密钥扫描。10.2 工具安全边界Tools 越强大风险越大。一个能执行 SQL、操作文件系统、调用支付接口的 Agent如果被恶意提示词利用后果非常严重。建议做到工具权限最小化。给 Agent 的工具只开放当前业务必需的能力。高危操作二次确认。例如“改签”和“退票”这类操作在 Agent 执行前要弹出确认或者走审批流。工具链访问控制。MCP Server 只暴露可信工具不建议直接开放任意数据库或 shell 工具。10.3 日志与可观测性Agent 应用最怕“黑盒”。建议记录以下几类日志请求入参用户 ID、会话 ID、消息内容。模型调用使用的模型、token 数、耗时、完整响应。工具调用触发了哪个工具、入参、返回值、是否异常。Agent 决策链路每一步模型做了什么选择。有了这些日志线上问题才能快速定位。Spring AI 2.x 也提供了很多指标和可观测性扩展生产项目建议接入。10.4 测试策略不要只看“最终回答好不好”。更要测试这些维度工具调用触发准确率意图明确时是否总调用正确工具。参数解析正确率用户说“北京到上海”工具参数是否解析为from北京, to上海。异常兜底工具执行失败时模型是否给出合理提示。多轮边界用户中途换话题是否还记得上下文。可以把典型场景写成单元测试和集成测试纳入 CI。10.5 流程与版本管理Agent 应用不是“训练一次就完事”。提示词改动、工具新增、模型切换都会影响效果。建议把系统提示词、工具描述、Agent 流程设计纳入版本管理并通过灰度验证后再全量发布。11. 总结与后续学习方向到这我们实际走通了一条完整的链路Spring AI 作为底层框架接入多模型通过 Tools 和 MCP 让 Agent 调用真实业务系统用 Skills 封装可复用能力用多层记忆保持用户与对话状态。这个智能航空项目虽然以航班查询为最小案例但它背后的问题模型——如何让大模型与 Java 业务系统安全、稳定、可控地协作是所有 AI Agent 项目的共性。接下来值得继续深入的方向有三个。第一个是 RAG。把企业内部的航班政策、服务条款、退改签规则灌入向量数据库让 Agent 在回答时先检索再生成这能显著减少模型“幻觉”。第二个是事件驱动与异步化。真实的企业 Agent 不会总是在 HTTP 请求里等模型算完更常见的模式是任务队列、WebSocket 推流、定时触发等。把 Agent 的执行链路和业务事件总线结合是工程化的重要一步。第三个是多智能体协作。一个助手拆成“客服 Agent”“订单 Agent”“库存 Agent”它们之间通过消息传递协作。Spring AI 的生态也在不断完善这类能力。最后给你一个实际的建议不要一开始就追求大而全。先用一个业务场景跑通“模型 工具 记忆”的最小闭环再逐步叠加 MCP、Skills、RAG。这篇文章里的代码只是骨架真正的价值在于你把它接到自己的业务数据源上。建议先收藏这篇文章动手写一个自己的 Agent Demo遇到问题再回来对照排错清单。这一步迈出去了你会发现在 Java 里做 AI Agent并没有想象中那么难。