news 2026/10/24 5:11:13

MCP、A2A、ANP三大智能体协议对比与落地实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MCP、A2A、ANP三大智能体协议对比与落地实践

简介:深度聚焦智能体协议对比的PPT,面向AI应用开发工程师、智能体平台架构师及技术决策者,系统梳理MCP、A2A、ANP三类协议的设计思路、核心机制与适用范围。资源共1个文件,格式为pptx,压缩包约5.23MB,便于阅读和二次整理。内容沿“智能体网络需要什么样的协议”这一主线展开,先分析Agent与数字世界交互的主要方式、互联互通趋势及数据孤岛挑战,再逐项拆解MCP的Root、Sampling、Prompt、Resource、Tools等核心概念与初始化、操作、关闭三阶段流程,A2A基于任务的协作模式,以及ANP为数十亿智能体构建开放安全协作网络的愿景,最后落到三者优缺点、适用场景与未来协议形态的对比判断。目前已累计219人学习,适合需要快速建立智能体协议全局认知、辅助技术选型或追踪AI原生协议演进的读者。

1. 智能体协议不是新名词,MCP、A2A、ANP 解决的是不同的“连接”

Cursor 里配置过 MCP 的开发者很多,但等到自己搭多智能体系统时,很多人只盯着 MCP,直到要跨系统协作才发现还有 A2A 和 ANP 两套协议等着对比。先说结论:这三个不是同一赛道的竞品,而是三个不同层级的连接协议。MCP 管的是智能体怎么“够到”外部工具,A2A 管的是两个智能体之间怎么“托人办事”,ANP 则想把智能体连成一张去中心化的开放网络。下面不会停留在 PPT 式的对比表格,而是把三个协议从前置原理拆到最小实现,再落到选型判断和踩坑记录上。适合正在做智能体落地的工程师、以及要给团队做技术预研的负责人。

2. MCP 先把工具接进大模型:三个原语与一条调用链路都理清再动手

2.1 MCP 解决的是“最后一米”:从 Cursor 到 Figma,先看它把什么标准化了

MCP(Model Context Protocol)由 Anthropic 提出并开源,解决的是“模型如何调用外部工具”的最后一米问题。你可以在 Cursor、Claude Desktop 里直接配 mcpServers,也可以把 Figma MCP、Playwright MCP、Chrome DevTools MCP 挂到自己的 Agent 上。国内设计协作工具蓝湖也放出了自己的 MCP 服务,社区里还有 Blender MCP、IDA MCP 这类垂直场景实现。这些工具的共同点是:把原本形态各异的 API 统一成“工具”(Tool)暴露给模型,让模型生成参数、客户端负责执行、结果再回填上下文。

MCP 常被拿来和 RAG 对比。RAG 解决的是“模型不知道的信息怎么进上下文”,MCP 解决的是“模型不能执行的操作怎么让外部系统完成”。两者互补:RAG 负责把静态知识捞回来,MCP 负责把动态操作推出去。理解这一点,后面做工具设计时就不会把文件搜索、数据库查询全塞进提示词里。MCP 的抽象层由三部分构成:Resources 暴露数据、Prompts 暴露可复用的提示模板、Tools 暴露可执行操作。真正让模型“动手”的是 Tools,这也是绝大多数落地场景最先实现的部分。

2.2 最小 MCP Server:用 FastMCP 30 行暴露一个本地文件工具

我一般用 FastMCP 而不是手写官方 Python SDK,因为它会自动把类型注解转成 JSON Schema,省掉手写 inputSchema 的时间。调试期工具定义改得勤,这个省法很划算。下面这个最小 Server 暴露两个工具:读文件预览、列目录。

