一、引言:什么是A2A协议?
Agent2Agent (A2A) 协议是一个开放标准,旨在实现人工智能代理之间的无缝通信和协作。在代理由不同供应商使用各种框架构建的当今世界,A2A 为代理互操作性提供了一种权威的通用语言。
为什么要将 A2A 与 MCP 结合?
在多智能体系统(MAS, Multi-Agent System)的演进过程中,我们经常面临两个维度的通信与解耦问题:
宏观维度(Agent-to-Agent):智能体与智能体之间如何进行业务协同?例如,“旅行规划智能体”如何发现并调用“天气查询智能体”?A2A 协议应运而生,它定义了 Agent 之间发现、调用以及流式回传事件的行业开放标准。
微观维度(Model-to-Context/Tool):智能体内部的大模型如何安全、动态地接入外部世界的数据与工具?MCP 协议(Model Context Protocol)正是为了解决这一痛点,它将“工具的实现”与“模型的推理决策”彻底解耦。
在本文中,我们将通过一个天气查询智能体的实战项目,展示如何将这两个协议强强联合:对外,它是一个符合 A2A 协议标准的远程微服务;对内,它作为一个 MCP 客户端,连接高德地图官方的 MCP 服务,并通过 DeepSeek-Chat 进行 ReAct(推理-行动)决策。
多Agent协作系统(MAS)=调度Agent+N个远程Agent(单体Agent+MCP+控制循环)
本文借助了Google的开源项目实现了一个简单的LLM调用“天气查询Agent”的A2A流程
A2A协议规范官网:https://a2a-protocol.org/latest/
二、系统架构与数据流向
在进入代码之前,我们先通过一张Mermaid架构图理清系统的数据流向:

还有一张图,可以看的比较清晰
三、远程端:构建符合 A2A 规范的微服务
在 A2A 协议中,一个合规的 Agent 节点需要向外宣告自己的“身份名片”和“技能清单”,以便被调度端发现。
在我们的项目入口 __main__.py 中,我们利用 A2A SDK 声明了天气 Agent 的卡片:
# 定义Agent支持的两个技能,涵盖天气预报+空气质量报告
forecast_skill = AgentSkill(
id='天气预告',
name='天气预告',
description='给出某地的天气预告',
tags=['天气', '预告'],
examples=['给我广州未来 7 天的天气预告'],
)
air_quality_skill = AgentSkill(
id='空气质量报告',
name='空气质量报告',
description='给出某地当前时间的空气质量报告,不做预告',
tags=['空气', '质量'],
examples=['给我广州当前的空气质量报告'],
)
# 定义Agent卡片,告知外部该Agent的作用与技术指标
agent_card = AgentCard(
name='天气 Agent',
description='这是一个天气Agent,提供天气相关的查询功能',
url=f'http://{host}:{port}',
version='1.0.0',
defaultInputModes=['text'],
defaultOutputModes=['text'],
capabilities=AgentCapabilities(streaming=True), # 声明支持流式返回
skills=[forecast_skill, air_quality_skill],
)解析:
AgentCard相当于 Agent 的“名片”。当 UI 控制台或调度 Agent 扫描该节点时,就能知道它能做什么(skills),以及通信地址(url)。我们使用
A2AStarletteApplication(agent_card=agent_card, http_handler=request_handler)将其托管为一个 Starlette Web 服务,供外部调度端发起 HTTP 调用。
四、核心决策层:DeepSeek + MCP 动态工具链的 ReAct 实现
当 A2A 服务端收到天气查询请求时,它会唤醒内部的决策大脑——ReActAgent。
这一步是整套系统的精髓所在:大模型本身并不知道如何查天气,但它知道如何通过 MCP 协议去“借用”高德地图的工具。
1. 动态连接 MCP 服务并嗅探工具
在 deepseek_react_agent.py 中,我们通过 MCP 客户端建立连接:
async def connect_to_streamable_http_server(self, url: str, headers: Optional[dict] = None):
"""连接到高德 HTTP MCP 服务器"""
# 1. 建立双向流通道
self._streams_context = streamablehttp_client(url=url, headers=headers)
read_stream, write_stream, _ = await self._streams_context.__aenter__()
# 2. 建立 MCP 客户端会话
self._session_context = ClientSession(read_stream, write_stream)
self.session: ClientSession = await self._session_context.__aenter__()
# 3. 初始化会话并动态拉取可用工具
await self.session.initialize()
response = await self.session.list_tools()2. 翻译工具定义并喂给 DeepSeek
大模型(这里使用的是 DeepSeek-Chat)需要知道工具的 JSON Schema 才能决定是否调用。我们将从高德 MCP 服务器获取的工具动态转换为大模型理解的格式:
response = await self.session.list_tools()
available_tools = [
{
"type": "function",
"function": {
"name": tool.name,
"description": tool.description,
"parameters": tool.inputSchema, # 动态注入工具所需的参数描述
}
}
for tool in response.tools
]3. ReAct 决策循环(Reasoning + Acting)
大模型接收到用户的天气问题后,会产生一个“调用高德 MCP 工具”的意图。我们在代码中实现这个经典的 ReAct 闭环:
# 1. 第一次调用大模型,附带高德 MCP 工具箱
response = self.openai.chat.completions.create(
model="deepseek-chat",
messages=self.messages,
tools=available_tools,
)
response_message = response.choices[0].message
tool_calls = response_message.tool_calls
# 2. 判断模型是否决定调用工具
if tool_calls:
self.messages.append(response_message.model_dump())
# 3. 执行工具调用
for tool_call in tool_calls:
tool_name = tool_call.function.name
tool_args = json.loads(tool_call.function.arguments)
# 4. 远程调用高德 MCP 服务器执行具体工具(如查询高德天气API)
result = await self.session.call_tool(tool_name, tool_args)
# 5. 将工具执行结果追加到对话历史中
self.messages.append({
"tool_call_id": tool_call.id,
"role": "tool",
"name": tool_name,
"content": json.dumps(result.model_dump()),
})
# 6. 第二次调用大模型,让它结合高德返回的实时数据,生成最终的自然语言答复
second_response = self.openai.chat.completions.create(
model="deepseek-chat",
messages=self.messages,
)
return second_response.choices[0].message.content五、传输桥梁:A2A 异步事件泵
当大模型跑完 ReAct 决策,生成了最终的自然语言天气报告后,WeatherAgentExecutor 将接管结果,并将其安全地推送回 A2A 的事件管道。
# agent_executor.py
class WeatherAgentExecutor(AgentExecutor):
async def execute(self, context: RequestContext, event_queue: EventQueue) -> None:
text = context.message.parts[0].root.text # 用户提问
agent = ReActAgent()
try:
# 连接高德 MCP 服务
await agent.connect_to_streamable_http_server(
url=f"https://mcp.amap.com/mcp?key={os.getenv('GAODE_API_KEY')}"
)
# 调用大模型执行 ReAct 获取响应
response = await agent.process_query(text)
# 将结果组装为 A2A 协议规定的事件格式,并推入异步事件队列
await event_queue.enqueue_event(
completed_task(
context.task_id,
context.context_id,
[new_artifact(parts=[Part(root=TextPart(text=response))], name="天气查询结果")],
[context.message],
)
)
finally:
await agent.cleanup()解析:
event_queue.enqueue_event实现了事件的异步推送。由于 A2A 协议支持流式传输,前端 UI(如 Mesop 控制台)能够订阅该队列,并在大模型完成处理的第一时间,实时渲染出天气卡片和回答。
六、运行效果

