Python A2A实战:为AI Agent创建专属邮箱与多智能体协作网络

Python A2A实战:为AI Agent创建专属邮箱与多智能体协作网络
1. 先搞清楚 A2A 和 AI Agent 邮箱到底要解决什么问题如果你最近在关注 AI Agent 开发大概率会看到“A2A时代”和“为 AI Agent 申请专属邮箱”这类说法。这背后其实是一个很实际的问题当多个 AI Agent 需要像人一样协作时它们之间如何可靠、标准化地传递信息和任务A2A即 Agent-to-Agent 协议就是 Google 提出的一套解决这个问题的标准。你可以把它想象成 AI Agent 之间的“普通话”或“HTTP 协议”。它定义了 Agent 如何自我介绍Agent Card、如何发送任务Task、如何返回结果Artifacts以及如何流式传输Streaming。而所谓的“为 AI Agent 申请专属邮箱”并不是真的去注册一个 Gmail 或 QQ 邮箱而是指为你的 Agent 创建一个标准化的、可被其他 Agent 发现和访问的网络端点Endpoint。这个端点就是 Agent 的“邮箱地址”其他 Agent 可以通过这个地址给它“发邮件”发送任务。所以这篇文章的核心不是教你怎么注册网页邮箱而是带你用 Python A2A 这个库快速搭建一个具备“收发邮件”能力的 AI Agent并让它能与其他 Agent 协作完成像“自动邮件采编与数据清洗”这样的实际工作流。如果你正在做多智能体系统、AI 工作流自动化或者想让你现有的 LangChain 应用能和其他人的 Agent 对话那这个主题就值得你花十分钟看完。2. 环境准备别急着写代码先把“地基”打好在动手之前我们需要明确运行条件。Python A2A 是一个 Python 库它的设计目标是生产就绪所以对环境的兼容性比较好但有些前置步骤能让你少踩坑。2.1 基础环境要求Python 版本建议使用 Python 3.9 及以上版本。这是目前大多数 AI 相关库的基线要求。操作系统Windows、macOS、Linux 均可。库本身是跨平台的但某些依赖如某些系统工具可能在 Windows 上需要额外配置。网络需要能正常访问 PyPIpip 安装源和 GitHub如果从源码安装。部分功能如 MCP 的 GitHub Provider需要访问对应服务的 API。2.2 包管理工具选择pip 还是 uv从官方文档看它强烈推荐使用uv。uv是一个用 Rust 写的、速度极快的 Python 包管理工具能很好地处理依赖冲突。如果你的项目依赖复杂或者你厌倦了pip偶尔的依赖地狱用uv是更好的选择。安装 uv (所有平台通用命令):# 使用官方安装脚本 curl -LsSf https://astral.sh/uv/install.sh | sh安装后重启你的终端然后就可以用uv命令了。2.3 安装 Python A2A根据你的需求选择不同的安装方式。我建议新手直接从all开始避免后续缺少组件。使用 pip 安装全功能版:pip install python-a2a[all]这个命令会安装核心库以及所有可选依赖包括 LangChain 集成、MCP 支持、各种 LLM 提供商OpenAI, Anthropic等的客户端。如果你的网络环境一般这个安装过程可能会稍长。使用 uv 安装推荐:uv install python-a2a[all]uv会更快地解析和安装依赖。按需安装如果你确定只需要部分功能可以精简安装# 仅核心功能通常不够因为很多例子依赖其他组件 pip install python-a2a # 核心 服务器功能用于运行Agent pip install python-a2a[server] # 核心 OpenAI 集成 pip install python-a2a[openai] # 核心 MCP (Model Context Protocol) 支持 pip install python-a2a[mcp]验证安装安装完成后在 Python 交互环境里快速验证一下import python_a2a print(python_a2a.__version__) # 应该能打印出版本号如 0.5.x如果没有报错说明基础库安装成功。3. 实战第一步创建你的第一个“邮箱 Agent”现在我们来创建一个最简单的 AI Agent并为它绑定一个“邮箱”即 HTTP 服务端点。这个 Agent 将拥有一个get_weather的 skill技能其他 Agent 可以通过网络地址向它询问天气。3.1 编写一个带 Skill 的 Agent创建一个文件比如叫weather_agent.py。# weather_agent.py from python_a2a import A2AServer, skill, agent, run_server, TaskStatus, TaskState # 使用 agent 装饰器定义Agent的元信息这就像是它的“名片” agent( nameWeather Agent, description提供指定城市的天气信息, version1.0.0 ) class WeatherAgent(A2AServer): # 使用 skill 装饰器定义一个具体的技能 skill( nameget_weather, description获取某个城市的当前天气, tags[weather, forecast] ) def get_weather(self, location: str) - str: 这里是技能的具体实现。实际应用中这里应该调用天气API。 # 模拟实现返回一个固定字符串 return f{location}的天气是晴朗25摄氏度。 # handle_task 是Agent处理 incoming “邮件”任务的核心方法 def handle_task(self, task): # 从任务消息中提取用户输入 message_data task.message or {} content message_data.get(content, {}) # 处理文本内容 text content.get(text, ) if isinstance(content, dict) else str(content) # 简单的意图识别如果文本包含“天气”和“在” if 天气 in text and 在 in text: try: # 提取城市名例如“北京天气怎么样” - “北京” # 这里逻辑很简单实际应用可能需要更复杂的NLP location text.split(在)[-1].split(。)[0].split()[0].strip() weather_result self.get_weather(location) # 构造返回给调用者的“邮件附件”Artifacts task.artifacts [{ parts: [{type: text, text: weather_result}] }] # 标记任务为完成 task.status TaskStatus(stateTaskState.COMPLETED) except Exception as e: # 如果处理出错返回失败状态 task.status TaskStatus( stateTaskState.FAILED, message{role: agent, content: {type: text, text: f处理请求时出错: {e}}} ) else: # 如果无法理解意图请求更多输入 task.status TaskStatus( stateTaskState.INPUT_REQUIRED, message{role: agent, content: {type: text, text: 请问您想查询哪个城市的天气例如北京的天气怎么样}} ) return task # 启动Agent服务器这就是在“申请邮箱” if __name__ __main__: agent_instance WeatherAgent() # 在本地5000端口启动服务。现在你的Agent就有了一个专属地址http://localhost:5000 run_server(agent_instance, host0.0.0.0, port5000)3.2 运行并测试你的 Agent在终端运行这个 Agentpython weather_agent.py你会看到类似Running on http://0.0.0.0:5000的输出。你的第一个 Agent “邮箱”已经开好了。测试“收信”功能。保持服务器运行打开另一个终端使用curl或任何 HTTP 客户端如 Postman发送一个任务curl -X POST http://localhost:5000/tasks \ -H Content-Type: application/json \ -d { message: { role: user, content: { type: text, text: 请问北京的天气怎么样 } } }如果一切正常你会收到一个 JSON 响应其中artifacts字段包含了“北京的天气是晴朗25摄氏度。”并且state是“COMPLETED”。关键点理解agent和skill这两个装饰器是 A2A 协议的核心。它们为你的代码添加了标准的元数据让其他 Agent 能自动发现这个 Agent 有什么能力Skills。handle_task方法这是 Agent 的“收件箱处理逻辑”。所有发到http://localhost:5000/tasks的请求都会交给这个方法处理。Task 状态TaskState.COMPLETED,INPUT_REQUIRED,FAILED是标准状态。这确保了调用者能明确知道任务进展。4. 构建多 Agent 网络与工作流实现自动邮件采编单个 Agent 能力有限。A2A 的强大之处在于让多个 Agent 协作。我们来模拟一个“自动邮件采编与数据清洗”的场景。假设我们有三个 Agent采集 Agent (Crawler Agent)负责从某个数据源比如模拟的新闻列表采集原始信息。清洗 Agent (Cleaner Agent)负责清洗采集到的文本比如去除空白、规范格式。摘要 Agent (Summarizer Agent)负责为清洗后的内容生成摘要。我们将创建一个Agent 网络 (AgentNetwork)和一个工作流 (Flow)来串联它们。4.1 创建三个协作 Agent首先我们创建三个简单的 Agent 文件每个运行在不同的端口。crawler_agent.py(端口 5001)from python_a2a import A2AServer, skill, agent, run_server, TaskStatus, TaskState import random agent(nameCrawler Agent, description从模拟数据源采集新闻标题和链接, version1.0) class CrawlerAgent(A2AServer): skill(namefetch_news, description获取最新的模拟新闻列表) def fetch_news(self, topic: str 科技): # 模拟采集过程 news_list [ {title: f{topic}领域重大突破新算法效率提升50%, url: http://example.com/1, raw_content: 这是一段有很多空格的原始内容。 }, {title: f专家解读{topic}行业未来趋势, url: http://example.com/2, raw_content: 另一段\n需要清洗\n的内容。}, ] return news_list def handle_task(self, task): text task.message.get(content, {}).get(text, ) if 采集 in text or fetch in text.lower(): topic 科技 # 简单起见固定主题 news self.fetch_news(topic) task.artifacts [{parts: [{type: text, text: str(news)}]}] task.status TaskStatus(stateTaskState.COMPLETED) else: task.status TaskStatus(stateTaskState.INPUT_REQUIRED, message{role:agent, content:{text:请告诉我采集什么主题的内容}}) return task if __name__ __main__: run_server(CrawlerAgent(), port5001)cleaner_agent.py(端口 5002)from python_a2a import A2AServer, skill, agent, run_server, TaskStatus, TaskState import json agent(nameCleaner Agent, description清洗文本数据去除多余空格和换行, version1.0) class CleanerAgent(A2AServer): skill(nameclean_text, description清洗一段文本) def clean_text(self, raw_text: str): # 简单的清洗去除首尾空格将多个空格/换行符替换为单个空格 cleaned .join(raw_text.split()) return cleaned def handle_task(self, task): # 期望接收到的内容是之前Agent的Artifacts try: # 这里简化处理实际应从task.artifacts解析 input_data task.message.get(content, {}).get(text, []) news_list json.loads(input_data) cleaned_news [] for item in news_list: item[cleaned_content] self.clean_text(item.get(raw_content, )) cleaned_news.append(item) task.artifacts [{parts: [{type: text, text: json.dumps(cleaned_news, ensure_asciiFalse)}]}] task.status TaskStatus(stateTaskState.COMPLETED) except Exception as e: task.status TaskStatus(stateTaskState.FAILED, message{role:agent, content:{text: f清洗失败: {e}}}) return task if __name__ __main__: run_server(CleanerAgent(), port5002)summarizer_agent.py(端口 5003)from python_a2a import A2AServer, skill, agent, run_server, TaskStatus, TaskState import json agent(nameSummarizer Agent, description为新闻内容生成简短摘要, version1.0) class SummarizerAgent(A2AServer): skill(namesummarize, description生成摘要) def summarize(self, title: str, content: str): # 模拟摘要生成真实场景可接入LLM summary f标题《{title}》的核心内容是{content[:30]}... if len(content) 30 else content return summary def handle_task(self, task): try: input_data task.message.get(content, {}).get(text, []) news_list json.loads(input_data) summarized_news [] for item in news_list: summary self.summarize(item[title], item[cleaned_content]) item[summary] summary summarized_news.append(item) task.artifacts [{parts: [{type: text, text: json.dumps(summarized_news, ensure_asciiFalse)}]}] task.status TaskStatus(stateTaskState.COMPLETED) except Exception as e: task.status TaskStatus(stateTaskState.FAILED, message{role:agent, content:{text: f摘要生成失败: {e}}}) return task if __name__ __main__: run_server(SummarizerAgent(), port5003)打开三个终端分别运行这三个 Agent# 终端1 python crawler_agent.py # 终端2 python cleaner_agent.py # 终端3 python summarizer_agent.py现在你有三个独立的 Agent 在运行地址分别是http://localhost:5001,5002,5003。4.2 使用 AgentNetwork 和 Flow 编排工作流创建一个orchestrator.py文件作为“总指挥”它不提供具体技能只负责协调。# orchestrator.py from python_a2a import AgentNetwork, Flow, A2AClient import asyncio import json async def main(): print(正在构建Agent网络...) # 1. 创建网络并添加已知的Agent“邮箱地址” network AgentNetwork(name新闻采编网络) network.add(crawler, http://localhost:5001) network.add(cleaner, http://localhost:5002) network.add(summarizer, http://localhost:5003) # 2. 创建一个工作流 (Flow) flow Flow(agent_networknetwork, name自动化新闻采编清洗摘要工作流) print(开始执行工作流...) # 3. 定义工作流步骤顺序执行 # 第一步让采集Agent工作 flow.ask(crawler, 请采集科技新闻) # 第二步将采集结果交给清洗Agent flow.ask(cleaner, 请清洗以下数据{latest_result}) # 第三步将清洗结果交给摘要Agent flow.ask(summarizer, 请为以下新闻生成摘要{latest_result}) # 4. 执行工作流初始上下文可以空着因为第一个Agent不依赖它 final_result await flow.run({}) # 5. 处理最终结果 if final_result and artifacts in final_result: # 通常最后一个Agent的结果会在 final_result 的 artifacts 里 result_text final_result[artifacts][0][parts][0][text] try: news_with_summary json.loads(result_text) print(\n 工作流执行完成 ) for item in news_with_summary: print(f标题: {item[title]}) print(f链接: {item[url]}) print(f摘要: {item[summary]}) print(- * 40) except json.JSONDecodeError: print(最终结果原始文本:, result_text) else: print(工作流执行未返回预期结果。) print(完整响应:, final_result) if __name__ __main__: asyncio.run(main())运行这个协调器python orchestrator.py如果一切顺利你将在控制台看到经过采集、清洗、摘要三个环节处理后的最终新闻列表。这就是一个最简单的多 Agent 自动化流水线。工作流引擎的核心价值{latest_result}这是 Flow 引擎的关键特性。它自动将上一个步骤的输出作为下一个步骤的输入。你不需要手动在代码里传递数据。错误处理在实际生产中你需要在flow.run()外围添加try...except并对每个ask步骤可能失败的情况设计重试或备选路径。并行与条件分支Flow 还支持.parallel()并行执行和.if_contains()条件分支可以构建非常复杂的业务流程图。5. 关键技能Skills开发与 MCP 工具集成仅仅让 Agent 内部处理逻辑还不够强大。A2A 通过Model Context Protocol让 Agent 能安全、标准化地使用外部工具如读写文件、查询数据库、调用 API。这就是python-a2a[mcp]安装选项提供的功能。5.1 为你的 Agent 添加一个“读取本地文件”的 Skill假设我们想让清洗 Agent 不仅能处理传来的数据还能主动读取一个本地配置文件来决定清洗规则。我们可以通过 MCP 来实现。首先确保安装了 MCP 支持pip install python-a2a[mcp]。然后创建一个file_tool_server.py作为 MCP 工具服务器# file_tool_server.py from python_a2a.mcp import FastMCP, text_response import json import os # 创建一个 FastMCP 服务器实例 mcp_server FastMCP(nameFile Tools Server, description提供安全的文件读取工具) # 定义一个 MCP 工具读取指定文件内容 mcp_server.tool( nameread_config, description读取一个JSON格式的配置文件, inputSchema{ type: object, properties: { filepath: {type: string, description: 配置文件的路径} }, required: [filepath] } ) def read_config(filepath: str): 读取并解析JSON配置文件。出于安全限制路径。 # 安全限制只允许读取当前目录下的 configs 文件夹 allowed_dir os.path.join(os.getcwd(), configs) requested_path os.path.abspath(filepath) if not requested_path.startswith(allowed_dir): return text_response(f错误无权访问 {filepath}。只能读取 {allowed_dir} 下的文件。) try: with open(requested_path, r, encodingutf-8) as f: config_data json.load(f) return text_response(json.dumps(config_data, ensure_asciiFalse, indent2)) except FileNotFoundError: return text_response(f错误文件 {filepath} 不存在。) except json.JSONDecodeError: return text_response(f错误文件 {filepath} 不是有效的JSON格式。) except Exception as e: return text_response(f读取文件时发生未知错误: {e}) # 再定义一个工具列出目录 mcp_server.tool( namelist_configs, description列出 configs 目录下所有可用的配置文件 ) def list_configs(): try: config_dir os.path.join(os.getcwd(), configs) files [f for f in os.listdir(config_dir) if f.endswith(.json)] return text_response(json.dumps(files, ensure_asciiFalse)) except FileNotFoundError: return text_response(configs 目录不存在。) except Exception as e: return text_response(f列出文件时出错: {e}) if __name__ __main__: # 在 6000 端口启动 MCP 服务器 print(启动 MCP 文件工具服务器在 http://localhost:6000) mcp_server.run(host0.0.0.0, port6000)运行这个工具服务器python file_tool_server.py。现在一个提供read_config和list_configs工具的服务就在http://localhost:6000运行了。5.2 改造清洗 Agent使其能使用 MCP 工具修改之前的cleaner_agent.py让它成为一个能使用外部 MCP 工具的“增强版”Agent。# enhanced_cleaner_agent.py from python_a2a import A2AServer, skill, agent, run_server, TaskStatus, TaskState from python_a2a.mcp import FastMCPAgent # 继承这个类以获得 MCP 能力 import json import asyncio agent(nameEnhanced Cleaner Agent, description清洗文本并能读取外部配置, version1.1) class EnhancedCleanerAgent(A2AServer, FastMCPAgent): # 注意多继承 def __init__(self): # 初始化 FastMCPAgent并告诉它 MCP 工具服务器的地址 mcp_servers { file_tools: {url: http://localhost:6000} # 连接到我们刚启动的 MCP 服务器 } FastMCPAgent.__init__(self, mcp_serversmcp_servers) # 可以在这里初始化一些默认清洗规则 self.default_rules {trim_spaces: True, remove_empty_lines: True} skill(nameclean_with_config, description根据外部配置文件清洗文本) async def clean_with_config(self, raw_data: str, config_name: str default.json): 这个技能会先读取配置再根据配置清洗数据 cleaned_results [] try: # 1. 调用 MCP 工具读取配置 config_result await self.call_mcp_tool( server_namefile_tools, tool_nameread_config, filepathfconfigs/{config_name} # 假设配置在 configs/ 下 ) # 解析 MCP 返回的文本响应 config_text config_result if isinstance(config_result, str) else config_result.get(content, {}) cleaning_rules json.loads(config_text) print(f已加载清洗规则: {cleaning_rules}) except Exception as e: print(f读取配置失败使用默认规则: {e}) cleaning_rules self.default_rules # 2. 应用规则清洗数据 (这里简化处理) data_list json.loads(raw_data) for item in data_list: raw_content item.get(raw_content, ) cleaned raw_content if cleaning_rules.get(trim_spaces): cleaned cleaned.strip() if cleaning_rules.get(remove_empty_lines): cleaned .join(cleaned.splitlines()) # 合并行 cleaned .join(cleaned.split()) # 合并空格 item[cleaned_content] cleaned item[applied_rules] cleaning_rules cleaned_results.append(item) return json.dumps(cleaned_results, ensure_asciiFalse) async def handle_task(self, task): # 注意因为使用了 MCP可能涉及网络IOhandle_task 也改为 async text task.message.get(content, {}).get(text, ) if 清洗 in text: try: # 提取可能的配置名例如“使用 config_v2.json 配置清洗” config_name default.json if 配置 in text: # 非常简单的提取逻辑实际应用需要更健壮的解析 parts text.split(配置) if len(parts) 1 and .json in parts[0]: config_name parts[0].split()[-1] # 简化处理 input_data task.message.get(content, {}).get(text, []) # 调用我们新的、支持配置的清洗技能 cleaned_data await self.clean_with_config(input_data, config_name) task.artifacts [{parts: [{type: text, text: cleaned_data}]}] task.status TaskStatus(stateTaskState.COMPLETED) except Exception as e: task.status TaskStatus(stateTaskState.FAILED, message{role:agent, content:{text: f清洗失败: {e}}}) else: task.status TaskStatus(stateTaskState.INPUT_REQUIRED, message{role:agent, content:{text: 请提供需要清洗的数据。}}) return task if __name__ __main__: # 注意因为类内部有 async 方法启动方式略有不同 import uvicorn from python_a2a.server import create_app agent_instance EnhancedCleanerAgent() app create_app(agent_instance) uvicorn.run(app, host0.0.0.0, port5002) # 还是跑在5002端口关键变化继承FastMCPAgent这赋予了 Agent 调用外部 MCP 工具的能力。__init__中配置 MCP 服务器通过一个字典指定工具服务器的地址和名称。异步方法因为调用外部工具是网络 I/O 操作所以技能方法clean_with_config和handle_task都改成了async。call_mcp_tool这是调用 MCP 工具的核心方法需要指定服务器名和工具名。测试这个增强版 Agent在项目根目录创建一个configs文件夹里面放一个default.json{ trim_spaces: true, remove_empty_lines: true, max_length: 500 }确保file_tool_server.py在运行端口6000。运行新的enhanced_cleaner_agent.py。使用orchestrator.py或者直接发送 POST 请求测试你会发现清洗 Agent 现在会先去读取configs/default.json文件再应用里面的规则进行清洗。通过 MCP你的 Agent 能力边界被极大地扩展了。官方提供了 GitHub、Browserbase浏览器自动化、Filesystem 等生产级的 Provider你可以轻松让 Agent 拥有操作 GitHub Issue、自动浏览网页、安全读写文件等“超能力”。6. 生产环境考量与常见问题排查当你把玩具示例跑通准备投入生产时下面这些点需要重点关注。6.1 性能、扩展性与部署并发与异步如上例所示一旦涉及 I/O网络、磁盘、数据库务必使用async/await编写 Agent 的技能和handle_task方法并使用uvicorn或hypercorn等 ASGI 服务器部署以支持高并发。Agent 发现与服务注册在生产中Agent 的地址“邮箱”可能是动态的。Python A2A 提供了AgentRegistry和DiscoveryClient来实现中心化的注册与发现机制而不是在代码里硬编码localhost:5001。错误处理与重试工作流Flow中的每个ask都可能失败。务必为关键步骤实现重试逻辑并设置合理的超时。安全性MCP 工具权限像上面的文件工具必须严格限制可访问的路径防止任意文件读取。API 认证暴露在公网的 Agent 端点必须实施认证如 API Key、JWT。输入验证在handle_task中严格验证和清理输入防止注入攻击。6.2 常见问题与排查顺序当你开发的 Agent 不工作时按这个顺序排查Agent 服务启动了吗检查端口是否被占用。netstat -an | grep 5000(Linux/macOS) 或Get-NetTCPConnection -LocalPort 5000(Windows PowerShell)。查看服务日志是否有导入错误或初始化失败。网络能通吗用curl http://localhost:5000/health或curl http://localhost:5000/检查 Agent 健康端点如果实现或根路径。如果是跨机器调用检查防火墙和安全组规则。请求格式对吗A2A 协议对 Task 的 JSON 结构有要求。确保你的POST /tasks请求体符合规范特别是message字段的结构。最稳妥的方式是使用python-a2a库内的A2AClient来发送请求而不是手动拼装curl。from python_a2a import A2AClient client A2AClient(http://localhost:5000) response client.ask(请问北京的天气怎么样) # 使用内置客户端Skill 定义和路由对吗确保skill装饰器正确定义了技能名和参数。在handle_task中你的意图识别逻辑是否能正确匹配到请求内容添加详细的日志打印text变量。MCP 工具调用失败首先确认 MCP 服务器是否独立运行且可访问。检查call_mcp_tool时传入的server_name和tool_name是否与 MCP 服务器定义的一致。查看 MCP 服务器的日志看它是否收到了请求以及错误信息是什么。工作流卡住Flow引擎依赖于{latest_result}这样的变量替换。检查上一个步骤的返回值是否是一个可以被正确解析和替换的格式。为每个flow.ask()步骤添加超时设置。考虑将复杂工作流在Agent Flow UI通过a2a ui命令启动中进行可视化编排和调试这比看日志直观得多。6.3 与现有系统集成LangChain如果你已经在使用 LangChain迁移到 A2A 或与之集成非常平滑。python-a2a提供了双向转换工具将 LangChain Tool 转换为 A2A Agent让你现有的 LangChain 工具能通过 HTTP 提供服务。将 A2A Agent 转换为 LangChain Tool让你能在 LangChain 的链条中直接调用远程的 A2A Agent。 这为你提供了极大的灵活性可以在不重写现有逻辑的前提下逐步将系统迁移到 A2A 架构。7. 总结从“邮箱”到“协作网络”的思维转变为 AI Agent 申请“专属邮箱”的本质是为其赋予一个标准化的、可寻址的网络身份。Python A2A 库极大地降低了实现这一目标的门槛。通过今天的实践你应该能清晰地看到一条路径定义技能用skill装饰器包装你的函数。创建 Agent用agent装饰器定义你的服务并在handle_task中处理请求。暴露服务用run_server将其变为一个 HTTP 端点。组建网络用AgentNetwork管理多个 Agent 的地址。编排工作流用Flow以声明式的方式描述 Agent 之间的协作顺序、并行和条件分支。扩展能力通过MCP安全地集成外部工具让 Agent 的能力突破代码边界。真正的价值不在于单个 Agent 多强大而在于多个专注的、可复用的 Agent 能通过标准协议A2A像乐高一样快速组合构建出适应复杂业务的智能工作流。下次当你再看到“A2A”、“Agent 邮箱”这些词时希望你能立刻联想到这个可落地、可扩展的技术栈并能亲手搭建属于你自己的智能体协作网络。