from fastmcp import FastMCP from pathlib import Path mcp = FastMCP("file-assistant") @mcp.tool() def read_file_preview(path: str, max_bytes: int = 1024) -> str: """读取文本文件开头,适合模型先了解文件内容再决定下一步。""" p = Path(path).expanduser().resolve() if not p.exists(): return f"文件不存在: {path}" if p.is_dir(): return "这是目录,请改用 list_directory 工具" with open(p, "r", encoding="utf-8", errors="replace") as f: return f.read(max_bytes) @mcp.tool() def list_directory(path: str = ".") -> str: """列出目录下的文件和子目录,适合模型了解项目结构。""" p = Path(path).expanduser().resolve() if not p.exists() or not p.is_dir(): return f"目录不存在: {path}" entries = sorted(p.iterdir(), key=lambda x: (x.is_file(), x.name.lower())) return "\n".join( f"[{'D' if e.is_dir() else 'F'}] {e.name}" for e in entries[:200] ) if __name__ == "__main__": mcp.run()

两个工具的返回值都设计成字符串,这是刻意的。MCP 工具的输出最终要拼进模型上下文,字符串最省 token;如果返回结构化 JSON,模型还得自己解析,反而容易出错。@mcp.tool()会把函数名、docstring 和参数类型加工成 tools/list 返回的 JSON Schema,所以 docstring 里的描述要写“什么场景用”,而不是“这个函数做什么”。

参数说明:max_bytes默认 1024 防止模型一次性读入超大文件导致上下文爆炸;list_directory限制 200 条避免把几千个文件的目录全塞进对话;Path.expanduser().resolve()把~和相对路径统一成绝对路径,避免模型传相对路径时工作目录不一致导致找不到文件。运行方式很简单:python server.py或uv run server.py,默认走 stdio 传输。

2.3 从 Client 侧看一次工具调用是怎么走完的

在 Cursor 或 Claude Desktop 里配置这个 Server,写法是统一的 mcpServers JSON:

{ "mcpServers": { "file-assistant": { "command": "uv", "args": ["run", "path/to/server.py"], "env": { "LOG_LEVEL": "WARNING" } } } }

mcpServers下的 key 是给模型看的服务名,command和args组成启动命令,env传环境变量。Client 按这个配置拉起子进程后,一次工具调用的完整链路是:启动子进程并做 initialize 握手,确认协议版本和能力;然后 tools/list 拉取工具清单;模型根据用户问题和工具描述选择工具、生成参数 JSON;Client 发起 tools/call 把参数发给 Server 执行;最后结果以文本形式回填进模型上下文。调试时可以用npx @modelcontextprotocol/inspector打开可视化界面,能看到工具描述、参数校验结果和返回内容,不用反复猜模型为什么没选某个工具。

提示:stdio 模式下 Server 进程的 stdout 是留给协议用的,print 会把协议流冲垮,日志一律走 stderr 或文件。这个问题在后面的避坑章节还会展开。

MCP 还有 streamable HTTP 传输方式,适合把工具部署成远端服务;stdio 适合本机脚本工具,零网络配置。刚上手建议先在本机用 stdio 跑通,再考虑通过 HTTP 暴露给远程 Client。

3. A2A 让智能体学会“托人办事”:Agent Card 与任务状态机是核心

3.1 A2A 和 MCP 不是二选一:一个管“手”,一个管“嘴”

MCP 的形态是 Client-Server,一个智能体直接调用工具;A2A 是 Agent-to-Agent,Google 在 2025 年对外公布并开源了参考实现,定位是智能体之间的任务协作。A2A 的关键假设是两个智能体不共享内存、不共享运行时,只能通过消息交流。它定义的 Agent Card、Task、Message,本质上把一次协作建模成“任务委托”:我告诉你我要什么,你异步干完告诉我结果。

维度MCPA2A
连接双方智能体 Client ↔ 工具 Server智能体 ↔ 智能体
通信单位工具调用(Tool Call)任务(Task)与消息(Message)
状态管理无,一次调用一次返回状态机:submitted / working / input-required / completed 等
发现机制客户端手动配服务地址Agent Card(/.well-known/agent-card.json)
典型场景读文件、查数据库、操作浏览器跨团队智能体协作、业务流程接力

