首页/新闻资讯/正文详情

A2A协议全解析:AI Agent互操作标准、MCP关系与从0到1实践

发布时间:2026/9/26 8:49:35 来源:云帆数科 栏目:资讯中心
A2A协议全解析:AI Agent互操作标准、MCP关系与从0到1实践
在 Agent 开发圈子里混久了你会发现一个很魔幻的现实单个 Agent 的能力越来越强但把两个不同团队做的 Agent 放在一起它们根本没法好好说话。这不光是接口格式不统一的问题连“你帮我干件事”“干完了结果放这了”这种最基本的对话方式都没有一个通用标准。A2A 协议Agent-to-Agent就是为了填这个坑而出现的——它给 AI Agent 之间定义了一套互相发现、协商、派活、交活的公共语言。这篇文章我不打算照着官方文档念而是从“为什么需要它”“它到底设计了什么”“我怎么从 0 到 1 搭一个能跑的 A2A 协作流程”这几个角度把 A2A 掰开揉碎讲清楚顺便把实际踩过的坑也一并交代了。这套内容适合正在做 Agent 开发、想搞懂 A2A 和 MCP 到底什么关系、以及准备 Agent 方向技术面试的人。看的时候建议你手边备一个终端后面第三部分的代码不是给你读的是给你跑的。1. 为什么 AI Agent 之间需要协作A2A 协议的诞生背景1.1 AI Agent 的尴尬现状智能孤岛先说个我自己的观察。去年我在做一个偏内部效率的 Agent 项目核心功能是让一个“助理 Agent”帮忙查库存、写邮件、协调会议。一开始还挺顺利但随着需求变多我发现自己陷入了一个奇怪的局面每个功能模块都长成了独立的 Agent但因为没有一个共同的通信协议模块之间只能靠我自己写一堆“胶水接口”去转接。这种“胶水接口”有多痛苦两个 Agent 之间的调用你要处理 API 路径、鉴权方式、消息格式、错误重试……关键问题是这些都是项目私有的换个 Agent 全得重写。这就像每家酒店都用自己的插座标准你出门必须带一堆转接头。A2A 要解决的就是这个问题让不同厂商、不同技术栈、不同部署环境的 AI Agent 之间能通过一套统一协议完成发现、通信和协作而不是靠项目里写死的私有接口。它由 Google 在 2025 年 4 月联合多家厂商发起目标非常直白——把 Agent 之间的互操作做成像浏览器访问网页那样自然。1.2 A2A 与 MCP 到底什么关系这是网上被问爆的问题。很多人一看到“Agent 通信标准”就以为是 MCP 的升级版其实完全不是。MCPModel Context Protocol解决的是 Agent 与工具、数据源之间的连接你可以把它理解成“Agent 的手和眼睛”——让模型去调用函数、读数据库、操作外部系统。而 A2A 解决的是 Agent 与 Agent 之间的协作它是“Agent 的嘴和耳朵”。用一个组合场景来说明假设你有一个“行程规划 Agent”它需要查询航班信息。最简单的做法是给它接一个 MCP 航班查询工具自己直接去查。但更符合真实业务的做法是你手上已经有一个由另一个团队维护的“航班查询 Agent”它封装了所有航司接口和退改签逻辑那么这个行程规划 Agent 就可以通过 A2A 直接调用那个航班查询 Agent——让它去查查完把结果交回来。所以 MCP 和 A2A 不是竞争关系而是分工协作MCP 负责让 Agent“用工具”A2A 负责让 Agent“找伙伴”。在实际工程里两者经常组合出现——一个 Agent 通过 MCP 接入内部系统再通过 A2A 暴露自己的能力给其他 Agent 调用。1.3 Agent、LLM 与 AI 模型先把概念摆正聊 A2A 之前有一个基础概念必须理清因为网上经常混着用。LLM大语言模型是“大脑”负责理解语言、推理、生成文本比如 DeepSeek、GPT、Claude 都属于这类。AI 模型是个更大的范畴除了语言模型还包括视觉模型、语音模型、多模态模型等。而 Agent 是“完整的人”而不是“大脑”。一个 Agent 通常包含 LLM 作为推理内核但还额外具备记忆、工具调用、任务规划、环境交互能力。也就是说DeepSeek 是一个 LLM它可以被用来“驱动”一个 Agent但 DeepSeek 本身不是 Agent。这个区别在 A2A 语境下特别重要因为 A2A 设计的基本假设是每个 Agent 内部用的是什么模型不重要甚至可以不包含 LLM——比如一个负责固定计算的 Agent 完全可以由普通代码实现。A2A 只关心 Agent 对外暴露的行为契约你能干什么、怎么调用你、怎么把结果给我。这也是它能够跨厂商协作的根本原因。2. A2A 协议的核心设计Agent Card、任务与消息2.1 Agent Card智能体的“名片”A2A 协议里最基础的一个概念叫 Agent Card它是一个 JSON 文件存放于每个 Agent 的/.well-known/agent-card.json路径下。这个路径是有讲究的它遵循 RFC 8615 的 Well-Known URI 规范也就是说任何人只要知道你的域名就能通过固定路径拿到你的“能力说明书”。Agent Card 里包含这些核心字段name和description用来告诉别人你是谁url是 A2A 入口地址skills数组用来声明你具备哪些技能每项技能包括技能 ID、名称、描述和可选参数结构capabilities用来声明你支持流式输出还是推送通知authentication字段声明调用你需要什么鉴权方式。这里我想强调skills设计得好不好直接影响 Agent 被“发现”和“匹配”的精准度。我见过一些团队把 skills 描述写得特别含糊比如“能帮忙处理日常事务”这等于没写。真正可用的写法应该是“根据出发日期和目的地查询航班价格支持国内主要航司”。注意Agent Card 不是只在注册中心里存一份而是应该由每个 Agent 自己对外提供。这样你的 Agent 被谁发现取决于谁能访问到这个 JSON 文件而不是依赖某个中心化的目录服务。2.2 任务生命周期从创建到完成A2A 协议把一次协作抽象成一个“任务”Task而不是简单的“请请求响应”。这个设计非常关键因为真实场景里 Agent 干活常常不是瞬间完成的——它可能要去调接口、要等人确认、要后台跑几分钟分析甚至中途还要反过来问你问题。Task 有自己明确的状态流转。创建之后进入submitted随后变成working如果执行中需要更多输入会进入input-required状态此时客户端 Agent 需要补充消息再继续正常结束是completed失败是failed被调用方主动终止则是canceled。整个流程是异步的客户端可以轮询状态也可以通过订阅 Webhook 等待状态变更通知。我举个例子帮你建立直觉你让同事帮你写一份报告他不可能秒回他可能写着写着发现资料不够转头问你“去年的数据你有吗”。你补给他之后他继续写最后把报告给你。任务状态机就是在这个自然协作过程的形式化表达。理解了这一点你就明白 A2A 为什么不是简单发一个 HTTP 请求拿一个响应就完事——因为复杂任务天然是长时运行、多轮交互的。2.3 消息与 Artifact协作的“聊天记录”任务执行过程中参与双方需要交换信息。A2A 用Message对象来承载这些信息每个消息有角色和内容。角色只有两种user和agent分别代表发起方和接收方。消息内容由Part构成Part可以是文本、文件内容、或者一个结构化数据块。除了对话消息A2A 还定义了Artifact这个概念它表示 Agent 执行任务后产生的正式产物。举个例子一个“写代码 Agent”在任务过程中可能会给你发消息说“我开始写了”最后完成的代码文件就是一个 artifact。之所以要把 artifact 单独拿出来是因为它和普通对话消息有本质区别——它是任务的正式交付物需要被引用、被版本化管理、被后续流程消费。我在实际使用中的体会是这个设计非常贴近真实团队协作。Message 是过程沟通Artifact 是最终交付物两者分开管理你才能做到“多轮沟通不影响交付物追踪”。2.4 能力发现与协商A2A 为什么能“自动对接”传统系统对接是 A 说了算调用方写死 B 的地址、字段、鉴权方式一旦 B 变了A 必须跟着改。A2A 的做法完全不同它把“对接”变成了“发现”加“协商”。调用过程大致是这样的客户端获取目标 Agent 的 Agent Card解析里面的skills数组发现自己需要的技能然后读取capabilities看看对方支不支持流式输出支不支持推送通知最后按照authentication要求做鉴权发出一条携带具体任务的 JSON-RPC 请求。如果任务需要额外输入服务方会通过input-required状态主动索要。最关键的是这个发现和协商的过程不是一次性的而是每次调用都可以发生。也就是说A2A 在架构层面让“对接”变成了一种运行时行为而不是编译期/部署期的静态约定。这跟现在互联网领域的微服务注册发现很相似——Agent 世界也需要这样一个标准不然每个 Agent 都得手动背诵对方的“电话号码”。3. 从 0 到 1 搭建一个 A2A 工作流3.1 环境准备语言与依赖纸上谈兵聊完了进入实操环节。我用 Python 搭建一套最小可用的 A2A 工作流用 FastAPI 做 HTTP 服务用官方提供的a2a-sdk加速开发。之所以选 Python是因为 Agent 生态里 Python 的资料最多而且 FastAPI 的异步能力很适合处理 A2A 这种多轮任务场景生产环境你完全可以用 Java 或 Go协议本身没有语言绑定。安装依赖只需要两行命令pip install fastapi uvicorn a2a-sdk我建议你在一个干净的虚拟环境里操作避免把系统 Python 环境弄乱。这一步做完我们开始写第一个 A2A Server。3.2 实现一个基础 A2A Server先写 Agent Card。在项目根目录建一个static/agent-card.json文件内容是最小可用的“翻译技能”描述{ name: translation-agent, description: 一个提供中英互译能力的 Agent, url: http://localhost:8000/, protocolVersion: 0.2.0, skills: [ { id: translate, name: 文本翻译, description: 根据用户提供的文本执行中英互译, inputModes: [text/plain], outputModes: [text/plain] } ], capabilities: { streaming: false, pushNotifications: false }, authentication: { schemes: [], credentials: none } }接着创建入口文件server.py注册 A2A 路由from fastapi import FastAPI from a2a_sdk import create_a2a_server, AgentCard, Skill, NoAuth app FastAPI() def translate_text(text: str, target_lang: str) - str: # 这里替换成真实的翻译模型调用比如某个 LLM 接口 if target_lang zh: return f[中文翻译] {text} return f[English Translation] {text} def handle_skill(skill_id: str, params: dict) - str: if skill_id translate: text params.get(text, ) target params.get(target_lang, zh) return translate_text(text, target) raise ValueError(f未知技能: {skill_id}) agent_card AgentCard( nametranslation-agent, description一个提供中英互译能力的 Agent, urlhttp://localhost:8000/, protocol_version0.2.0, skills[ Skill( idtranslate, name文本翻译, description根据用户提供的文本执行中英互译, ) ], authenticationNoAuth(), ) a2a_server create_a2a_server( agent_cardagent_card, skill_handlerhandle_skill, ) app.mount(/, a2a_server) if __name__ __main__: import uvicorn uvicorn.run(app, host0.0.0.0, port8000)这段代码实现了两件事一是通过/根路径直接返回 Agent Card二是把/rpc路径挂载成 JSON-RPC 入口负责处理任务创建、状态查询、消息发送等标准请求。skill_handler是真正的业务逻辑所在A2A 协议会把对方传来的任务参数解析后路由到这里。3.3 实现客户端 Agent 并验证协作服务端就绪后我们写一个客户端 Agent去“发现”翻译 Agent并给它派发翻译任务。这里我用a2a-sdk里的客户端工具类来简化调用import asyncio from a2a_sdk import A2AClient, TaskInput, Message, TextPart async def main(): client A2AClient(http://localhost:8000/) # 1. 获取 Agent Card card await client.get_agent_card() print(发现 Agent:, card.name) print(支持技能:, [s.id for s in card.skills]) # 2. 创建翻译任务 input_message Message( roleuser, parts[TextPart(textHello, world)] ) task_input TaskInput( skill_idtranslate, parameters{text: Hello, world, target_lang: zh}, messageinput_message ) task await client.create_task(task_input) print(任务状态:, task.status) # 3. 轮询直到完成 while task.status not in [completed, failed, canceled]: await asyncio.sleep(1) task await client.get_task(task.id) print(任务结果:, task.artifacts[0].text) asyncio.run(main())跑起来之后你的客户端 Agent 会经历完整的“发现-建任-轮询-取结果”链路。这一步验证通过说明你已经具备了最基本的 A2A 互操作能力。在真实项目中客户端 Agent 内部通常也是由一个 LLM 驱动它通过读 Agent Card 来决定要不要调用你而不是像示例里这样写死技能 ID——但对初学者来说先跑通固定链路比追求动态决策重要得多。3.4 多 Agent 场景接力与协作单点通了之后我们来设计一个稍微复杂一点的协作一个“翻译 Agent”加一个“摘要 Agent”再由一个“编排 Agent”把它们串起来。场景是用户输入一段英文长文编排 Agent 先把英文翻成中文再把中文做摘要。这个场景里编排 Agent 不需要知道翻译和摘要的具体实现它只需要知道两个 Agent 的地址和 skills。它的工作流程是先调用翻译 Agent 拿到中文文本再把中文文本作为参数调用摘要 Agent最后把摘要结果返回给用户。这种“编排模式”是 A2A 最常见的用法。每个 Agent 保持单一职责复杂度全部集中在编排层。好处是显而易见的任何一个 Agent 的内部实现变了只要它的 Agent Card 不变整个链路就无需改动如果你想替换翻译服务只需要换一个同样声明translate技能的 Agent 地址。实操心得多 Agent 场景下务必注意消息大小和超时设置。A2A 协议对单个 Artifact 的大小没有硬性限制但实际部署时一定要在网关层设置合理的体积上限和超时时间否则一个 Agent 传大文件会把整条链路拖死。4. 常见问题与排查技巧实录4.1 Agent Card 404 或无法发现这个问题的现象是客户端访问/.well-known/agent-card.json返回 404或者拿到的是 HTML 错误页。排查顺序我建议如下。第一步检查路径大小写well-known全是小写agent-card.json也是小写第二步检查静态文件挂载配置FastAPI 里要用app.mount(/.well-known, StaticFiles(directorystatic))这类方式显式暴露第三步检查是否有反向代理拦截了这个路径。很多网关默认会屏蔽以点开头的路径或者.well-known下的请求这个坑我踩过不止一次。另外提一个很容易被忽略的点Agent Card 的 Content-Type 必须是application/json如果你用静态文件服务器托管记得确认 MIME 类型配置正确否则客户端解析会失败。这类“看起来是 404其实是配置不对”的问题最有效的排查方法是先用curl -i看响应头。4.2 任务一直卡在 working 状态任务状态长时间停留在working不前进是 A2A 调试里最高频的问题。原因通常有三类。第一类是最常见的服务端的返回结构不合法。A2A 规定任务结束必须返回completed状态并附带 artifacts或者返回failed状态及错误信息如果你在业务处理里抛了异常但没有被 SDK 转换成合法的任务终止状态客户端就会一直轮询。第二类是网络问题服务端实际已经处理完但回调通知没有送达客户端只能干等。第三类是死锁服务端在处理任务时又发起了对客户端的反向调用两边互相等待。排查的时候我会先看服务端日志里任务处理是否结束再看客户端轮询请求是否正确携带了任务 ID最后用curl手动调一下服务端的tasks/get接口确认状态。如果服务端已经completed但客户端还在轮询旧状态绝大多数是回调或缓存的问题。4.3 消息格式与版本兼容A2A 目前还在快速演进中protocolVersion 字段一定要从 Agent Card 里读清楚。生产环境里我见过不少案例两个 Agent 用的是同一套协议但版本号不一致导致对 artifact 结构和状态枚举的理解不同——比如早起版本用completed-artifact新版改成了artifact数组结构。解决这个问题没有银弹最可靠的做法是两边都使用官方 SDK并且把 SDK 版本锁在一个已知兼容的组合上。如果你在做一个生产级的 A2A Server强烈建议再加一层 schema 校验对请求体做严格校验后再进入业务逻辑。毕竟 A2A 的调用方可能是外部团队你无法假设它会发什么格式过来。4.4 安全与鉴权注意事项A2A 协议默认没有内置鉴权authentication字段只是声明了“需要什么鉴权方式”具体怎么校验完全由服务端自己实现。所以不要在公网裸奔暴露 A2A 端点这是我在任何场合都要强调的第一条安全底线。实操层面有几个建议。第一正常的生产环境必须上 TLS保证消息在传输过程中不被篡改第二用 OAuth 2.0 Bearer Token 或 API Key 做调用方身份识别并确保 Agent Card 里声明的鉴权方式和实际校验逻辑一致第三实现最小权限原则——并不是所有调用方都能触发所有技能有些敏感技能需要额外授权第四对任务输入和产物做内容过滤防止恶意 Agent 通过任务参数注入异常指令。重要任何 Agent 都不应该盲目信任另一个 Agent 的输出。A2A 消息本质上是数据你需要像处理外部 API 响应一样对它做校验和清理尤其在 Agent 输出会被直接作为代码执行或系统命令的情况下。最后再说两句A2A 协议从发布到现在已经有不少团队从“观望”转向“落地”了。在我个人的实践里它最大的价值不是让你少写几行 JSON 解析代码而是让 Agent 系统的边界变得清晰各团队独立开发部署自己的 Agent通过 A2A 暴露标准接口上层编排者可以像拼乐高一样组合它们。这种松耦合架构才是大规模 Agent 协作真正需要的东西。如果你刚开始接触 A2A别急着上复杂编排和动态协商先把第三部分的示例代码跑通把“一个 Agent 调另一个 Agent 拿结果”的链路走完整。然后再去思考 Agent Card 怎么写更清晰、任务状态怎么管理更健壮、安全边界怎么划分更合理。等你把这些基础打牢再回头设计多 Agent 协同就会有一种“原来如此”的通透感。

相关推荐

信用卡违约预测实战:模型融合与可解释性落地
信用卡违约预测实战:模型融合与可解释性落地

简介:本资源是一份面向数据科学初学者与金融风控从业者的信用卡违约预测实战项目,聚焦机器学习建模与模型融合策略在信贷风险评估中的落地应用。压缩包仅含1个核心Python脚本(predict.py),大小4KB,完整覆盖… · 2026/9/26 8:49:29

DeepSeek-R1 下载与部署完整指南:三步在本地跑通模型
DeepSeek-R1 下载与部署完整指南:三步在本地跑通模型

DeepSeek-R1 下载与部署完整指南:三步在本地跑通模型 【免费下载链接】DeepSeek-R1 探索新一代推理模型,DeepSeek-R1系列以大规模强化学习为基础,实现自主推理,表现卓越,推理行为强大且独特。开源共享,助力… · 2026/9/26 8:49:29

Landsat8影像批量预处理:从辐射定标到特征工程的全流程解析
Landsat8影像批量预处理:从辐射定标到特征工程的全流程解析

简介:这套Landsat8影像批量预处理方案面向人工智能与机器学习从业者,重点解决遥感数据清洗、云遮挡去除、辐射与大气校正、波段合成、光谱指数计算等特征工程问题。压缩包约46.93MB共含16个文件,核心为2个Python批处理脚本,配合Ge… · 2026/9/26 8:49:29

Visual C++ 2010学习版真实价值与安全使用指南
Visual C++ 2010学习版真实价值与安全使用指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 9:28:48

SolidWorks工程图字体修改三步法:系统字体→软件映射→模板固化
SolidWorks工程图字体修改三步法:系统字体→软件映射→模板固化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 9:28:48

Codex CLI 国内环境安装配置与 CC-Switch 多环境切换实战
Codex CLI 国内环境安装配置与 CC-Switch 多环境切换实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 9:28:48

Navicat官网历史版本下载指南:合规获取与版本管理实践
Navicat官网历史版本下载指南:合规获取与版本管理实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 9:28:48

洗碗机BLDC水泵EMC整改实战:共模电流路径与滤波设计
洗碗机BLDC水泵EMC整改实战:共模电流路径与滤波设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 9:28:48

从单片机到嵌入式Linux:学习路线、交叉编译与避坑指南
从单片机到嵌入式Linux:学习路线、交叉编译与避坑指南

1. 从单片机到Linux,到底跨过了哪条河干了七八年嵌入式,从最早拿51单片机点灯开始,到后来用STM32跑裸机程序,再到被项目逼着上Linux,这条路我走得不算快,但踩的坑足够多。身边不少做MCU的兄弟一提到Linux就… · 2026/9/26 9:28:42

数据库课后习题答案别硬背:当测试用例集刷,效率翻倍
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍

简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21

OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 0:00:40

向下兼容与向上兼容:接口设计中的兼容性策略与工程实践
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践

一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码