跳到主要内容

04 · 流、异步与安全

本章讲三件事:长跑任务的三种跟踪方式(重点是 SSE 流和 push webhook)、流的有序/广播/重连保证、以及把“黑盒 agent + 企业级安全”落到协议层的那些硬约束。

1. 三种跟踪方式,各有适用场景

任务可能跑几秒,也可能跑几小时。A2A 给三种互补的跟踪机制(docs/specification.md:646-677):

机制怎么工作适合
轮询(GetTask)客户端定期查任务状态简单集成、更新不频繁、客户端在严格防火墙后
流(SSE)服务端通过长连实时推事件交互式、实时进度、增量大结果;需 capabilities.streaming
push 通知(webhook)状态变化时服务端 POST 到客户端的 webhook超长任务、客户端不能保持长连(移动端/serverless);需 capabilities.pushNotifications

三者可组合:流式跟实时,断了用 push 兜底,关键节点再用 GetTask 拉全量。

2. 流(SSE):实时推增量

机制(docs/topics/streaming-and-async.md:5-25)。 客户端用 SendStreamingMessage 发初始消息并同时订阅该任务;服务端回 200 OK + Content-Type: text/event-stream,连接保持打开,服务端往里推事件。

推的事件是 StreamResponse(specification/a2a.proto:790),一个 oneof,四选一:

StreamResponse(oneof payload)
├─ task : 当前完整 Task 状态
├─ message : agent 发来的一条消息
├─ status_update : TaskStatusUpdateEvent(状态变了,如 working→completed)
└─ artifact_update : TaskArtifactUpdateEvent(产出了/更新了 artifact)

增量 artifact 怎么拼。 TaskArtifactUpdateEvent(specification/a2a.proto:308)带两个布尔位:append(true 表示追加到同 ID 的旧 artifact)和 last_chunk(true 表示这是最后一块)——用来把大文件/大结构分块流式传输并在客户端重组。

三条硬保证(docs/specification.md:679-700):

  • 有序:事件必须按生成顺序投递,任何绑定都不得重排;
  • 多流广播:同一任务可有多条并发流(多个客户端/同客户端多连),事件必须广播给所有活跃流、顺序一致,关一条流不影响其他流;
  • 任务生命周期独立于任何单条流

断线重连。 流中途断了而任务还活着,客户端用 SubscribeToTask(specification/a2a.proto:76)重新订阅。注意这个 RPC 对已到终态的任务会返回 UnsupportedOperationError(specification/a2a.proto:74-75)。任务到终态/中断态时,服务端关流、不再推(docs/topics/streaming-and-async.md:23)。

3. push 通知:断连场景的兜底

适用(docs/topics/streaming-and-async.md:43-64)。 任务可能跑几分钟到几天,或客户端是移动端/serverless 不便保持长连——这时让服务端在关键状态变化时主动 POST 到客户端给的 webhook。

配置怎么给。 客户端提供 PushNotificationConfig(对应 proto 的 TaskPushNotificationConfig,specification/a2a.proto:469),含 url(HTTPS webhook)、可选 token(客户端侧校验用)、可选 authentication(让服务端向 webhook 鉴权)。给法有二:随初始 SendMessage 带,或对已有任务调 CreateTaskPushNotificationConfig

负载是什么 + 客户端怎么接。 通知体就是前面那个 StreamResponse(task/message/statusUpdate/artifactUpdate 四选一);且不论 agent 自己用哪种绑定,webhook 调用一律用普通 HTTP + HTTP 绑定定义的 JSON 负载(docs/specification.md:677)。客户端收到通知、验真后,通常再用 GetTask 拉回完整任务(docs/topics/streaming-and-async.md:56)。

Server ──POST(StreamResponse JSON, 带鉴权)──▶ Client Webhook 服务
│ 1. 验真(是不是合法 Server)
│ 2. 验 token / 防回放

Client ──GetTask(taskId)──▶ Server
◀── 完整 Task + 新 artifacts ──

4. push 的安全:SSRF 与回放,两端都要防

push 是“服务端主动向外发起请求”,天然危险,所以发送端和接收端都有责任(docs/topics/streaming-and-async.md:77-110):

