数据流¶
消息如何从代理流向 UI。
架构¶

消息格式¶
A2UI 定义了一系列描述 UI 的 JSON 消息。当进行流式传输时,这些消息通常被格式化为 JSON Lines (JSONL),其中每一行都是一个完整的 JSON 对象。
{"surfaceUpdate":{"surfaceId":"main","components":[...]}}
{"dataModelUpdate":{"surfaceId":"main","contents":[{"key":"user","valueMap":[{"key":"name","valueString":"Alice"}]}]}}
{"beginRendering":{"surfaceId":"main","root":"root-component"}}
为什么使用这种格式? 自包含的 JSON 对象序列对流式传输友好,易于 LLM 增量生成,并且对错误具有弹性。
生命周示例:餐厅预订¶
用户: "Book a table for 2 tomorrow at 7pm" (明天晚上 7 点预订一张 2 人的桌子)
1. 代理定义 UI 结构:
{"surfaceUpdate": {"surfaceId": "booking", "components": [
{"id": "root", "component": {"Column": {"children": {"explicitList": ["header", "guests-field", "submit-btn"]}}}},
{"id": "header", "component": {"Text": {"text": {"literalString": "Confirm Reservation"}, "usageHint": "h1"}}},
{"id": "guests-field", "component": {"TextField": {"label": {"literalString": "Guests"}, "text": {"path": "/reservation/guests"}}}},
{"id": "submit-btn", "component": {"Button": {"child": "submit-text", "action": {"name": "confirm", "context": [{"key": "details", "value": {"path": "/reservation"}}]}}}}
]}}
2. 代理填充数据:
{"dataModelUpdate": {"surfaceId": "booking", "path": "/reservation", "contents": [
{"key": "datetime", "valueString": "2025-12-16T19:00:00Z"},
{"key": "guests", "valueString": "2"}
]}}
3. 代理发出渲染信号:
4. 用户将人数编辑为 "3" → 客户端自动更新 /reservation/guests(此时没有发送消息给代理)
5. 用户点击 "Confirm" (确认) → 客户端发送带有更新数据的操作:
{"userAction": {"name": "confirm", "surfaceId": "booking", "context": {"details": {"datetime": "2025-12-16T19:00:00Z", "guests": "3"}}}}
6. 代理响应 → 更新 UI 或发送 {"deleteSurface": {"surfaceId": "booking"}} 以进行清理
传输选项¶
- A2A 协议: 多代理系统,也可用于代理到 UI 的通信
- AG UI: 双向、实时
- ... 其他
有关详细信息,请参阅 传输层。
渐进式渲染¶
不需要等待生成完整的响应后再向用户显示任何内容,响应的块可以在生成时流式传输到客户端并渐进式渲染。
用户可以看到 UI 实时构建,而不是盯着加载微调器。
错误处理¶
格式错误的消息: 跳过并继续,或向代理发送错误以进行更正 网络中断: 显示错误状态,重新连接,代理重新发送或恢复
性能¶
批处理 (Batching): 缓冲更新 16ms,批量渲染
差异比较 (Diffing): 比较旧/新组件,仅更新更改的属性
粒度更新 (Granular updates): 更新 /user/name 而不是整个 / 模型