你有没有遇到过这种情况公司里同时有客服AI、数据分析AI、工单处理AI每个单拎出来都能干活但想让它们互相配合比如客服AI把用户诉求转给工单AI去处理却只能靠你在中间写胶水代码。我在做多智能体项目时这个问题被放大得特别明显。直到我认真研究了Google开源的A2A协议才意识到agent之间协作这件事本不该这么痛苦。A2A协议Agent-to-Agent是Google于2025年4月发布的开放协议专门解决AI代理之间的发现、通信和协作问题。和其他把agent接入工具的协议不同A2A把每个agent都当成可对话的实体让它能向另一个agent发送任务、接收结果、追问细节。下面我从原理层面拆解A2A的工作方式适合准备做多agent协作但不想被厂商绑定的开发者也适合想搞懂agent间通信底层逻辑的技术决策者。1. 聊天机器人之间为什么还需要一套协议现在做单个功能Agent已经不难。难的是让多个Agent像团队一样一起工作。举个真实的例子我用AI做了个日程管理助手它能读取邮件、识别用户“周五下午开评审会”的意图然后创建日历事件。这没问题。但我想让另一个Agent去联系参会人、预订会议室问题就来了——两个Agent都是我自己开发的我可以让它们直接调用同一个数据库、共享同一个内存表等于我在它们之间拉了根私线。1.1 多Agent协作的真实困境私线一多就变成了蜘蛛网。假设有三个AgentA、B、C需要两两协作至少得写三条私线再加一个D连线变成六条五个Agent就是十条。每条私线的接口风格、参数语义、错误处理都不同维护成本指数级上升。更隐蔽的问题是语义不一致A发一个“query_inventory”B理解为“库存查询”C却理解成“库存的单价查询”。没有公共规范就只能靠人肉对齐。我当时还试过用企业内部的消息队列比如Kafka来做Agent通信。能解决一部分问题但消息队列本质上是“发布-订阅”模型Agent之间的对话更像“请求-响应”我需要向Agent X发起一个任务等待它的反馈甚至中途打断它。消息队列的异步拓扑天然不适合这种交互。所以A2A协议出来的时候我第一反应是终于有人把“Agent之间的协作语言”标准化了。1.2 A2A协议想解决的三件事A2A的官方定位说起来特别朴素让不同厂商、不同技术栈的Agent能在一起工作且不需要提前知道对方的内部实现。拆开看它主要解决三个问题。第一是发现。一个Agent怎么知道另一个Agent存在知道后怎么知道它“会做什么”A2A用一张标准化的Agent Card对外描述自己的身份和能力并通过HTTP端点暴露出来相当于每个Agent在网上挂了一张“电子名片”。第二是交互。Agent之间不是“调接口”的关系而是“派活协作”的关系。A2A定义了Task、Message、Artifact这些通用概念让Agent能发送任务、接收中间状态、追问细节、取消任务。这套交互模型比单纯的API调用更贴近人类的沟通习惯。第三是安全与集成。A2A支持OAuth 2.0等主流认证方式并且定义了在企业环境里如何控权、怎么做审计。这一点对商业化落地很重要——不然你连一个公司内部的Agent都不敢往外放。顺便补充一个时间线2025年4月Google发布A2A后6月就把它捐给了Linux基金会成立A2A工作组来继续演进。也就是说它现在不是Google自家的私有协议而是一个社区驱动的开放标准。对做技术选型的人来说这一点比协议本身还重要——你不会想为一家公司的私有方案投入两年架构。2. Agent CardA2A世界的“自我介绍”与角色模型要理解A2A我建议先看Agent Card因为它最直观。它相当于Agent在互联网上的公开资料页别的Agent拿到这张卡就能判断“我要不要跟它合作、怎么跟它合作”。2.1 一张标准化的Agent名片Agent Card本身是一个JSON文档放在Agent服务的根路径或者已知路径下比如/.well-known/agent.json。它最少要包含这些信息{ name: CalendarAgent, description: 负责日程管理、会议安排的智能助手, url: https://agent.example.com/, version: 1.0.0, capabilities: { streaming: true, pushNotifications: false }, security: { schemes: [ { type: oauth2, description: 使用OAuth 2.0授权码模式 } ] }, skills: [ { id: create_event, name: 创建日程, description: 根据用户自然语言创建日历事件 }, { id: query_meeting_room, name: 查询会议室, description: 检查指定时间点哪些会议室可用 } ] }注意capabilities字段不是“我能做什么”而是“我在通信方式上有什么能力”。比如开启了streaming说明这个Agent支持流式响应客户端可以先收到零碎结果再拼装开启了pushNotifications说明它能主动往你注册的回调地址推任务状态。这两个开关直接决定了客户端用哪种交互方式。skills列表才是“我会做什么”。里面每一项有一个id和description。客户端拿到这张卡会先看description再看具体skill的description判断任务能不能交给它。这有点像你打开某家公司的官网先看主营介绍再看业务条线。我在自己的项目里测试过Agent Card对不确定性的贡献很大以前多个Agent协作时对方到底能力边界在哪全凭猜现在有了标准卡至少可以自动做“能力筛选”。当然它不能解决“Agent吹牛”的问题——卡上说会修图实际接过来可能只是滤镜这是另一个层面的事。2.2 既分Client/Server又彼此对等A2A协议里其实引入了client和server的说法主动发起协作的Agent是client接收任务的Agent是server。但这里要特别强调一点这不是传统意义上的“调用方-被调用方”的不平等关系。因为每个Agent都可以既当client又当server。比如一个复杂的跨国协作场景上海公司的金融分析Agent需要新加坡公司的数据Agent给一份财报摘要。前者发起任务是client后者接收任务是server。但如果数据Agent中途发现需要更进一步的信息比如需要分析Agent确认某个指标的口径它也可以反过来发起一个任务角色就互换了。所以A2A的模式在协议层面是对等的不像REST API那样严格区分“服务提供者”和“服务消费者”。这种对等性让A2A更像两个同事之间来回商量而不是主从指令。我见过有人把A2A理解成“Agent的远程过程调用”其实不准确——RPC关心的是“执行一个函数”A2A关心的是“完成一项任务”后者天然包含了上下文、中间反馈、多轮澄清这些过程。2.3 传输协议的选择JSON-RPC 2.0与HTTPSA2A没有发明新的传输协议而是直接复用HTTP和JSON-RPC 2.0。这一点我特别欣赏因为这意味着任何语言、任何框架都能接入只要你能处理HTTP请求和JSON。具体来说Agent必须暴露一个HTTP端点接受JSON-RPC 2.0格式的方法调用。核心方法大概有这些方法作用initialize建立连接前客户端用它确认对方的基本信息和协议版本message向Agent发送一条Message用于无状态或者对话式的交互tasks/send下发一个任务返回任务结果或任务IDtasks/get根据任务ID查询当前状态tasks/cancel取消一个正在执行或等待中任务tasks/resubscribe重新订阅某个任务的状态变更用JSON-RPC而不是自定义协议还有个附带好处生态成熟很多语言都有现成SDK不需要自己造轮子。传输层用HTTPS既保证了基本加密又为身份认证留下了扩展空间。安全性上A2A没有自己做一套而是复用OAuth 2.0这些企业里已经跑通的方案——老老实实站在巨人的肩膀上。3. 任务生命周期A2A协作的核心状态机如果你只记一点A2A的知识我建议记住Task任务这个概念。它是A2A里最核心的抽象理解了任务生命周期就理解了整个协议的运转方式。3.1 Task是协作的“经办单”你可以把Task想象成公司里的“经办单”一个任务进来有一个编号有负责人有当前状态。无论任务被分给谁这个编号全程不变双方靠它对齐进度。A2A定义了Task的几个状态状态含义submitted任务已提交对方已收到尚未开始处理working任务正在执行中input-required需要更多信息等待发起方补充completed任务成功完成failed任务执行失败canceled任务被发起方取消awaiting-execution任务已排队但还没轮到自己执行如果目标Agent有并发控制状态流转的直观路径是submitted - working - completed失败则走working - failed需要补充信息时走input-required这时候client可以再发一条带补充信息的请求让它回到working。这里有个设计上的细节input-required很重要但很多Agent框架都忽略它。我在自己项目里发现大部分Agent协作失败不是因为技术问题而是因为对方需要澄清用户意图但发起方直接当成死任务挂了。A2A把“追问”显式建模成状态等于是在协议层面承认了“Agent之间可以对话而不只是递交”。3.2 Message、Part与Artifact的关系Task之外A2A还有几个容易混淆的概念Message、Part和Artifact。Message是Agent之间往来的具体内容可以类比微信里的一条消息。每条Message由若干个Part组成Part可以是一段文本、一张图片、一份PDF文件甚至是一段结构化JSON。这个设计的好处很直接一条任务请求里既要写“请分析这份财报”又要附上财报PDF文本在Part1文件在Part2一起放进Message。Artifact则完全不同它是Task执行过程中产生的结果“物”。比如分析Agent完成财报解读产出一份“财报解读报告”这就是Artifact。区别在于Message是沟通过程中的对话内容Artifact是协作成果的产出物。所以Task最终会关联一个或多个Artifact而Message只是过程中的数据交换载体。我把这几个概念用一个例子串起来我是主Agent向某个报表Agent发了一个Task内容为“生成第一季度销售汇总”。这条请求是一个Message其中Part1是指令文本Part2是原始销售数据文件。报表Agent开始工作期间发回一条Message说“原始文件中包含缺失值是否用均值补全”这是Message不是Artifact。我回复“可以并标注缺项”它继续运行最终返回一个Excel文件这个Excel才是Artifact。3.3 同步、轮询、推送与SSE四种交互姿势A2A在设计上很聪明的一点是它不强迫所有Agent用同一种交互方式而是提供了四种模式通信双方根据能力协商选择。先说同步。所谓同步是指调用方发出tasks/send后对方在同一个HTTP响应里直接返回最终结果。这种方式适合短任务比如“把英文翻译成中文”几秒内能出结果没必要搞异步。但现实里很多Agent任务耗时长比如“爬取100个网页并生成摘要”。这时同步等待会让客户端长时间挂起于是一般用异步tasks/send先返回任务ID和当前状态比如submitted客户端隔一段时间再调用tasks/get查询进度。这就是轮询。轮询虽然实现简单但效率不高频繁查空转还浪费HTTP请求。所以A2A支持推送如果server的Agent Card里声明了pushNotifications: trueclient可以提供一个回调URLserver在任务状态变化时主动POST一个状态更新过去。屋里有人说这像webhook确实就是同一个思路。第四种是流式输出对应的是streaming能力。服务端通过SSEServer-Sent Events向客户端持续推送中间结果。比如一个AI创作Agent开始写文章你可以看到它一个字一个字往外蹦而不是等整篇生成完才一次性返回。对用户体感来说流式输出明显比“干等5秒然后一次性弹出一大篇”友好得多。我在实际项目里的建议是短任务直接同步长任务优先让server开SSE流式不具备条件就退化为轮询。推送通知适合内部系统间协作因为需要双方都有公网回调能力本地开发环境下配置起来会比较麻烦。4. A2A与MCP的分工一个管队友一个管工具聊A2A的人几乎一定会聊到MCP。因为这两个协议听起来都跟Agent生态有关而且名字都带“C/P”容易混。我在社区的问答里也见过不少“是不是有了MCP就不需要A2A了”的问题。我的回答是它们解决的不是同一个问题非但不冲突反而应该配合使用。4.1 MCP解决的是Agent到工具的最后一公里MCPModel Context Protocol最初由Anthropic提出后来Google、OpenAI等也陆续加入生态。它要解决的问题是怎么让LLM统一地调用外部工具和数据源。没有MCP之前你要让AI的行为依赖一个数据库查询得专门给它写一个工具函数让模型自己决定何时调用。每个工具一套写法每接一个新数据源就要写新代码。MCP把这些“AI与外部世界交互”的动作标准化了模型通过MCP client去连接一个个MCP server每个server封装了一组工具或数据资源。可以粗暴地理解MCP解决的是“Agent如何拿到信息和执行动作”它让Agent有了用工具的双手。A2A解决的是“Agent如何和别的Agent合作”它让Agent有了能对话的队友。一个是纵向的“Agent-工具”通道一个是横向的“Agent-Agent”通道。4.2 A2A解决的是Agent到Agent的横向协作A2A的设计场景是两个独立Agent之间的协作。它们可能运行在不同公司、不同云厂商、不同技术栈上不可能共享内部数据模型。A2A为这种场景定义了一套大家都认的“商务沟通规则”你投递任务我反馈进度产出Artifact中间还可以追问。MCP继承不了这种职责。你可以想象如果A2A只提供工具调用那就意味着每个Agent都要知道“另一个Agent有哪些内部方法”。这正好违反了解耦原则而且一旦另一个Agent内部改了方法名所有协作方一起挂。有了A2A的任务级抽象协作双方只需要知道“对方能完成什么任务”以及“输入输出格式是什么”不需要理解对方内部架构。所以一个Agent内部可以用MCP去访问数据库、文档、API对外则用A2A跟另一个Agent互通。两层协议管的事情不同不存在谁取代谁。4.3 实际项目中的组合架构我在自己搭的一个多Agent系统里就是这样组合的。以“用户让客服Agent预约会议室”为例完整链路是用户对客服Agent说“帮我预约周五下午的会议室。”客服Agent先用MCP调用会议室查询工具拿到可用会议室列表。客服Agent发现涉及跨团队的日程同步于是用A2A向日历Agent发一个Task内容为“创建会议室预约事件参会人包括市场部四人”。日历Agent内部通过MCP调用企业的日历API创建事件。日历Agent通过A2A返回任务状态和最终的日历event作为Artifact。客服Agent拿到结果后用MCP调用邮件工具通知参会人。整个过程里A2A管的是客服Agent和日历Agent之间的横向协作MCP管的是各自内部的纵向工具调用。两层各司其职逻辑很清晰。对这种组合架构我的体会是不要试图让一个Agent直接调用另一个Agent的MCP工具否则耦合会非常深一旦对方内部工具升级你的调用就废了。通过A2A的任务边界把耦合切断在“任务”这一层是最稳的。5. 动手把一个Agent暴露成A2A服务流程与踩坑原理讲完说点实操。如果你想把自己手头的Agent接入A2A生态最直接的做法是写一个A2AServer适配层。下面是我验证过的最小路径和几个值得注意的坑。5.1 最小实现需要准备哪些东西实现一个能被其他Agent调用的A2A服务从最小规模看你只需要做三件事一份Agent Card、一个接收JSON-RPC请求的HTTP端点、一套任务状态管理逻辑。Agent Card可以直接放在Agent服务的根目录下路径固定为/.well-known/agent.json。HTTP端点负责路由各个JSON-RPC方法最核心的是tasks/send、tasks/get、message。任务状态管理则是在服务端维护一张任务表记录每个任务ID对应的状态、输入Message、输出Artifact。用官方社区提供的A2ASDK来搭的话代码骨架大致是这样不同版本API可能会有微调核心流程是一样的from a2a.sdk import A2AServer, A2AServerConfig from a2a.types import Task, Message, TaskState def handle_send_task(task): # 保存任务返回 task_id 和状态 ... server A2AServer( configA2AServerConfig( host0.0.0.0, port8080, agent_card_path./agent.json ), task_handlerhandle_send_task ) server.run()写这段代码不是为了让你照抄而是想说明接入A2A时大部分协议细节都被SDK封装掉了你需要关心的只是两件事——把Agent的能力如实写进Agent Card以及提供一个将Task映射到你实际业务逻辑的handler。如果你的Agent已经有了HTTP服务比如一个FastAPI应用也不一定要引入新SDK最原始的方式是自己实现那几个JSON-RPC方法本质上就是解析POST请求体里的method和params然后返回对应结果。SDK只是帮你把反序列化、协议细节、错误码统一处理了。5.2 最简单的验证方法先给Agent发一条任务接入完成后验证客户端是否能发现和调用可以用两条命令。先用curl看Agent Card是否正常返回curl https://your-agent.example.com/.well-known/agent.json如果能看到完整的JSON文档说明发现机制没问题。然后用A2A client模拟另一个Agent发送任务curl -X POST https://your-agent.example.com/ \ -H Content-Type: application/json \ -d { jsonrpc: 2.0, id: 1, method: tasks/send, params: { task: { message: [ { role: user, parts: [ {text: 帮我生成一份季度销售简报} ] } ] } } }如果配置得当你会收到一个包含taskId的响应后续可以轮询tasks/get来查看状态直到出现completed并且携带Artifact。这一步跑通意味着一个最小A2A协作闭环已经建立。我强烈建议在做多Agent编排之前先单独把每个Agent用这种方式验证一遍。因为多Agent链路一旦出现问题排查成本是叠加的你先要知道A到底有没有把任务发给BB有没有正确处理C有没有收到结果。单点验证能先消灭一半的变量。5.3 我在集成A2A时踩过的几个坑坑一Agent Card里的capabilities声明和实际行为不一致。最常见的是我声明了streaming: true但后端的SSE实现只发了最终结果没发中间事件。客户端在复杂场景下会认为连接异常直接报错。反过来如果声明支持pushNotifications却没有实现回调推送对方等待推送更新就会一直卡着。所以Agent Card上的能力声明一定要和实际行为对齐宁可保守不要夸大。坑二任务状态机的分支处理不完整。很多初版实现只处理了working - completed这条happy path遇到需要澄清的场景就直接返回failed了。这其实是把责任推给了发起方。对方收到failed根本不知道是哪里失败也没法继续沟通。要真正发挥A2A的价值至少在业务可能出现歧义的地方实现input-required状态并返回一个“需要补充哪些信息”的说明Message。坑三异步长任务的超时和幂等。如果你用轮询方式client侧一定要实现合理的退避策略不要每秒猛打tasks/get。server侧要为同一任务的重复请求做幂等处理尤其当task ID由client生成时避免同一个任务被重复执行两次。我遇到过隔壁团队任务并发控制没做好同一份报告被Agent自动生成三遍时间和费用都浪费了。坑四本地开发环境的webhook回环。pushNotifications依赖server主动回调client本地开发时如果两端都在自己的笔记本电脑上回调URL会变成类似http://localhost:9000/callback的地址对方根本访问不到。遇到这种场景要么用内网穿透工具把本地回调地址暴露出去要么直接退回轮询模式。对于只想在本地验证协议逻辑的团队轮询是最省事的选择。整体来说A2A从协议设计上并不复杂复杂的是真实业务里的状态混乱和安全边界。但有了标准状态机和Agent Card至少不同团队不用再为“你们家Agent的创建任务接口传什么参数”开半天对齐会议了。最后分享一个我自己的体会A2A目前还在快速演进期官方工作组每个季度都在更新规范所以不要急着把所有Agent都重构成A2A。先挑出一两个跨团队的协作场景试点跑通全链路和异常处理积累经验后再铺开比一口气全量接入要稳得多。如果你正在做多Agent系统我建议你现在就把Agent Card的能力清单整理出来——这一步无论将来用不用A2A都会让你的系统边界清晰很多。