
2025年4月,Google发布A2A(Agent-to-Agent)协议时,官方博客给出了一段看似简单的判断:“API是刚性的、确定性的;Agent是流动的、自主的。把Agent当成API来对待,会严重限制其潜力”。这句话划定了A2A与REST API之间的本质区别,也划定了A2A与MCP之间的分工边界。
但这个判断本身隐藏着一个需要被审视的假设:“流动和自主”在协议层面到底意味着什么? 如果A2A只是把API调用包装成JSON-RPC消息,那它不过是换了个传输格式的远程过程调用。A2A的实质区别在于Task作为一等公民的设计——任务不是一次请求-响应的原子操作,而是一个有生命周期、有中间状态、可以被中断和恢复的持久实体。
这个设计选择的代价,在协议落地一年后开始显现。A2A v1.0于2026年3月发布,提供了“第一个稳定的开放标准,让Agent跨框架和供应商边界相互发现和协调工作”。但同一份指南紧接着给出了一句刺眼的限定:“规范只解决通信问题。因为v1.0.0刻意排除了内置授权,使用宽泛的OAuth作用域部署这些系统会带来下游数据泄漏和Agent冒充的风险”。
这是理解A2A当前状态的关键。它不是一个“尚未完善”的协议,而是一个在架构层面刻意选择了极简主义的协议。这个选择的后果,远比“安全性待加强”这样的模糊评价要具体和深远。
A2A最核心的架构决策,是把“任务”而非“消息”作为协议的基本单元。一个Task在A2A中经历八个离散状态:submitted、working、input_required、auth_required、completed、failed、canceled、rejected。其中input_required和auth_required是中断状态——任务在此处暂停,等待外部输入或授权,而不是继续消耗计算资源或保持连接。
这个设计解决了一个实际问题。在传统API调用中,如果一次请求需要用户授权或补充信息,调用方必须保持连接或轮询,而服务端必须维持会话状态。A2A的中断状态把“等待”从连接层解耦到了任务层:客户端收到input_required后可以断开,在获得用户输入后通过taskId恢复任务。TaskId成为了跨连接、跨会话的持久引用。
以下是一个简化的A2A任务状态转移的TypeScript类型定义,展示了协议层面的核心约束:
// 基于 @agentskit/core a2a.d.cts 的类型抽象
type TaskState =
| 'submitted' // 已提交,未开始处理
| 'working' // 处理中
| 'input_required' // 中断:需要用户补充信息
| 'auth_required' // 中断:需要授权
| 'completed' // 终态:成功
| 'failed' // 终态:失败
| 'canceled' // 终态:被取消
| 'rejected'; // 终态:被拒绝
interface Task {
id: string;
contextId: string; // 用于将相关任务分组到同一对话上下文中
state: TaskState;
artifacts?: Artifact[]; // 任务输出
history?: Message[]; // 对话历史
}
// 合法状态转移的约束(简化表示)
const VALID_TRANSITIONS: Record<TaskState, TaskState[]> = {
submitted: ['working', 'failed', 'rejected', 'canceled'],
working: ['input_required', 'auth_required', 'completed', 'failed', 'canceled'],
input_required: ['working', 'failed', 'canceled'],
auth_required: ['working', 'failed', 'canceled'],
// 终态不可转移
completed: [], failed: [], canceled: [], rejected: [],
};这个状态机的工程意义在于:它把“任务在多个Agent之间流转”从隐式约定变成了显式契约。一个编排Agent可以可靠地知道,当它收到一个input_required的Task时,对端Agent正在等待用户输入,而不是在“假装工作”或者陷入了死循环。对于跨组织边界的Agent协作——这正是A2A的设计目标——这种可观测性是部署可靠性的前提。
A2A的另一个核心机制是Agent Card——一个位于/.well-known/agent-card.json的JSON文档,声明Agent的身份、技能、端点URL、支持的传输绑定和安全方案。
这个设计在工程上是优雅的。编排Agent不需要预先知道另一个Agent的接口细节,只需获取其Agent Card,解析其中的skills数组,匹配任务需求,然后向声明的endpoint发送请求。能力的“协商”通过JSON Schema的输入输出声明完成,而非通过人类编写的接口文档。
但优雅的代价是发现层的信任缺失。
2026年6月发表在IEEE的一项研究首次系统性地测量了“Agent Card投毒”攻击。攻击方式极其直接:攻击者在一个Agent Card的url字段中填入自己控制的端点,编排Agent在发现阶段解析卡片后,会忠实地将后续任务路由到攻击者的服务器。研究测试了三种攻击层级——朴素攻击(纯文本覆写)、结构化攻击(JSON嵌入)和自然语言社会工程攻击——在无防护的编排Agent上,三种攻击全部成功。
正则清洗(D1)能拦截所有语法显式的尝试,但被自然语言社会工程攻击全部绕过。LLM-based的卡片审计(D3)在试点中拦截了所有9次攻击尝试,在100次良性测试中零误报,但研究明确标注这是试点规模(n=3 per tier per combo),需要更大规模的验证。
这项研究的价值不在于“发现了一个漏洞”,而在于它揭示了一个结构性问题:A2A的发现层假设Agent Card是可信的,但协议本身不提供任何密码学验证机制。Agent Card是明文JSON,通过HTTP GET获取,没有签名,没有证书绑定。在一个只涉及同一组织内部Agent的场景中,这个问题可能被网络边界防护掩盖。但A2A的整个价值主张就是“跨组织边界协作”——当编排Agent需要发现并调用另一个公司的专业Agent时,它凭什么信任那张卡片上写的url?
A2A经常被拿来与MCP对比,而两者确实形成了清晰的互补关系。一篇2026年的系统性综述给出了精确的界定:“MCP是‘工具连接的USB-C’(垂直的,Agent到工具),A2A是‘Agent协作的HTTP’(水平的,Agent到Agent)。生产系统通常同时使用两者:A2A将任务路由到正确的专业Agent;MCP为该Agent提供上下文和工具”。
这个类比在架构层面是准确的。MCP解决的是“一个Agent如何安全、标准化地调用外部工具和数据源”;A2A解决的是“一个Agent如何发现、协商并委派任务给另一个Agent”。两者不竞争,是同一栈的两层。
但“互补”的表述掩盖了一个重要的不对称。MCP在2026年7月经历了一次大规模修订,其核心变化是“从有状态的、需要握手和会话管理的协议,彻底转向了无状态核心”。这次修订的方向是降低协议的认知负荷和工程复杂度。而A2A的设计方向恰好相反:它的核心价值恰恰在于Task作为有状态、跨连接、可中断恢复的持久实体。A2A的有状态性是它的功能,不是它的负担。
这意味着,一个团队如果同时使用MCP和A2A,它需要维护两套完全不同的状态管理心智模型。MCP侧,每次调用自包含,状态由应用层管理;A2A侧,Task本身承载状态,协议层管理生命周期。这种认知切换的成本,在2026年的工程实践中尚未被充分讨论。
A2A v1.0明确将授权委托给外部机制:“A2A将认证委托给标准Web机制,主要依赖HTTP头和OAuth 2.0、OpenID Connect等既定标准”。这个选择的理由是合理的——协议不需要重新发明OAuth。但“委托”的代价是,协议层面无法对授权行为的语义施加任何约束。
2026年6月的一项研究专门分析了A2A的Token管理风险,结论是:“A2A标准没有对Token使用上下文、授权委托或Agent之间的Token传播施加严格规范,这可能导致Token泄露和重用漏洞”。
这个发现的实践含义是严重的。考虑一个典型的多Agent工作流:用户通过前端Agent发起请求,前端Agent将任务委派给专业Agent A,A又将子任务委派给专业Agent B。在这个过程中,用户的OAuth Token需要从用户→前端Agent→A→B传播。如果Token的scope是宽泛的(比如read:all),那么B获得了远超其任务需要的权限。如果Token的传播没有绑定到特定任务上下文,那么B可以将Token重用于其他目的。如果Token的lifetime没有被严格约束,那么一旦泄露,攻击窗口是开放的。
A2A的协议规范没有强制任何这些约束。它提供了auth_required状态来标记需要授权的任务中断,但授权决策本身不在协议范围内。这意味着,部署A2A的生产系统需要自行设计一套Token传播和验证机制,而协议不提供任何参考实现或规范性约束。
更隐蔽的问题是“意图泄漏”(Intent Leakage)。一篇2026年的论文识别了工具增强架构中的治理盲区,指出A2A在任务委派过程中,调用方的原始意图——包括用户的目标、约束条件、敏感偏好——会随着任务描述传播到所有下游Agent。如果下游Agent属于不同的信任域,这些意图信息就构成了跨边界的信息泄漏。协议没有提供意图的最小化披露机制。
A2A v1.0是一个通信协议,不是一个安全协议。它的设计目标是以最小的规范复杂度,解决Agent之间“如何说同一件事”的问题。它成功地做到了这一点:八个任务状态、Agent Card发现、JSON-RPC/HTTP+JSON/gRPC三种传输绑定,构成了一个足够简单以至于可以被不同框架和供应商实现的标准。
但“足够简单”的另一面是“不够安全”。Agent Card的发现层信任是明文且无验证的。Token传播没有上下文绑定。意图在委派链中全量传播。这些问题在协议层面被识别,但被有意地排除在规范范围之外——委托给OAuth、mTLS、应用层的授权策略。
对于正在部署A2A的工程师,这意味着几件具体的事。第一,Agent Card必须被签名和验证,否则发现层就是攻击者的入口。第二,Token必须绑定到任务上下文,不能允许一个Agent将Token用于它被授权范围之外的操作。第三,委派链中的意图信息需要最小化,只传递下游Agent完成任务所必需的信息,而非调用方的完整上下文。
协议不会替你解决这些问题。这是它的设计选择,也是它的工程代价。A2A让Agent之间“能说话”变得便宜,但让它们“安全地说话”仍然昂贵。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。