一个 A2A Server 内部完全可以再依赖一堆 MCP 工具:外部智能体通过 A2A 把任务委托给我,我内部用 MCP 调文件、调浏览器、调数据库,干完再把结果通过 A2A 回传。两者是不同层的协议,不是替代关系。MCP 管“手怎么够到工具”,A2A 管“两个智能体怎么对话”。

3.2 Agent Card:先亮出你会什么,别人才好找你

A2A 的发现机制是 Agent Card,部署在目标 URL 的/.well-known/agent-card.json。对端智能体先 GET 这个文件,读 skills 决定要不要把任务发给你。一份最小可用的 Agent Card 长这样:

{ "name": "research-agent", "description": "擅长把内部 MCP 工具包成任务,供其他智能体调用", "url": "https://agent.example.com/", "version": "1.0.0", "capabilities": { "streaming": false, "pushNotifications": false }, "skills": [ { "id": "web_search", "name": "Web 搜索", "description": "调用内部搜索工具并汇总结果", "inputModes": ["text"], "outputModes": ["text"] } ] }

capabilities.streaming和pushNotifications别乱设:不支持流式就把 streaming 设 false,否则对端以为你能 SSE 推送,会等一个永远不会来的事件。skills 里的inputModes和outputModes声明的是消息格式支持,初期都写["text"]最稳。写 Agent Card 时只放对外暴露的能力白名单,不要直接把内部 OpenAPI 文档导进来,否则等于把内网接口广播给所有能访问这张卡的人,具体后果在避坑章节单独说。

3.3 用 FastAPI 写一个最小 A2A Server:先跑通 tasks/send

真实 A2A 协议基于 JSON-RPC 2.0,端点包含 tasks/send、tasks/get、tasks/cancel,支持流式时还有 message/stream。这里为了把核心逻辑讲清楚,用 FastAPI 做一个 REST 简化版,重点是任务状态机的流转:

from fastapi import FastAPI, HTTPException from pydantic import BaseModel, Field from uuid import uuid4 from typing import Optional app = FastAPI() class TaskSendRequest(BaseModel): taskId: Optional[str] = None message: dict = Field(default_factory=dict) metadata: dict = Field(default_factory=dict) class TaskStore: def __init__(self): self.tasks = {} def send(self, req: TaskSendRequest): # 幂等:客户端传入 taskId 时复用已有任务 tid = req.taskId or uuid4().hex if tid in self.tasks: return self.tasks[tid] # 这里替换成真实业务:调度内部 MCP 工具 text = req.message.get("text", "") self.tasks[tid] = { "id": tid, "status": "working", "messages": [f"已收到任务: {text[:120]}"], } return self.tasks[tid] store = TaskStore() @app.post("/tasks/send") def tasks_send(req: TaskSendRequest): return {"result": store.send(req)} @app.get("/tasks/get") def tasks_get(task_id: str): task = store.tasks.get(task_id) if not task: raise HTTPException(404, "task not found") return {"result": task}

状态机的关键点是:新任务进入submitted,开始处理后转working,如果缺信息要转input-required等对方补充,而不是直接从 submitted 跳到 completed。taskId由客户端传入,用于幂等和断线重连,Server 端必须支持“同一个 taskId 重复提交返回同一个任务”。验证方式用 curl 足够:

curl -X POST http://127.0.0.1:8000/tasks/send \ -H "Content-Type: application/json" \ -d '{"message":{"text":"帮我把MCP工具列表整理成JSON"}}'

返回结果里能看到 taskId 和初始状态 working;再 GET tasks/get 就能拉到最终结果。生产环境建议直接用官方 Python SDK 里的 A2A Server 实现,它把 JSON-RPC 包装、状态校验、流式传输都处理好了,自己手写容易漏边界。

4. ANP 想做智能体的“互联网”:没有中央调度器的网络怎么运转

