简介:这份PPT资料面向大模型与人工智能方向的开发者、架构师及技术决策者,系统梳理智能体通信协作领域的三大主流协议——MCP、A2A与ANP。内容从未来智能体互联网对协议的需求切入,逐一剖析MCP的Root、Sampling、Prompt、Resource、Tools等核心概念与初始化、操作、关闭三大流程,A2A基于任务的企业内部智能体协作模式,以及ANP面向数十亿智能体构建开放协作网络的愿景,并对比三者差异、优缺点与未来角色。资源包为1个pptx文件,约5.23MB,结构完整、图文并茂,适合用于技术分享、方案选型或自学参考。目前已有219人学习下载。通过这份资料,读者可快速建立对智能体协议体系的整体认知,理解数据孤岛、互联互通与标准化三大挑战的应对思路,为后续架构设计与技术选型提供参考。
1. 三个协议摆在面前,先搞清楚它们各自解决哪一段问题
如果你最近在给团队做智能体架构选型,大概率会遇到这个场景:老板丢过来一份《深入对比智能体协议:MCP、A2A、ANP.pptx》,让你评估到底该用哪个。打开一看,三个缩写,每个都号称是"智能体时代的 HTTP",但翻完 PPT 你还是不知道明天该写哪行代码。我一开始也这样,直到把三个协议分别跑通了一个最小 demo,才真正理解它们不是竞品关系,而是各自卡在智能体协作链路的不同位置。MCP 解决的是"智能体怎么调用工具和数据",A2A 解决的是"智能体之间怎么互相通信",ANP 想解决的是"智能体怎么在开放网络里被发现和组网"。搞混这三个层次,选型必然翻车。这篇笔记按我实际落地的顺序,把三个协议的最小可运行路径、关键参数和踩过的坑一次讲清楚,适合正在做智能体平台选型或集成的工程师。
2. MCP:把工具调用从"每个框架写一遍"变成"写一遍到处用"
2.1 MCP 到底在标准化什么
MCP 全称 Model Context Protocol,核心思路是把"模型需要调用的外部能力"抽象成统一的 Server,任何支持 MCP 的客户端都能直接挂载。在没有 MCP 之前,你要让 Claude 调数据库、让 GPT 调文件系统、让本地模型调内部 API,每个组合都得写一套适配层。MCP 把这层适配收敛成三个原语:Tools(可调用的函数)、Resources(可读取的数据)、Prompts(预置的提示模板)。客户端负责发现和调用,Server 负责实现,两边通过 JSON-RPC 2.0 通信。
这个设计的关键价值在于解耦。你的工具实现只写一次 MCP Server,之后不管换哪个支持 MCP 的客户端——不管是 IDE 插件、桌面应用还是自建 Agent 框架——都能直接复用。热搜里频繁出现的"codex 接入 figma mcp""idea 插件通义灵码怎么使用 mcp 链接 oracle",本质上都是在做同一件事:把某个具体能力包装成 MCP Server,然后让手上的客户端去连。
2.2 写一个最小 MCP Server 并跑通
我一般用 Python 的mcp包起手,因为它把 JSON-RPC 的样板代码封装得比较干净。下面是一个只暴露一个工具的最小 Server,功能是查询本地 SQLite 里的订单数量:
# minimal_mcp_server.py import asyncio import sqlite3 from mcp.server import Server from mcp.server.stdio import stdio_server from mcp.types import Tool, TextContent app = Server("order-query-server") @app.list_tools() async def list_tools() -> list[Tool]: # 声明本 Server 暴露哪些工具,客户端启动时会拉取这个列表 return [ Tool( name="count_orders", description="统计指定状态的订单数量", inputSchema={ "type": "object", "properties": { "status": { "type": "string", "description": "订单状态,如 paid / shipped / refunded" } }, "required": ["status"] } ) ] @app.call_tool() async def call_tool(name: str, arguments: dict) -> list[TextContent]: # 实际执行工具逻辑,这里直接查 SQLite if name != "count_orders": raise ValueError(f"unknown tool: {name}") status = arguments["status"] conn = sqlite3.connect("orders.db") cur = conn.execute( "SELECT COUNT(*) FROM orders WHERE status = ?", (status,) ) count = cur.fetchone()[0] conn.close() return [TextContent(type="text", text=f"{status} 订单数:{count}")] async def main(): # stdio 模式:客户端通过标准输入输出与本进程通信 async with stdio_server() as (read, write): await app.run(read, write, app.create_initialization_options()) if __name__ == "__main__": asyncio.run(main())逻辑说明:list_tools返回工具清单和 JSON Schema 格式的参数定义,客户端据此知道有哪些工具可用、每个参数什么类型。call_tool是实际执行入口,参数校验由客户端按 Schema 做,但 Server 侧仍要防御性检查。stdio_server是最简单的传输方式,适合本地进程;如果要跨机器部署,换成 SSE 或 Streamable HTTP 传输。
参数说明:inputSchema必须写标准 JSON Schema,required字段决定客户端是否强制传参。description会直接进入模型的上下文,写得越清楚模型选工具越准——这是最容易忽视的调优点。
2.3 客户端挂载与调试
Server 写好后,在支持 MCP 的客户端里配置启动命令即可。以常见的 JSON 配置为例:
{ "mcpServers": { "order-query": { "command": "python", "args": ["/path/to/minimal_mcp_server.py"], "env": {} } } }配置完重启客户端,正常情况下工具列表里会出现count_orders。如果没出现,先看客户端日志里有没有 JSON-RPC 握手失败,再确认 Python 路径和脚本路径是否绝对路径。热搜里"codex无法找到mcp"这类问题,九成是路径或环境变量没配对,跟协议本身无关。
提示:调试 MCP Server 时,可以先用
mcp包自带的 inspector 工具单独跑,确认 Server 本身没问题,再排查客户端配置。
3. A2A:让两个独立智能体互相"打电话"
3.1 A2A 与 MCP 的边界在哪
MCP 管的是"智能体到工具"的纵向调用,A2A 管的是"智能体到智能体"的横向通信。举个具体场景:你有一个负责查库存的 Agent 和一个负责生成报价单的 Agent,它们可能由不同团队用不同框架开发,部署在不同机器上。A2A 定义的就是这两个 Agent 怎么互相发现能力、怎么发起任务、怎么回传结果。
A2A 的核心概念是 Agent Card——一张描述"我是谁、我能做什么、怎么调用我"的元数据卡片,通常挂在/.well-known/agent.json路径下。调用方先拉取 Agent Card,理解对方能力,再通过标准化的任务接口发起请求。热搜里"如何把 agent 暴露出 a2a agentcoard"问的就是这一步。
3.2 用 Spring 起一个 A2A Agent 的最小路径
热搜里"a2a spring"出现频率很高,说明不少团队在 Java 侧落地。下面用 Spring Boot 搭一个最小 A2A Agent 的骨架:
// AgentCardController.java @RestController public class AgentCardController { @GetMapping("/.well-known/agent.json") public Map<String, Object> agentCard() { // Agent Card 描述本 Agent 的能力和调用入口 return Map.of( "name", "quote-generator", "description", "根据库存和折扣规则生成报价单", "url", "http://localhost:8080/a2a", "version", "1.0.0", "capabilities", Map.of( "streaming", false, "pushNotifications", false ), "skills", List.of( Map.of( "id", "generate-quote", "name", "生成报价单", "description", "输入商品 ID 和数量,返回报价", "inputModes", List.of("application/json") ) ) ); } }逻辑说明:Agent Card 是 A2A 的发现入口,调用方通过它知道这个 Agent 支持哪些 skill、用什么传输方式、是否支持流式。url字段指向实际的任务接收端点。skills数组里每个条目对应一个可调用的能力,inputModes声明接受的内容类型。
参数说明:capabilities.streaming决定是否支持 SSE 流式返回,如果设为 true,任务端点需要实现流式响应。version用于调用方做兼容性判断,改接口时务必同步升版本。
3.3 任务调用与状态回传
调用方拿到 Agent Card 后,向url指向的端点 POST 一个任务请求:
// A2aTaskController.java @PostMapping("/a2a") public Map<String, Object> handleTask(@RequestBody Map<String, Object> task) { // task 里包含 skillId、输入参数、任务 ID String skillId = (String) task.get("skillId"); if (!"generate-quote".equals(skillId)) { return Map.of("status", "failed", "error", "unsupported skill"); } // 实际业务逻辑省略,返回标准任务结果结构 return Map.of( "taskId", task.get("taskId"), "status", "completed", "artifacts", List.of( Map.of("type", "text", "content", "报价单内容...") ) ); }逻辑说明:A2A 的任务模型是异步的,调用方发任务后拿到 taskId,再通过轮询或推送获取最终结果。上面简化成同步返回,生产环境建议按规范实现任务状态机(submitted → working → completed/failed)。
参数说明:taskId由调用方生成,用于幂等和追踪。artifacts是结果载体,可以是文本、文件或结构化数据。如果任务耗时长,status先返回 working,后续通过回调或轮询更新。
4. ANP:开放网络下的智能体发现与组网
4.1 ANP 想解决的是"没有中心目录"的问题
MCP 和 A2A 都隐含一个前提:你知道要连谁。MCP 你手动配 Server 地址,A2A 你手动填 Agent Card 的 URL。但如果你要构建一个开放生态,让智能体在互联网上自动发现彼此、按需组网,就需要 ANP(Agent Network Protocol)。ANP 的核心是去中心化的身份标识(DID)加服务发现机制,让智能体不依赖任何中心化注册表就能找到对方。
这个方向目前成熟度最低,落地案例也最少。我实际试过的场景是:用 DID 给每个 Agent 分配一个可验证身份,通过 DHT(分布式哈希表)发布和查找 Agent 能力描述。下面是一个用 Python 做 DID 身份生成的最小示例:
# did_identity.py from cryptography.hazmat.primitives.asymmetric import ed25519 import base58 import hashlib def generate_did(): # 生成 Ed25519 密钥对作为 Agent 的身份根 private_key = ed25519.Ed25519PrivateKey.generate() public_key = private_key.public_key() pub_bytes = public_key.public_bytes_raw() # DID 标识 = did:anp: + 公钥的 base58 编码 did = "did:anp:" + base58.b58encode(pub_bytes).decode() return did, private_key def sign_message(private_key, message: bytes) -> str: # 用私钥对消息签名,接收方用 DID 里的公钥验签 signature = private_key.sign(message) return base58.b58encode(signature).decode() if __name__ == "__main__": did, priv = generate_did() print("Agent DID:", did) sig = sign_message(priv, b"hello anp") print("Signature:", sig)逻辑说明:DID 把身份和公钥绑定,不需要中心化 CA。签名机制保证消息确实来自声称的 Agent。实际组网时,Agent 把自己的 DID、能力描述、通信端点打包成服务记录,发布到 DHT 或分布式目录,其他 Agent 按能力关键词查找。
参数说明:did:anp:是方法前缀,具体方法名按 ANP 规范定义。Ed25519 选它是因为签名短、验证快,适合高频通信场景。base58 编码避免特殊字符,方便在 URL 里传递。
4.2 ANP 当前的落地边界
必须说清楚:ANP 目前没有像 MCP 那样被广泛采用的客户端生态,也没有 A2A 那样明确的跨厂商支持。我试过的 DHT 方案在局域网内几十个 Agent 的规模下能跑,但跨公网、跨组织的互操作性还没验证。如果你的场景是企业内部可控环境,用中心化注册表加 A2A 就够了;只有当你确实需要开放网络下的无中心发现,才值得投入 ANP 方向。
5. 避坑:三个协议落地时最容易翻车的五个地方
5.1 MCP Server 启动成功但客户端看不到工具
现象:手动跑 Server 脚本没问题,客户端配置也写了,但工具列表始终为空。
原因:九成是 stdio 通信被污染。MCP 用标准输出传 JSON-RPC 消息,如果你的 Server 里任何一行print()往 stdout 写了日志,协议消息就被冲掉了。
解决:所有调试日志走sys.stderr或 logging 模块配到 stderr,绝对不要用print()。这是血泪经验,我第一次调 MCP 卡了两小时就因为这个。
5.2 A2A Agent Card 路径大小写不一致
现象:调用方按规范请求/.well-known/agent.json,返回 404。
原因:不同 Web 框架对路径大小写的处理不一样,有的框架默认把/.well-known/当成静态资源路径,路由没匹配上。
解决:显式注册路由,别依赖静态资源映射。Spring 里用@GetMapping("/.well-known/agent.json")明确声明,Flask 里用@app.route("/.well-known/agent.json")。部署后先用 curl 直接验证这个路径能返回 JSON。
5.3 MCP 工具参数 Schema 写太松导致模型乱传参
现象:模型调用工具时传了不存在的参数,或者该传的没传。
原因:inputSchema里required没写全,或者description太模糊,模型只能猜。
解决:每个参数都写清楚类型、枚举值和示例。required数组一个都不能漏。宁可 Schema 写严一点让模型报错重试,也不要写松了让它传垃圾数据进来。
5.4 A2A 任务超时没有幂等保护
现象:调用方超时重试,同一个任务被执行了两次,产生重复报价单。
原因:A2A 任务模型是异步的,调用方拿不到及时响应就会重发,如果服务端不按 taskId 去重,就会重复执行。
解决:服务端维护 taskId 到执行状态的映射,收到重复 taskId 直接返回已有状态,不要重新执行。taskId 由调用方生成,服务端只做去重判断。
5.5 ANP 的 DID 解析依赖外部网络导致启动慢
现象:Agent 启动时要解析其他 Agent 的 DID,网络不通时整个启动流程卡住。
原因:DID 解析如果同步阻塞在主流程里,外部依赖一挂全挂。
解决:DID 解析结果本地缓存,设置合理 TTL。启动时异步预热,不要阻塞主流程。解析失败要有降级策略,比如用上次缓存的结果继续跑。
6. 选型决策:什么阶段用哪个协议,以及怎么组合
三个协议不是互斥的,实际架构里往往是组合使用。我一般按这个思路做决策:
| 场景 | 首选 | 理由 |
|---|---|---|
| 单个 Agent 调外部工具/数据 | MCP | 生态成熟,客户端支持广,改造成本低 |
| 两个自研 Agent 跨团队通信 | A2A | 接口标准化,Agent Card 解决发现和描述问题 |
| 开放网络下无中心发现 | ANP | 唯一支持去中心化身份和组网的方向,但成熟度低 |
| 企业内多 Agent 协作 | MCP + A2A | MCP 管工具,A2A 管 Agent 间任务流转 |
组合使用的典型架构是:每个 Agent 内部用 MCP 挂载自己需要的工具,Agent 之间用 A2A 互相调用,如果未来要接入外部生态再考虑 ANP 做发现层。这个分层的好处是每层可以独立演进,MCP 换版本不影响 A2A 通信,A2A 改协议不影响工具实现。
验证组合方案是否跑通,我习惯用一个最小闭环测试:Agent A 通过 MCP 查数据库拿到库存,然后通过 A2A 把库存数据发给 Agent B,Agent B 生成报价单后通过 A2A 回传。这个链路跑通,说明两层协议都工作正常。测试时重点看三个地方:MCP 工具返回的数据结构是否和 A2A 任务输入对得上、A2A 的 taskId 在两端是否一致、超时重试时有没有重复执行。
最后说一个我自己的习惯:每次引入新协议前,先花半天写一个最小可运行 demo,不接业务逻辑,就跑通"发现→调用→返回"这个最短路径。MCP 的 demo 是一个工具加一个客户端配置,A2A 的 demo 是一个 Agent Card 加一个任务端点,ANP 的 demo 是一对 DID 加一次签名验签。demo 跑通了再往业务里嵌,比直接改生产代码试错成本低得多。希望帮到你。
本文还有配套的精品资源,点击获取