发送端(A2A Server)防 SSRF。 服务端不应盲目信任客户端给的任意 URL——恶意 URL 可能指向内网服务或第三方,造成 SSRF 或 DDoS 放大。缓解:域名白名单、所有权验证(挑战-应答)、出口防火墙。并且服务端必须authentication 指定的方案向 webhook 鉴权(Bearer/API key/HMAC/mTLS)。

接收端(client webhook)防伪造与回放。 webhook 必须严格验证来的请求确实出自合法 Server(验 JWT 签名/HMAC/API key,并校验 token);防回放靠时间戳(拒绝太老的)+ 一次性 nonce(如 JWT 的 jti);并做密钥轮换(JWKS)。

规范给了一个完整的 JWT + JWKS 非对称密钥流作示范(docs/topics/streaming-and-async.md:96-110):Server 用私钥签 JWT(带 iss/aud/iat/exp/jti/taskId)、公钥挂在 JWKS 端点;webhook 按 kid 取公钥验签、再校验各项 claim 和 token

5. 黑盒安全:不泄露“资源是否存在”

A2A 把“agent 是黑盒”落实成了安全约束,而不只是口号。最能体现的是错误处理规则(docs/specification.md:511-529):

  • 授权错误时,服务端指出缺哪个权限/scope,但不得泄露客户端无权访问的资源的信息;
  • 资源不存在与无权访问不应区分(都按 not-found 处理),防止信息泄露——比如你不能通过“404 还是 403”探出“别人的某个任务 ID 是否存在”。

鉴权在协议消息之外。 A2A 沿用标准 Web 安全:鉴权要求声明在名片里,凭证(OAuth token / API key)走 HTTP 头,与 A2A 消息体分开(docs/topics/key-concepts.md:102)。安全方案由 SecurityScheme(specification/a2a.proto:503)的 oneof 表达:API key / HTTP auth / OAuth2 / OpenID Connect / mTLS。OAuth2 流里 v1.0 加了 Device Code(RFC 8628)和 PKCE,移除了已废弃的 implicit/password 流(specification/a2a.proto:566-579docs/whats-new-v1.md 主题 4)。

任务内授权(In-Task Authorization)。 任务跑到一半可能需要访问受保护的下游资源,这时 agent 可把任务切到 AUTH_REQUIRED 态,要客户端补凭证后再继续(spec §7.6,docs/specification.md:1908)——这正是 §02 状态机里那个中断态的实际用途之一。

6. 扩展(Extensions):在不动核心的前提下加能力

扩展让 agent 声明“核心之外的额外能力”而不破坏互通(docs/specification.md:998-1051)。agent 在名片 capabilities.extensions 里用 AgentExtension(specification/a2a.proto:423)声明:uri(唯一标识)、descriptionrequired(true 表示客户端必须懂并遵守)。客户端通过绑定特定机制(HTTP 头 A2A-Extensions、gRPC metadata、JSON-RPC 参数)声明自己要用哪些扩展。

若服务端要求一个 required: true 的扩展但客户端没声明支持,则返回 ExtensionSupportRequiredError(docs/specification.md:576)。

7. 边界与坑

  • Message 不可靠:流断线重连可能漏掉中间 status message(docs/specification.md:762);关键信息靠 artifact + GetTask,别靠 message。
  • webhook 是攻击面:不做 SSRF/回放防护的实现会被滥用——这是 push 最容易出事的地方。
  • 能力没声明就用 = 报错:用 streaming/push/extendedCard 前先查名片 capabilities,否则拿 UnsupportedOperationError / PushNotificationNotSupportedError(docs/specification.md:569-577)。

8. 代码地图

主题文件符号 / 锚点
流事件(四选一)specification/a2a.protomessage StreamResponse(:790)
状态更新事件specification/a2a.protomessage TaskStatusUpdateEvent(:296)
增量 artifact 事件specification/a2a.protomessage TaskArtifactUpdateEvent(:308)、append/last_chunk
重订阅(终态报错)specification/a2a.protorpc SubscribeToTask(:76)
push 配置specification/a2a.protomessage TaskPushNotificationConfig(:469)
安全方案(oneof)specification/a2a.protomessage SecurityScheme(:503)
扩展声明specification/a2a.protomessage AgentExtension(:423)
流 / push 叙事docs/topics/streaming-and-async.md全篇
安全考量docs/specification.md§7 鉴权(:1871)、§13 安全(:3069)
更新交付机制docs/specification.md§3.5(:646)