4.1 ANP 的出发点:中心化编排撑不住开放网络

现在大多数多智能体系统是中心化编排:一个 orchestrator 调度多个 worker,好处是可控,坏处是单点、信任集中、跨组织协作难。ANP(Agent Network Protocol)走的是另一个方向:没有中央调度器,每个 Agent 自己声明能力,网络负责把请求路由到有对应能力的 Agent。它常被称作“智能体互联网”,把 DNS、路由、会话这类网络概念搬到智能体层。从公开的白皮书资料看,ANP 的核心是“元协议”——不只定义一种 API,而是定义 Agent 如何描述自己、如何注册、如何被找到。

以下三小节按 ANP 白皮书公开的思路做了一个教学最小样例,不是官方源码。目的是帮你理解这类去中心化协议的路由逻辑,而不是照着部署。

4.2 能力声明:一张会说话的名片

去中心化网络里没有中央注册处来告诉你“谁能干什么”,所以每个 Agent 必须先发布自己的能力声明。ANP 风格的能力描述长这样:

{ "@context": "https://agent-network-protocol.example/context.jsonld", "@type": "AgentDescription", "agentId": "anp:research-agent-01", "name": "research-agent", "endpoint": "https://agent.example.com/anp", "ttl": 3600, "capabilities": [ { "action": "search", "input": {"query": "string", "limit": "integer"}, "output": {"items": "array"} } ] }

agentId要全局唯一,相当于智能体的身份证;endpoint是别人调用你的入口地址,相当于 IP;capabilities描述你提供什么动作、接受什么参数、返回什么结构,相当于路由表。ttl是声明缓存时长,3600 秒意味着别的节点最多缓存这张名片一小时,过期后要重新获取。这里的@context和@type借鉴了 JSON-LD 的语义化思路,让机器能解析而不只是人眼看。

4.3 注册与发现的极简模拟:三个端点看懂路由逻辑

ANP 完整实现涉及跨域转发、心跳、多方共识,已经超出单篇能覆盖的范围。我用 FastAPI 模拟一个最小注册-发现-调用闭环,只保留路由的核心逻辑:

from fastapi import FastAPI from pydantic import BaseModel from typing import List, Optional app = FastAPI() registry = {} class Capability(BaseModel): action: str input: dict output: dict class AgentDesc(BaseModel): agentId: str name: str endpoint: str ttl: int = 3600 capabilities: List[Capability] @app.post("/register") def register(desc: AgentDesc): """Agent 启动时把能力声明注册进目录。""" registry[desc.agentId] = desc.model_dump() return {"ok": True, "agentId": desc.agentId} @app.get("/discover") def discover(action: str): """按能力查,返回能处理该动作的 Agent 列表。""" hits = [] for desc in registry.values(): for cap in desc["capabilities"]: if cap["action"] == action: hits.append({ "agentId": desc["agentId"], "endpoint": desc["endpoint"], "input": cap["input"], }) return {"results": hits[:20]} @app.post("/invoke/{agent_id}") def invoke(agent_id: str, payload: dict): """按查到的 endpoint 转发请求,模拟调用目标 Agent。""" desc = registry.get(agent_id) if not desc: return {"error": "agent not found"} # 真实场景这里应请求 desc["endpoint"],而不是本地直接执行 return {"agentId": agent_id, "received": payload, "status": "working"}

discover返回的是“谁能干这个活”,而不是“这个 IP 在跑什么服务”,这是去中心化路由和传统负载均衡的本质区别。ttl和心跳续租在生产里是必须的:Agent 挂掉以后,目录不能一直把流量导向它。注册表本身也可以多副本部署,节点之间同步状态,那就是另一个层面的分布式问题了。

4.4 现实情况:方向明确,生态没跟上

