Skip to content

数据流

消息如何从代理流向 UI。

架构

代理 (LLM) → A2UI 生成器 → 传输层 (SSE/WS/A2A)
客户端 (流读取器) → 消息解析器 → 渲染器 → 原生 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"

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. 代理信号渲染:

{"beginRendering": {"surfaceId": "booking", "root": "root"}}

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:双向、实时
  • ... 其他

有关更多详细信息,请参阅 传输层

渐进式渲染

与其等待整个响应生成后再向用户显示任何内容,不如将响应块在生成时流式传输到客户端并渐进式渲染。

用户可以实时看到界面构建,而无需盯着加载微调器。

错误处理

格式错误的消息: 跳过并继续,或发送错误回代理以进行更正 网络中断: 显示错误状态,重新连接,代理重新发送或恢复

性能

批处理: 缓冲更新 16ms,批量渲染 差异比较: 比较旧/新组件,仅更新更改的属性 粒度更新: 更新 /user/name 而非整个 / 模型