Spring AI MCP 双向通信深度实战:四大 Provider、采样回调与双模部署,重塑 AI 微服务架构
Spring AI MCP 双向通信深度实战:四大 Provider、采样回调与双模部署,重塑 AI 微服务架构
摘要
当大多数团队还停留在 Function Calling 的“单向工具调用”阶段时,MCP(Model Context Protocol)已经把 AI 系统推进到了“双向通信、可订阅上下文、服务端反向采样、跨模型协同”的新阶段。本文不只讲怎么把 Spring AI MCP 跑起来,更从架构设计、并发治理、生产级代码组织、部署拓扑、可观测性与故障隔离等维度,系统回答一个核心问题:如何把 MCP 从 Demo 做成真正可上线的 AI 微服务基础设施。
一、为什么 Function Calling 已经不够用了
很多团队第一次做 AI 应用,往往会沿着这条路径前进:
- 用户请求进入网关。
- 大模型根据意图决定是否调用工具。
- 工具返回 JSON。
- 模型把 JSON 组织成自然语言,再返回前端。
这条路径在“小工具、短链路、弱上下文”的场景里很好用,但一旦进入生产环境,问题就会迅速暴露:
- 上下文管理失控:商品知识、规则文档、FAQ、图片说明、策略模板全靠手动拼 Prompt,token 消耗高,更新不一致。
- 链路完全单向:工具只能“被调用”,不能主动告诉客户端“我正在处理”“我需要更多推理”“我希望调用你的模型能力做补全”。
- 缺少协议级资源治理:静态文本、动态配置、二进制文件、模板提示词没有统一寻址方式,缓存、订阅、版本治理都很难做。
- 服务编排割裂:业务微服务明明知道上下文、知道结果、知道异常,但最终所有二次总结、润色、升级决策仍然要由客户端重新发起一轮模型调用。
- 模型能力无法回流到服务端:服务端拿到工具结果后,往往还想做总结、改写、翻译、安抚、结构化提炼,但传统调用链里,服务端并不能“反向要求客户端去采样”。
这正是 MCP 的价值所在。
MCP 不是“又一个函数调用协议”,而是把 AI 应用中的四类能力抽象成统一协议面:
- Resources:可寻址、可读取、可订阅的上下文资源。
- Prompts:参数化提示模板,不再把 Prompt 写死在业务代码里。
- Tools:模型可调用的业务能力。
- Sampling:服务端反向请求客户端进行模型推理,这是 MCP 最容易被低估、但最具架构价值的能力。
如果说 Function Calling 解决的是“模型怎么调用工具”,那么 MCP 解决的是“模型、上下文、服务、推理能力如何形成一个双向协作系统”。
二、MCP 到底改变了什么
2.1 从单次调用,变成双向会话
传统 Function Calling 更接近“HTTP API + JSON Schema”思维,而 MCP 更接近“有状态协议 + 能力协商 + 双向消息”思维。
在一次 MCP 会话中,双方会先进行初始化与能力协商,然后进入持续通信阶段。客户端可以请求资源、提示模板和工具,服务端也可以主动发送通知,甚至发起 sampling/createMessage 请求,让客户端调用自己所连接的大模型来完成推理。
这意味着:
- 服务端不再只是“被动执行函数”。
- 客户端不再只是“请求发起者”。
- 模型能力开始成为一种可以跨边界回流的基础设施能力。
2.2 从“Prompt 拼接”变成“上下文治理”
MCP 的 Resources 本质上是一套可寻址上下文协议。与其每次把商品说明、售后政策、物流模板拼进 prompt,不如把它们变成有 URI 的资源:
product://sku/10001policy://refund/v2faq://delivery/delayfile://campaign/poster-2026-04
资源一旦协议化,就能自然引出工程能力:
- 资源缓存
- 资源订阅与更新通知
- 资源权限控制
- 资源版本治理
- 多租户隔离
2.3 从“工具返回结果”变成“工具驱动业务闭环”
传统工具调用只关心结果值,而生产系统真正关心的是过程:
- 任务开始了没有
- 目前做到哪一步
- 需要用户等待多久
- 下游异常后是否要自动降级
- 是否需要再调用一次模型,把结果转成用户可读内容
MCP 的通知与采样机制,让服务端第一次可以把这些过程暴露出来,而不必把所有逻辑都堆回客户端。
三、Spring AI MCP 在体系中的位置
Spring AI 把 MCP 能力落到了 Spring Boot 的工程模型里,这一点非常关键。因为真正决定 MCP 能不能进生产的,不是协议本身,而是它能不能:
- 像普通 Spring 组件一样装配
- 像普通微服务一样部署
- 像普通业务能力一样治理
- 像普通链路一样被观测、限流、审计、降级
在 Spring AI 里,你可以把 MCP 服务端理解成一个“暴露 AI 能力面的 Spring Boot 应用”,它对外输出的不是普通 REST API,而是 MCP 能力集合。
从实现方式看,通常有两种路线:
- 注解式暴露能力:更贴近业务开发,适合生产项目日常维护。
- 底层 Provider / Spec 显式注册:更灵活,适合框架封装或深度定制。
本文重点讲“四大 Provider 能力”的架构与落地,但在代码示例里优先采用更适合团队工程化维护的注解式写法,并解释它与底层能力模型的一一映射关系。
四、生产级目标:我们到底要构建什么
本文以“电商智能客服中台”为例,构建一个具备以下能力的 MCP 微服务:
- 对外暴露商品资料、售后政策、履约 SLA 等资源
- 提供催单、退款判断、物流查询等 Prompt 与 Tool
- 在长耗时查询过程中推送进度与日志
- 在工具执行后,服务端主动发起 Sampling,请客户端模型生成安抚话术或处理摘要
- 同时支持 Streamable HTTP 与 Stdio 两种传输模式
- 能在本地开发、容器部署、Kubernetes 集群中稳定运行
- 面向高并发场景具备缓存、限流、超时、重试、幂等、观测与安全控制
参考架构如下:
flowchart LR U["用户 / 前端"] --> G["AI Gateway / Chat Client"] G --> C["MCP Client"] C <-->|"Streamable HTTP / Stdio"| S["Spring AI MCP Server"] S --> R["Resource Layer<br/>商品/政策/FAQ"] S --> P["Prompt Layer<br/>标准话术模板"] S --> T["Tool Layer<br/>订单/物流/退款判定"] T --> O["订单服务"] T --> L["物流服务"] T --> K["知识库 / 向量库"] S --> E["事件与观测层<br/>Metrics / Logs / Traces"] S -. "sampling/createMessage" .-> C C --> M["LLM Provider<br/>OpenAI / Claude / 本地模型"]这张图里最重要的一条链路不是客户端调用工具,而是虚线部分:服务端向客户端发起采样请求。这条链路让 MCP 真正具备了双向系统的特征。
五、核心原理:四大能力如何映射到架构分层
5.1 Resources 不是“静态文本”,而是上下文资产层
Resources 最适合承载这类信息:
- 商品详情
- 规则与制度
- 运营活动说明
- 产品手册
- FAQ
- 可被引用的二进制内容元数据
从架构上看,Resources 不应该直接耦合数据库表,而应该经过一层“上下文资产服务”封装:
- 上游数据源:MySQL、CMS、对象存储、配置中心
- 中间治理:缓存、版本、权限、格式化
- 对外协议:URI + metadata + 内容读取
5.2 Prompts 不是“字符串模板”,而是业务策略面
Prompts 的本质是把提示工程从代码细节提升到业务策略层。
典型收益有三点:
- 运营、策略、算法团队可以共建模板
- 可以按租户、品牌、渠道、语种做差异化编排
- 可以纳入灰度发布、AB 实验和版本回滚
5.3 Tools 不是“函数集合”,而是业务能力编排面
一个生产级 Tool 不只是方法调用,它至少要考虑:
- 参数校验
- 幂等
- 下游超时
- 熔断降级
- 结果标准化
- 进度通知
- 审计日志
- 失败恢复
5.4 Sampling 不是“再调一次模型”,而是跨边界推理编排
Sampling 最关键的价值,是让服务端在业务链路内部“借用”客户端的模型能力。
这意味着:
- 模型连接、模型凭据、模型选择策略可以留在客户端侧
- 服务端专注业务逻辑与上下文治理
- 模型能力变成协议层能力,而不是代码层强耦合
在大型系统里,这个设计特别适合做:
- 多模型路由
- 模型降级
- 客户端本地模型优先
- 不同租户绑定不同模型策略
六、项目工程骨架:依赖、模块与配置建议
6.1 Maven 依赖
下面给出一个更接近生产项目的依赖骨架。版本号请以团队当前稳定版本为准,建议统一使用 Spring AI BOM 管理。
<properties> <java.version>17</java.version> <spring-ai.version>1.1.4</spring-ai.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-bom</artifactId> <version>${spring-ai.version}</version> <type>pom</type>