ANP 的架构思路值得跟踪,但做技术选型时要清醒:参考实现少,没有类似 MCP Inspector 那样的调试工具,也没有一致性测试套件,社区讨论热度远低于 MCP 和 A2A。适合在内部沙箱里验证去中心化路由的架构思路,或者做预研项目;直接上生产风险很高。过渡方案是混合架构:内部多智能体协作走 A2A,对外暴露能力时借鉴 ANP 的能力声明思路自己实现一个轻量注册表,等生态成熟再替换。

5. 智能体协议落地避坑:5 条血泪经验照单全收

5.1 print 调试击穿 MCP stdio 协议流

现象:MCP Server 跑起来了,但 Client 一直报“收到非法 JSON”或直接卡死,Inspector 里看不到任何工具列表。原因:stdio 模式下 stdout 是协议专用通道,Server 代码里的 print 调试信息混进了 JSON-RPC 流,把协议数据冲散了。解决:日志一律走 stderr 或文件。

# 错误:print 会混入 stdout 协议流 print("[DEBUG] 收到参数", path) # 正确:日志走 stderr,协议流不被污染 import logging logging.basicConfig(stream=sys.stderr, level=logging.INFO) logging.info("收到参数: %s", path)

不少嵌入端会把 stderr 转发到自己的日志面板,所以在 stderr 里打敏感信息(比如完整 API Key)也要克制,日志级别默认设 WARNING,排查时再临时调到 INFO。

5.2 长任务撞上 Client 30 秒超时,工具调用直接翻车

现象:工具本身没问题,但一跑超过 30 秒就被 Client 掐断,报类似“timed out after 30 seconds”的错误。原因:MCP 协议本身没有长任务语义,超时时间由 Client 端决定,很多 Client 默认请求超时就是 30 秒。解决:把长任务拆成“提交任务返回 task_id”和“查询任务结果”两个工具,模式上参考 A2A 的异步任务;或者调大客户端超时参数。我一般优先选择拆工具而不是调超时,因为调得过大可能让一个不可用工具挂死整个会话。

5.3 A2A 状态机不完整,另一端永远在等回话

现象:两个 Agent 明明都通了,但发起方一直在等,接收方却认为任务已经完成。原因:实现时只处理了 submitted 到 completed,漏了 input-required 分支;或者消息发出去了,但没有用 messageId 正确地回传会话上下文。解决:先把状态机画出来,每个分支都实现一遍,再直接用官方 SDK 提供的测试客户端跑一轮两方对话。A2A 的 Message 是带轮次的,不是简单的一句一问一答,漏掉 messageId 会导致对端无法把新消息关联到原任务上。

5.4 Agent Card 把内网接口亮成了公网能力

现象:Agent Card 上线后,外部智能体能调到内网接口,有些还是未鉴权的。原因:图省事直接把内部 OpenAPI 文档自动转换成 skills,导致内部端点路径、鉴权方式、参数结构全部暴露。解决:Agent Card 用白名单方式手写,只声明对外能力;在网关口做独立鉴权;上线前定期对比线上 Agent Card 和内部接口清单,防止有人后来往里加东西。

5.5 在 ANP 生态完备之前强行上生产

现象:拿着 ANP 参考实现直接部署到生产,路由时好时坏,出问题也没人接盘。原因:生态早期,参考实现主要用于对齐规范,不是用来承载流量的,缺少限流、监控、故障转移这些生产要素。解决:容器化部署加超时和熔断;短期内用混合架构,内部走 A2A,边界上按 ANP 思路自建注册表;同时派人跟协议版本更新节奏。值得跟踪,但不值得赌上生产稳定性。

6. 把三条协议放进同一个系统:适配层设计与最小验证顺序

现在很多团队的架构是:一个主 Agent 配一堆 MCP 工具,同时对外以 A2A 接收跨团队协作,再在边界上按 ANP 思路把能力注册到目录。三条链路可以共用同一个业务工具层,避免每个协议各自实现一遍业务逻辑。做法是把业务能力抽成独立的核心层,协议只当外壳:

