MCP 与 A2A 入门:从概念到 Python 实战
深入浅出理解 MCP 和 A2A 的核心思想、差异与协同方式,并通过两个简洁的 Python 示例快速上手。
为什么你会经常同时听到 MCP 和 A2A?
如果你最近在做 AI Agent 项目,大概率会遇到两个高频词:MCP(Model Context Protocol) 和 A2A(Agent-to-Agent)。
很多同学一开始会困惑:
- MCP 和 A2A 是不是同一个东西?
- 都是“让模型干活”,那它们的边界在哪里?
- 实际工程里,是二选一,还是组合使用?
先给一个直观结论:
- MCP 更像“给 Agent 接工具和上下文的标准插座”;
- A2A 更像“让多个 Agent 彼此协作的沟通协议”。
一句话记忆:MCP 解决“怎么用工具”,A2A 解决“怎么找同伴协作”。
MCP 是什么?
核心定位
MCP 的目标是把“外部能力”标准化地暴露给模型或 Agent,例如:
- 文件系统读取;
- 数据库查询;
- 调用内部 API;
- 执行受控脚本。
以前每接一个工具都要写一套定制 glue code;有了 MCP 以后,工具提供方按 MCP 协议实现一个服务端,Agent 侧按 MCP 客户端方式接入,就能复用统一接口。
你可以把它理解成
- USB-C 接口:形态统一,背后设备多样;
- “工具菜单”协议:先告诉你我有哪些工具、参数怎么填,再执行调用并返回结构化结果。
MCP 的典型交互流程
- Client 连接 MCP Server;
- 读取 server 能提供的 tools / resources;
- 按需调用某个 tool(带参数);
- 将返回结果喂给 LLM 继续推理。
这意味着:MCP 把“能力接入层”做成了标准件。
A2A 是什么?
核心定位
A2A(Agent-to-Agent)强调的是多个 Agent 之间如何发现、协商、委托与回传结果。
当任务复杂到一个 Agent 不够时(例如“先做检索,再做代码生成,再做审校”),A2A 会非常自然:
- 一个协调者 Agent(Orchestrator)负责拆任务;
- 多个专业 Agent(检索、编码、审校)分别完成子任务;
- 最终汇总结果。
你可以把它理解成
- 团队协作协议:不是直接调用工具,而是“调用另一个有专业能力的 Agent”;
- 跨角色协同机制:定义了任务消息、状态、结果回传的格式和流程。
A2A 的典型交互流程
- Agent A 发布任务或请求;
- Agent B 接收并执行;
- B 返回中间状态(可选)和最终结果;
- A 继续编排后续步骤。
这意味着:A2A 把“多 Agent 编排层”做成了标准件。
MCP 与 A2A 的关系:不是替代,而是分层
用一张“分层图”来记忆最清楚:
- 上层(协作层):A2A,解决 Agent 与 Agent 的协作;
- 下层(能力层):MCP,解决 Agent 与工具/数据源的连接。
在真实系统里非常常见的一种组合是:
- 协调 Agent 通过 A2A 把任务派给“数据分析 Agent”;
- 数据分析 Agent 内部再通过 MCP 调数据库、读文件、跑计算。
也就是说:A2A 管“谁来做”,MCP 管“拿什么做”。
Python 实战示例一:一个最小可运行的 MCP 思想示例
说明:下面代码为了教学简化了工程细节,重点是帮助你理解“工具注册 + 工具调用 + 结构化返回”的模式。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# mcp_like_demo.py
from typing import Callable, Dict, Any
class SimpleMCPServer:
def __init__(self):
self.tools: Dict[str, Callable[..., Any]] = {}
def register_tool(self, name: str, fn: Callable[..., Any]):
self.tools[name] = fn
def list_tools(self):
return list(self.tools.keys())
def call_tool(self, name: str, **kwargs):
if name not in self.tools:
return {"ok": False, "error": f"unknown tool: {name}"}
try:
result = self.tools[name](**kwargs)
return {"ok": True, "data": result}
except Exception as e:
return {"ok": False, "error": str(e)}
# -------- 定义工具 --------
def add(a: float, b: float):
return a + b
def weather(city: str):
fake_db = {
"shanghai": "晴,20°C",
"beijing": "多云,16°C",
}
return fake_db.get(city.lower(), "暂无数据")
if __name__ == "__main__":
server = SimpleMCPServer()
server.register_tool("add", add)
server.register_tool("weather", weather)
print("tools:", server.list_tools())
print(server.call_tool("add", a=3, b=5))
print(server.call_tool("weather", city="Shanghai"))
运行:
1
python mcp_like_demo.py
你会看到类似输出:
1
2
3
tools: ['add', 'weather']
{'ok': True, 'data': 8}
{'ok': True, 'data': '晴,20°C'}
这个示例虽然不是完整 MCP 协议栈,但它抓住了 MCP 的核心价值:
- 工具以统一方式暴露;
- 调用参数结构化;
- 返回结果结构化,便于 LLM/Agent 继续消费。
Python 实战示例二:一个最小 A2A 思想示例
我们用两个“角色”模拟:
planner_agent:负责拆解任务;coder_agent:负责产出代码草稿。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# a2a_like_demo.py
from dataclasses import dataclass
from typing import Dict, Any
@dataclass
class A2AMessage:
sender: str
receiver: str
task: str
payload: Dict[str, Any]
class PlannerAgent:
name = "planner_agent"
def handle(self, user_goal: str) -> A2AMessage:
subtask = f"为目标生成 Python 示例代码:{user_goal}"
return A2AMessage(
sender=self.name,
receiver="coder_agent",
task="generate_code",
payload={"requirement": subtask},
)
class CoderAgent:
name = "coder_agent"
def handle(self, msg: A2AMessage) -> Dict[str, Any]:
if msg.task != "generate_code":
return {"ok": False, "error": "unsupported task"}
code = (
"def hello(name: str) -> str:\n"
" return f'hello, {name}'\n"
)
return {
"ok": True,
"from": self.name,
"result": {
"summary": "已生成最小示例函数",
"code": code,
"for_requirement": msg.payload["requirement"],
},
}
if __name__ == "__main__":
planner = PlannerAgent()
coder = CoderAgent()
# 用户目标
goal = "写一个问候函数"
# A2A 消息:planner -> coder
a2a_msg = planner.handle(goal)
response = coder.handle(a2a_msg)
print(response["result"]["summary"])
print(response["result"]["code"])
运行:
1
python a2a_like_demo.py
你会发现这个模型是“角色协作”而不是“直接工具调用”。这正是 A2A 的思路:
- 消息有发送者、接收者、任务类型;
- 接收方按职责处理并返回结果;
- 协调方可以继续串联下一跳 Agent。
一个更贴近真实项目的组合案例
假设你要做“自动生成周报”的 Agent 系统:
orchestrator_agent(总控)接收“生成周报”;- 通过 A2A 把“拉取数据”分配给
data_agent; data_agent再通过 MCP 调用:jira_query_tool(拿任务完成情况);git_stats_tool(拿代码提交统计);
writer_agent整理为自然语言周报;review_agent做格式和事实检查。
这个流程里:
- A2A 负责跨 Agent 协作;
- MCP 负责 Agent 内部工具接入。
分层清晰后,系统会更易于扩展、测试与治理。
落地建议:团队实践时优先做这 4 件事
1)先定义“协议对象”再写业务
不管 MCP 还是 A2A,都要先统一:
- 请求字段;
- 错误码;
- 结果结构。
否则后期联调成本会非常高。
2)把“可观测性”当一等公民
建议至少记录:
- 调用链路 ID;
- 每个 Agent/Tool 的耗时;
- 成功率与错误分布。
没有可观测性,多 Agent 系统会很快变黑盒。
3)从单 Agent + MCP 起步,再引入 A2A
很多团队一开始就上多 Agent,结果复杂度失控。更稳妥路径是:
- 第一步:先让单 Agent 通过 MCP 稳定调用核心工具;
- 第二步:当任务真的需要角色分工时,再接入 A2A。
4)给每个 Agent 明确“能力边界”
例如:
coder_agent只产出代码,不直接发版;review_agent只做审核,不改业务事实。
边界清晰,协作才可控。
总结
- MCP:标准化“Agent 如何使用工具与上下文”;
- A2A:标准化“Agent 之间如何协作”;
- 二者并不冲突,反而非常互补。
如果你刚开始实践,建议用这条路线:
- 先做一个最小 MCP 工具链(如搜索 + 数据查询);
- 再把任务拆给两个 Agent,体验 A2A 协作;
- 最后补齐日志、追踪、重试与权限控制。
当你把“工具层”和“协作层”分离开,整个 Agent 系统的可维护性会有质变提升。