六、回顾总结为什么需要多Agent?
单 Agent 也可以挂多一个 MCP 服务实现天气工具的绑定,也可以实现这样的提问&回复,那么为什么还需要多 Agent 协作系统呢?的确有可能也是行得通的,但问题也随之而来,比如:
效果不好:某些 Agent 它的预设 Prompt 与底层逻辑设计目标是为了写好代码,不是做旅行助手,它可能会犯一些错;
成本增加:把所有工具都挂到同一个 Agent 上,成本会大大增加,例如一个编程助手,挂了上百个关于天气查询、酒店订阅、机票火车票等非高频使用的工具,虽然不会调用到,但是这些工具也会占据上下文,增加每次对话的花费;
LLM 绑定工具限制: LLM 能绑定的工具是有上限的,例如 GPT、 DeepSeek 等模型,最多能上传绑定 128 个工具(需考虑上下文长度),随着 MCP 协议的普及, 一个系统绑定数百乃至上千个工具非常正常,传统的单 Agent 无法完成该任务;
LLM 上下文限制:除了绑定的工具有上限, LLM 上下文也是有限的,例如 deepseek-r1 上下文长度是 64K,让一个 Agent 的目标&完成的任务越多,需要更长的预设 Prompt,目标可能无限多,但是 Prompt 长度是有限的。
LLM 认知局限:不同的 LLM 有各自擅长的内容,例如 DeepSeek 可以降低成本、 Claude 编写代码很强、 GPT 数学很厉害、 Gemini 长文编写很强,不用的 Agent 使用不同的 LLM ,一个专精于某个领域的 Agent,其表现肯定比什么都会一点的通用 Agent 更强,将这些专精 Agent 结合起来,整个系统效果会更强;
多Agent系统更灵活:相比将功能一股脑硬编码在一个 Agent 中,使用 A2A 协议动态加载远程Agent 更灵活,需要什么能力的 Agent,直接集成进来,不改变原系统架构与预设,对开发维护更友好;
通过这个天气 Agent 实战项目,我们验证了 A2A 与 MCP 结合的巨大威力:
极佳的解耦性:天气 Agent 的业务代码完全不需要知道高德 API 的具体调用细节、鉴权参数如何拼接。它只需要通过 MCP 动态加载工具,高德地图 MCP 服务做到了“即插即用”。
标准化的协同:前端控制台(Mesop UI)不需要为天气 Agent 单独写一套接口对接代码。因为天气 Agent 暴露的是标准的 A2A HTTP 接口,控制台使用统一的 A2A 客户端就可以完成对其生命周期的调度与渲染。
这种宏观 A2A 协作 + 微观 MCP 赋能的混合架构,为我们在企业中构建大规模、易扩展、低耦合的多智能体协同网络(MAS)指明了一条清晰的技术路线。
评论