ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

智能体协议选型实战:MCP、A2A、ANP 最小可运行 Demo 与避坑指南

2026/10/6 9:45:02 拓冰建站 浏览量
智能体协议选型实战:MCP、A2A、ANP 最小可运行 Demo 与避坑指南 简介这份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 mcpidea 插件通义灵码怎么使用 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( namecount_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(funknown 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(typetext, textf{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 Schemarequired字段决定客户端是否强制传参。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 MapString, 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 MapString, Object handleTask(RequestBody MapString, 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。但如果你要构建一个开放生态让智能体在互联网上自动发现彼此、按需组网就需要 ANPAgent 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, bhello 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 A2AMCP 管工具A2A 管 Agent 间任务流转组合使用的典型架构是每个 Agent 内部用 MCP 挂载自己需要的工具Agent 之间用 A2A 互相调用如果未来要接入外部生态再考虑 ANP 做发现层。这个分层的好处是每层可以独立演进MCP 换版本不影响 A2A 通信A2A 改协议不影响工具实现。验证组合方案是否跑通我习惯用一个最小闭环测试Agent A 通过 MCP 查数据库拿到库存然后通过 A2A 把库存数据发给 Agent BAgent B 生成报价单后通过 A2A 回传。这个链路跑通说明两层协议都工作正常。测试时重点看三个地方MCP 工具返回的数据结构是否和 A2A 任务输入对得上、A2A 的 taskId 在两端是否一致、超时重试时有没有重复执行。最后说一个我自己的习惯每次引入新协议前先花半天写一个最小可运行 demo不接业务逻辑就跑通发现→调用→返回这个最短路径。MCP 的 demo 是一个工具加一个客户端配置A2A 的 demo 是一个 Agent Card 加一个任务端点ANP 的 demo 是一对 DID 加一次签名验签。demo 跑通了再往业务里嵌比直接改生产代码试错成本低得多。希望帮到你。本文还有配套的精品资源点击获取