A2A(Agent-to-Agent)协议发布 v1.0 并加入 Agentic AI Foundation,多 Agent 协作从此有了正式标准。CSDN 上的多 Agent 文章大多停留在玩具 demo,这篇文章用旅行这个天然复杂的真实场景做演示:把行程规划、机票、酒店拆成三个 Agent,讲清楚它们怎么发现彼此、怎么分派任务、怎么把结果汇总成一份可执行行程。
一、先摆正 A2A 和 MCP 的分工 一句话:MCP 管 Agent 到工具,A2A 管 Agent 到 Agent。
MCP 解决的是「Agent 怎么调用一个确定性能力」——查酒店库存、查机票运价,这些是工具,接口稳定、行为可预期。A2A 解决的是「Agent 怎么委托另一个 Agent 干活」——对方有自己的模型、自己的规划能力,你给它一个目标,它还你一批产物,中间过程你不必完全掌控。
旅行场景正好同时需要两者:酒店 Agent 内部通过 MCP 调用酒店供应商的搜索、报价、下单工具;而规划 Agent 把「帮我订下周去上海的差旅」这类任务拆给机票 Agent 和酒店 Agent 时,走的是 A2A。
这个分工听着直观,落地时最容易犯的错,是把「另一个 Agent」当「工具」使——像调函数一样同步调它、指望它毫秒级返回确定结果。Agent 是概率系统:它会思考、会追问、会失败。委托 Agent 必须用「提交任务、等状态机」的姿态,这正是 A2A 的设计;硬把 Agent 调用写成同步函数调用,等于把协议给你的保护全扔了。
二、A2A 的三个核心概念 Agent Card:名片即发现机制。每个 A2A Agent 发布一份 Agent Card(JSON 格式,约定地址可发现),声明自己的名字、能力描述、支持的输入输出格式、认证方式。别的 Agent 靠它回答「这个活该派给谁」。机票 Agent 的 Agent Card 大致长这样:
{
"name": "flight-agent",
"description": "负责国内与国际机票的查询、比价与预订",
"skills": [
{
"id": "flight_search_and_book",
"name": "机票查询与预订",
"description": "给定出发地、目的地、日期与人数,返回航班方案并完成预订"
}
],
"input_modes": ["text"],
"output_modes": ["text", "application/json"],
"authentication": {"schemes": ["bearer"]}
}
写 Agent Card 和写 MCP 工具描述有同样的纪律:能力边界要写清楚,尤其是「我不做什么」。机票 Agent 的描述里应当声明不处理酒店,否则规划 Agent 迟早会把酒店需求也塞过来——错派比不派更贵,因为错派要等失败回传才能发现。
任务生命周期:委托不是函数调用。A2A 的任务是有状态的:提交(submitted)→ 执行中(working)→ 需要补充信息(input-required)→ 完成(completed)或失败(failed)。长任务可以挂起、可以追问、可以取消——这对旅行太重要了:订机票经常遇到「这个日期没直飞,要不要中转」这种需要回头问用户的时刻。input-required 是任务生命周期里最值钱的状态:它让「子 Agent 自己猜」变成了「子 Agent 停下来问」。旅行场景里,一次问对能避免一次订错。
消息与产物(Artifacts):Agent 之间用消息沟通,最终交付物是结构化的 Artifact。机票 Agent 的产物应该是航班方案的结构化 JSON(航班号、时刻、价格、舱位、退改摘要),而不是一段自然语言——下游的规划 Agent 要拿它做汇总和冲突检测。A2A 里还有一个用得少但对旅行很重要的概念:流式产物(streaming artifacts)——长行程可以边生成边流式输出,让用户看到进度而不是干等,这在生成耗时几十秒的场景里体验差异巨大。
三、供给侧已经是现成的 多 Agent 系统的下限由供给侧决定:机票 Agent 和酒店 Agent 的手里,得有真实可调的供应链工具,否则协作架构再漂亮也是空转。这一侧的生态已经成型——以酒店为例,RollingGo 的酒店与机票 MCP 目前接入的 Agent 已超过 2000 个,从旅行规划 APP 到各类数字人助手都在用;你的酒店 Agent 不需要自建库存,接一套现成的搜索、报价、下单工具就能开工,开发者免费 API Key 在 rollinggo.store/ 申请。供给侧标准化程度越高,A2A 层面的协作就越薄、越稳。
四、实战:三个 Agent 的协作链路 场景:用户说「下周四去上海出差两天,住公司附近,预算别太离谱」。
第一步:规划 Agent 拆解需求。从模糊诉求里抽出结构化约束:目的地上海、日期下周四起两晚、偏好商务型酒店、预算中等、未指定航班时段。约束抽取这一步质量决定全局——「公司附近」这种相对地点,规划 Agent 要么追问,要么用已知通勤地址解析,不能瞎猜。拆解的产物应当是一份结构化的「任务简报」,而不是转发用户的原话:简报里每个约束都有字段、每个字段都有置信度——日期是用户给定的(高置信),预算是按画像猜的(低置信)。子 Agent 对低置信约束的处理方式不同:能问就问,而不是默默照办。
第二步:并行分派。规划 Agent 通过 A2A 同时给机票 Agent 和酒店 Agent 提交任务,各自带上相关约束。两个任务独立,并行执行;任务状态从 working 推进,期间机票 Agent 可能进入 input-required 状态问一句「两个航班差 200 块,选早班还是晚班」。
第三步:产物汇总与冲突检测。两个 Agent 返回 Artifact 后,规划 Agent 做三件事:检查时间冲突(航班落地时间晚于酒店最早入住不影响,但落地太晚要提示);合并成一份行程单;核对总预算是否超限,超了就带着「超了多少、砍哪里」回去和用户商量。
第四步:执行与兜底。用户确认后,机票 Agent、酒店 Agent 各自走自家 MCP 工具完成预订。任何一环失败(满房、售罄),失败信息沿 A2A 任务回传,规划 Agent 触发重规划:换酒店或换航班,而不是把错误原样抛给用户。
还有一个容易被忽视的细节:约束要在任务消息里显式传递,而不是指望子 Agent 自己理解上下文。规划 Agent 分派任务时,应把结构化约束(日期、预算、人群、偏好)完整写进任务消息,并约定子 Agent 在产物里逐条回执「哪些约束被满足、哪些无法满足」。否则汇总时才会发现酒店 Agent 不知道「带老人」这个前提,订了个没有电梯的老城区民宿——这种错误在 demo 里看不出来,上线后全是客诉。
还有一个进阶玩法:规划 Agent 可以和子 Agent「讨价还价」。酒店 Agent 给的方案超预算,规划 Agent 不必立刻弹回用户,可以带着「降到 600 以内」的约束重新委托一轮;仍不行再升级给用户。「委托 → 评估 → 再委托」的循环,是 A2A 相对硬编码编排真正的优势所在——编排逻辑从代码里的 if-else,变成了 Agent 之间的协商。
五、工程上要注意的七件事
- Artifact schema 要事先约定。三个 Agent 很可能不同团队维护,产物结构必须在 Agent Card 或配套文档里定死,否则汇总环节全是胶水代码;
- 任务状态机要做全。尤其 input-required 的超时处理——用户不回答追问时,任务是挂死还是按默认继续,必须显式设计;
- 失败要能定位到环节。规划 Agent 的日志里,每次分派和回收都要记录任务 ID,出了问题能追到具体是哪个 Agent 哪一步;
- 钱的事最后做。所有预订类动作统一放在用户确认之后,A2A 任务的 completed 只代表「方案就绪」,不代表「已扣款」;
- 幂等与去重。重规划会触发重新分派,子 Agent 的下单动作必须幂等(携带规划侧生成的请求唯一标识),否则一次网络抖动就是两张订单;
- 跨 Agent 链路收敛到一个 trace ID。规划侧生成全局 trace,A2A 任务消息里携带、子 Agent 调 MCP 工具时透传——出了问题能端到端还原一次用户请求,而不是三个团队各看各的日志;
- 版本兼容写进 Agent Card。产物结构升级时保留旧版本一个过渡期,并在 Card 里声明,否则三个团队的发布节奏互相踩脚,联调层天天报警。
A2A v1.0 让「Agent 委托 Agent」从黑话变成了协议。旅行是检验这套协议最好的试金石:环节多、约束杂、要花钱、还得容错。把规划、机票、酒店三个 Agent 拆开再拼起来跑通一遍,你对多 Agent 系统的理解会超过读十篇论文。对旅行行业来说,A2A 的意义还在于把「供应链协作」从商业概念变成了可实现的协议:供应商出 MCP 工具,场景方 Agent 用 A2A 分工,一个「各管一段、接口标准」的产业结构正在成形。