ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

MCP与A2A实战指南:AI Agent落地中的协议分界与架构设计

MCP与A2A实战指南:AI Agent落地中的协议分界与架构设计 过去半年我做企业内部的 AI Agent 落地MCP 这个关键词已经快被聊烂了。它让大模型应用可以像“USB 插头”一样接入外部工具理论上的确很优雅但真正铺到生产环境后我又遇到了另一层问题多个 Agent 之间怎么协作怎么把一个任务递交给另一个智能体执行怎么判断对方到底做完了没有。这些问题在 MCP 的协议边界内回答不了于是我陆续把 A2A 拉进了架构里。这篇文章就把我这两轮实践串起来讲清楚MCP 与 A2A 各自管哪一段怎么把两者接在一起才会是干净的设计以及我在真实联调中踩过、后来也修复掉的坑。适合团队里正准备引入这两个协议的技术负责人、后端工程师和独立开发者参考。1. 协议分界MCP 解决模型到工具A2A 解决 Agent 到 Agent1.1 先搞清楚 MCP 是“模型到工具的桥”不是“两个 Agent 之间的桥”MCP 全称 Model Context Protocol模型上下文协议。你可以把它理解成一层标准化的“工具插头”。过去我们要在 AI 应用里接一个内部查询接口通常是自己写 function calling 的封装每个模型一套、每个工具一套维护成本很快失控。MCP 把这件事收敛成一套 JSON-RPC 2.0 方法客户端负责发现 server 提供的工具和资源模型在推理时通过客户端调用这些工具数据和上下文按规范回传。举个例子。我给一个资产管理系统写了一个 MCP server暴露get_asset_status、list_assets两个工具。AI 客户端初始化时会向 server 发tools/list拿到工具名、描述、参数格式当用户在对话里问“设备 A 现在什么状态”模型会生成一次tools/call参数是设备编号server 再回传结构化结果。整个过程对业务后端是不可见的后端根本不关心调用方是模型还是另一个系统它只看到一个普通请求。这里有个关键逻辑MCP 是“模型到工具”的协议不是“Agent 到 Agent”的协议。一个 MCP server 本身没有“任务”概念它不维护某个任务的执行状态也不区分调用它的进程是主控 Agent 还是普通用户。它更像一个无状态的 RPC 服务只不过请求的发起方通常是大模型应用。很多人把 MCP 当成 Agent 协作的标准这是第一步就理解偏了。1.2 A2A 补上的是“谁负责什么、任务进行到哪一步”的空白A2A 全称 Agent-to-Agent智能体到智能体协议是 2025 年上半年开始在生态里广泛讨论的开放规范。它要解决的是 MCP 刻意不碰的那一部分当一个 Agent 需要把任务交给另一个 Agent 执行时双方怎么相互发现、怎么描述能力、怎么跟踪任务进度、怎么把结果返回。A2A 里两个基本概念是 AgentCard 和 Task。AgentCard 相当于每个 Agent 的“名片加路由表”用 JSON 描述这个 Agent 叫什么、能做什么、支持哪些能力一般放在服务端固定的 well-known 路径下。另一个 Agent 或者编排器拿到卡片后就知道该不该把任务发过来、往哪里发。Task 则是 A2A 的核心状态抽象。一次对话式的任务会有submitted、working、input-required、completed、failed、canceled这样的状态。相比 MCP 的“调用一次返回一次”A2A 更像项目管理客户端发出去的不是一次请求而是一个任务服务端会持续更新这个任务的状态直到终态。这个差异决定了两者在实现时的代码结构完全不同。1.3 两者不是替代关系而是上下游关系所以不要问“MCP 和 A2A 哪个好”要问“我在哪一层需要用哪个”。我把自己的理解整理成一张表做架构评审时非常有用维度MCPA2A核心定位模型连接到工具/数据Agent 与 Agent 协作核心抽象工具、资源、提示词AgentCard、Task、Message通信方式JSON-RPC 2.0支持 stdio 与 HTTP 传输JSON-RPC 2.0支持 HTTP 与 SSE 流式是否维护任务状态否偏向请求-响应是任务有完整生命周期典型调用方大模型应用、IDE、Agent 运行时编排器、其他 Agent典型返回工具执行结果任务状态、事件流、最终内容这张表是我实践的起跑线。先定边界再谈实现后面写代码才不会把两个协议混成一个大杂烩。2. MCP 集成实操从一个最小可运行示例说起2.1 用 FastMCP 搭一个可被客户端发现的 server理论讲多了容易飘先动手。我习惯用 FastMCP 这类 Python SDK 做 MCP server 原型因为工具注册、参数校验、JSON-RPC 封装都被框架处理好了。下面这个例子是我写内部资产管理工具时的最小版本from mcp.server.fastmcp import FastMCP mcp FastMCP(asset-api) mcp.tool() def get_asset_status(asset_id: str) - dict: 按资产编号查询运行状态 # 这里替换成真实的内部服务调用 return {asset_id: asset_id, status: running, cpu: 12.5} mcp.tool() def list_assets(owner: str ) - list: 按负责人过滤资产列表owner 为空时返回全部 return [{asset_id: A-001, name: 工作站, owner: owner or ops}] if __name__ __main__: mcp.run(transportstdio)这段代码本身没做什么复杂的事但它的价值在于任何支持 MCP 的客户端都能直接识别。你可以在 Cursor、Claude Desktop 或自研 Agent 里配一条命令指向这个脚本客户端启动时会先发initialize握手再发tools/list把工具元数据拉走。整个交互不需要你手写一行协议的报文SDK 全包了。客户端配置通常长这样放在mcp.json或 IDE 的 MCP 配置页里{ mcpServers: { asset-api: { command: python, args: [server.py], env: { API_TOKEN: 本地开发用生产走密钥管理 } } } }这里的command和args是指向本地脚本适合开发调试。如果 server 部署在远端就把command换成url改成 HTTP 方式连接。2.2 如果你不用 SDK协议层的几个关键方法必须知道依赖 SDK 会给人一种“协议不难”的错觉但真到排错阶段你迟早要直接看报文。MCP 的核心方法就这几个initialize/notifications/initialized客户端与 server 握手交换 protocolVersion 和 capabilities。最容易出问题的是版本号不匹配老客户端配新 server 经常直接拒绝连接。tools/list/tools/call工具发现和工具调用。tools/list返回的 schema 是 JSON Schema 格式模型能不能正确传参很大程度取决于你写的描述有多直白。resources/list/resources/read把文件、配置、数据库查询结果暴露成只读资源模型可以“读取上下文”而不是“执行动作”。prompts/list/prompts/get类似提示词模板的远程化回调适合打开客户端时自动加载预设指令。实际业务里 90% 的时间是在用 tools 部分。resources 和 prompts 我建议在初期全部关掉只暴露工具先把主链路跑通。过早把文件系统或数据库暴露给模型风险大于收益尤其当调用方行为不可控时。2.3 状态、鉴权和上下文长度三个容易被忽略的设计点MCP 很简单但简单不等于没坑。第一状态。因为 MCP 是请求-响应式工具本身不保存会话状态你需要自己处理“上一步选了资产 A下一步查询资产 A 的日志”这类多轮依赖。常见做法是把中间结果写回数据库或内存缓存然后在下一次tools/call里带上引用 ID。第二鉴权。MCP 规范里的鉴权机制还在完善早期很多 server 直接裸奔。我的建议是stdio 模式只用于本地开发对外服务的 HTTP 模式必须在网关层做身份认证。简单方案是每个客户端一个 token复杂方案是走 OAuth2。具体选哪个取决于你暴露的是内部系统还是公共数据。第三上下文长度。模型拿到的工具描述如果太长会挤占上下文预算。我遇到过把一个 server 的 200 多个工具描述全量暴露的情况每次对话光定义就占了三万多 token。后来我们强制每个工具描述控制在 100 字以内把最常用的工具放在描述最前面效果立竿见影。2.4 周边生态里高频出现的浏览器类工具怎么选聊到 MCP 集成总有人问 Playwright MCP、Browser Use MCP 和 Chrome DevTools MCP 之间怎么选。这个问题本身说明了 MCP 生态正从“接企业 API”延伸到“接管浏览器”。Playwright MCP 把浏览器自动化能力封装成 MCP 工具比如browser_navigate、browser_click、browser_snapshot。它适合做网页操作类任务模型可以按步骤读页面、点按钮、填表单。Browser Use MCP 偏重端到端的 Agent 操作会把“打开页面—查找信息—点击—提取数据”整个流程打包成高层动作。Chrome DevTools MCP 则更底层暴露的是 CDP 协议相关接口适合做性能分析、网络请求拦截这类偏工程化的操作。我的建议很直接如果你的核心诉求是“让模型完整操作流程”选 Browser Use 思路的封装如果你只想给模型一个可靠的浏览器自动化入口自己控制步骤选 Playwright MCP。底层不冲突两个都可以挂但别同时暴露给同一个客户端工具描述会互相干扰模型经常不知道该用哪个。3. A2A 从规范到代码AgentCard、Task 和消息流3.1 AgentCard先让别的 Agent 认识你A2A 没有中心注册中心强调去中心化。每个 Agent 只要把自己的卡片放在固定路径下其他 Agent 通过 URL 就能发现。结合公开规范和社区实践一个最小卡片长这样{ name: order-agent, description: 处理订单查询、退换货和物流跟踪的智能体, url: https://agent.example.com/, capabilities: { streaming: true, pushNotifications: false }, skills: [ { id: order-refund, name: 退款申请 }, { id: order-logistics, name: 物流追踪 } ] }这个卡片解决了两件事让外部 Agent 判断“这事你管不管”以及告诉调用方“你支持流式输出还是只能等最终结果”。如果你的 Agent 没有这个文件别的 Agent 基本没有标准途径了解你更谈不上协作。在协议联调阶段我发现很多团队卡在 AgentCard 的 URL 可达性上卡片文件本身写对了但内网防火墙把外部访问挡住了协作方一直拿不到 404。所以上线前先用浏览器或 curl 打开一遍卡片地址是成本最低的检查。3.2 Task 生命周期用状态机替换“一次调用”A2A 里一切工作单元都是 Task。客户端向 Agent 发送消息后会创建一个 Task然后不断查询任务状态或通过事件流接收状态更新。状态流转大致如下submitted任务刚创建服务端已经收到消息workingAgent 正在处理可能涉及多轮工具调用或内部推理input-requiredAgent 需要用户补充信息比如订单号缺失completed任务结束携带最终消息failed执行失败包含错误信息canceled被客户端取消。这个状态机是我认为 A2A 最有价值的部分。它把“AI 在干活”从黑盒变成了可观测过程编排器可以据此做超时、重试、人工介入。用一个类比MCP 像函数调用拿参数、返回结果A2A 像任务工单有开始、有中间状态、有结束甚至可以要求补充材料。我在企业内部汇报时经常用这个类比非技术同事也能立刻理解两套协议的区别。3.3 消息、Part 与流式输出接 SSE 时的细节A2A 的消息内容用 Part 表达Part 可以是文本、文件对象或结构化数据。客户端与 Agent 之间通过 JSON-RPC 2.0 交换消息其中普通发送用类似message/send的方法流式场景则配合 SSE 让任务状态和内容分块回传。联调时的消息流向一般是客户端拿到 AgentCard 里的 endpoint客户端发起消息发送请求请求体是 JSON-RPC 封装的任务消息服务端返回报文包含 Task id 与当前状态如果是流式客户端监听 SSE 通道收到状态更新事件或最终结果事件终态到达后客户端再用查询接口拉一次完整结果做最终确认可选。我实际写客户端时会优先用流式接口而不是轮询任务状态。轮询虽然简单但任务多的时候非常浪费资源。SSE 断线后再用任务查询接口恢复现场这个组合最稳。A2A 的流式响应本质上是一个持续推送的 JSON-RPC 响应所以你在服务端实现时不要复用普通 HTTP 的返回逻辑要单独处理长连接和事件序列。4. 设计一个双协议架构AI 前端、MCP 工具层、A2A 编排层4.1 三层各司其职的参考拓扑我的团队最终采用的架构很简单但边界很清晰用户 / IDE 应用 ↓ MCP 业务 Agent编排器 ↓ A2A 子 Agent A 子 Agent B ↓ MCP / 内部调用 企业内部服务与数据顶层是用户交互层用 MCP 连接 IDE 或聊天应用目的是让模型能操作工具。中间是编排层它本身也是一个 Agent负责拆解用户目标然后通过 A2A 把子任务分发给下游 Agent。底层是执行层子 Agent 内部再通过 MCP 或普通 HTTP 调企业内部系统。三层的好处是每层只解决一个问题换任何一家模型供应商、换任何协议版本都不会波及相邻层。4.2 别把两个协议塞进同一个服务的同一个端口我在早期踩过一个教训想用一套代码同时对外提供 MCP 和 A2A结果两个协议对“执行”的语义完全不同导致接口设计非常别扭。MCP 的tools/call是一次同步调用期待的结果是“工具执行完给我数据”A2A 的消息发送是一次异步任务期待的结果是“任务开始/推进请关注状态”。如果只做一个服务你当然可以同时挂两个端点、两套路由但状态管理、错误语义、鉴权都得分开实现。与其硬耦合不如拆成两个独立服务或两个独立模块共享业务函数但协议边界各自维护。这是很多刚开始混用的人容易栽跟头的地方。4.3 一个贴近实战的调用流程示例以一个“查询订单并自动发起退款”的场景为例完整协作是这样发生的用户在 AI 应用里说“订单 A-10086 有异常帮我发起退款”。入口 Agent 先调用 MCP 工具get_order_info确认订单 A-10086 属于可退状态。入口 Agent 判断退款动作超出自己的能力范围于是向退款子 Agent 的 A2A 端点发消息创建 Task。退款子 Agent 返回 Task id 和submitted状态同时开始处理内部逻辑调支付网关、写退款记录。入口 Agent 通过流式事件收到working、input-required等状态需要用户确认金额。用户在入口界面确认后入口 Agent 把确认信息通过消息方式回传给退款子 Agent。最终退款子 Agent 推送completed携带退款单号入口 Agent 再通过 MCP 工具写一条通知给用户。这个流程里MCP 负责“读数据/写数据”A2A 负责“跨 Agent 的异步协作与状态跟踪”职责完全没有交叉。我把这个示例保留了很长一段时间作为团队内部的架构范式新人上手很快。5. 联调、日志与排错教程里不容易看到的部分5.1 调试工具与最小复现思路MCP 联调最常用的是命令行直接跑 stdio server看标准输出里来来回回的 JSON-RPC 报文。A2A 则更依赖 curl 和 SSE 调试工具。很多时候排错就三步先把 server 单独启动用官方 SDK 的模拟客户端发一个最小请求确认最小请求能返回预期 JSON 后再接入真实业务逻辑出问题时打开协议层日志对比请求报文与响应报文。如果你不想写模拟客户端直接用 curl 打 HTTP 端点或者把 SSE 流保存到文件里查事件时间线也行。关键是“先隔离协议本身再怀疑业务代码”。我见过大量团队一联调就怀疑协议 bug结果最后几乎都是业务逻辑或参数格式问题。5.2 常见坑版本不匹配、超时、幂等按出现频率我把遇到的坑排个序版本字段不匹配。客户端宣告的 protocolVersion 与服务端不一致时有的实现会直接断连。建议在握手时打出双方版本号再决定要不要做兼容。超时设置太短。A2A 的任务可能运行几十秒甚至几分钟如果你按 MCP 的 2 秒超时去等必然大量失败。要把“网络请求超时”和“任务结果超时”区分开。缺少幂等。重试时如果每次都新建 Task下游 Agent 可能重复执行退款、重复发送通知。我建议每次重试都带上原始 task id 或业务幂等键服务端遇到重复请求直接返回原任务状态。SSE 断线未恢复。长任务跑到一半连接断开客户端如果没有拉取任务状态的逻辑就会一直停留在working状态。必须实现“断开后主动恢复现场”。5.3 统一日志和 trace id跨协议的排错钥匙既然 MCP 和 A2A 同时存在日志就必须打通。我给自己定了一个规矩入口 Agent 收到用户请求时生成一个trace_id之后无论是 MCP 工具调用还是 A2A 子任务分发这个 trace id 都要透传下去。实现上可以在 MCP 请求的附加字段里传可以在 A2A 消息的 metadata 里传也可以靠 HTTP Header 中间透传。一次完整排错链路应该是这样用户在应用里报错日志系统按 trace id 能查到入口 Agent 的决策记录继续按同 id 查到 MCP server 里tools/call的入参和出参再按同 id 查到 A2A 子 Agent 的任务流和最终结果。没有这条链两个服务互相踢皮球排查成本会成倍增长。日志里不要打印完整 token 或敏感字段只保留脱敏摘要。我吃过亏一个 MCP server 的调试日志把内部查询条件完整打出来了运维平台都能看到用户订单号后来改成只打个数和耗时才算收敛。5.4 权限与数据边界协议越开放越要拉住权限这条是我的强烈建议无论 MCP 还是 A2A协议本身不提供完整的业务权限模型。你能在tools/list里暴露多少工具只取决于代码里注册了多少不代表它安全。生产环境需要做到几件事对外暴露的 A2A 端点必须在网关校验调用方身份并限制能创建的 Task 类型MCP 工具按调用者的 token 做数据级隔离避免一个客户端能查所有用户订单接口日志和协议日志里都要保留 trace id但敏感参数要脱敏所有 Agent 跨服务调用都要带幂等键避免重复执行。我在实际项目里吃过一次亏一个 MCP 工具一开始没做 owner 参数过滤任何客户端传任意 asset_id 都能查到数据。后来把 owner 强制绑定到用户身份才算堵住漏洞。这类问题排查成本很高最好在网关层提前约束。6. 写在最后如果你也要推进这两个协议要是你现在正打算在企业里引入 MCP 和 A2A我给的建议优先级是这样的先把 MCP 的“一个 server 加一个客户端”跑通用真实业务工具验证价值不要一上来就铺几十个工具。第二阶段再引入 A2A只选两三个边界清晰的 Agent 做协作试点比如订单处理、告警升级。最后才是统一架构、规范日志、上权限网关。协议本身是开放的生态还在快速演进同一套规范在不同实现之间偶尔也有细微差异。遇到不一致时优先看协议官方文档和对应 SDK 源码不要靠猜。我自己在做的项目里MCP 已经稳定跑在生产A2A 部分还在逐步网关化。每一步改动都不大但边界一直很清晰模型到工具用 MCP智能体到智能体用 A2A。最后再分享一个小技巧给每个 MCP 工具的描述里都加一句“该工具应在什么条件下使用不应在什么条件下使用”。模型读起来更明确调用准确率会明显提升。这句成本几乎为零但收益长期存在。
返回列表