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。