class AgentCore: """业务能力注册中心,不关心外界用什么协议访问。""" def __init__(self): self._tools = {} def register(self, name, fn, schema): self._tools[name] = (fn, schema) def run_tool(self, name, **args): fn, _ = self._tools[name] return fn(**args) # MCP 方向:用 FastMCP 把 AgentCore 里的每个工具包成 @mcp.tool() # A2A 方向:在 tasks/send 里解析 message,调用 AgentCore.run_tool,结果按状态机回传 # ANP 方向:把 AgentCore 里每个工具的 name/schema 生成能力声明,POST 到注册表

只要工具层不直接依赖任何协议,三个协议就只是三套壳,替换、升级、加新协议都不需要动业务代码。最小验证顺序我建议固定成四条:先直接调业务函数确认逻辑正确,再包成 MCP 用 Inspector 调通工具调用,然后包成 A2A 用官方 SDK 跑一遍两方对话,最后注册到 ANP 目录里,用 discover 查到并调用一次。每一步都保持最小闭环,再叠加下一层,出问题能立刻定位是哪一层的事。

我现在的习惯是先画通讯拓扑再写代码,把“谁拿工具、谁发任务、谁当目录”画清楚,再按上面顺序验证。协议本身没有绝对好坏,只有在你当前拓扑里合不合适。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/24 7:21:54

无人机道路坑洞识别检测数据集:小目标检测训练与优化实战

1. 低空视角下的道路病害检测:这个数据集到底解决什么问题搞道路巡检和市政养护的朋友应该都有体会,坑洞这东西看着不起眼,但它是路面病害里最要命的一类。轻则颠簸伤车,重则引发爆胎、追尾,尤其在高速公路上&#xff…

作者头像 李华
网站建设 2026/10/19 7:17:44

如何验证并替代不存在的CLI工具impeccable

我注意到输入内容中仅提供了项目标题“impeccable”及相关热搜词、热词列表,但未提供任何实质性的项目正文、摘要描述或具体场景说明。根据你设定的严格规则,我的全部输出必须完全基于用户提供的输入参数,尤其是“项目标题”这一核心锚点&…

作者头像 李华
网站建设 2026/10/23 13:20:44

SDN流表增删改查实战:从OpenFlow原理到生产落地

1. 项目概述:这不是在背概念,而是在调试一张会呼吸的网络神经图 “SDN-流表分析与增删改查”这八个字,乍看像教科书里的章节标题,但在我带过的三届网络工程实训班里,它从来不是考前突击的重点,而是学生第一…

作者头像 李华
网站建设 2026/10/20 23:21:16

Claude Code快捷键速查:把终端变成AI开发操作系统

如果你只在网页聊天框里用 AI 写代码,那其实还停留在“AI 聊天”的阶段。真正把 Claude Code 用成“开发操作系统”,是你在终端里敲下claude这个命令之后才发生的事。它会把你日常开发里的对话、文件读写、命令执行、代码审查、Git 提交全部收进同一个终…

作者头像 李华
网站建设 2026/10/20 19:02:46

金属门窗表面缺陷检测数据集:VOC与YOLO双格式实战解析

1. 金属门窗表面缺陷检测数据集的核心价值拆解1.1 这个数据集到底解决什么问题金属门窗在生产线上跑一圈,表面磕碰、划痕、氧化色差这几类瑕疵几乎是绕不开的。人工目检的痛点很直接:一个班次盯下来,眼睛疲劳后漏检率飙升,而且不同…

作者头像 李华
网站建设 2026/10/18 21:06:17

数组三大硬核场景:动态边界、原地压缩与下标哈希

1. 这不是又一套“数组入门题”,而是帮你把数组真正焊进肌肉记忆的专项训练“专项练习数组(2)”这个标题看起来平平无奇,甚至有点像学生时代作业本上被老师用红笔圈出来的课后习题编号。但如果你已经做过“专项练习数组&#xff0…

作者头像 李华