组件与结构¶
A2UI 使用邻接列表模型表示组件层次结构。组件不是嵌套的 JSON 树,而是带有 ID 引用的扁平列表。
为什么使用扁平列表?¶
传统嵌套方法:
- LLM 必须一次性生成完美的嵌套
- 难以更新深层嵌套的组件
- 难以增量流式传输
A2UI 邻接列表:
- ✅ 扁平结构,LLM 易于生成
- ✅ 增量发送组件
- ✅ 通过 ID 更新任何组件
- ✅ 清晰分离结构和数据
邻接列表模型¶
{
"surfaceUpdate": {
"components": [
{"id": "root", "component": {"Column": {"children": {"explicitList": ["greeting", "buttons"]}}}},
{"id": "greeting", "component": {"Text": {"text": {"literalString": "你好"}}}},
{"id": "buttons", "component": {"Row": {"children": {"explicitList": ["cancel-btn", "ok-btn"]}}}},
{"id": "cancel-btn", "component": {"Button": {"child": "cancel-text", "action": {"name": "cancel"}}}},
{"id": "cancel-text", "component": {"Text": {"text": {"literalString": "取消"}}}},
{"id": "ok-btn", "component": {"Button": {"child": "ok-text", "action": {"name": "ok"}}}},
{"id": "ok-text", "component": {"Text": {"text": {"literalString": "确定"}}}}
]
}
}
组件通过 ID 引用子元素,而不是通过嵌套。
组件基础¶
每个组件都有:
- ID:唯一标识符(
"welcome") - Type(类型):组件类型(
Text、Button、Card) - Properties(属性):特定于该类型的配置
标准目录¶
A2UI 定义了按用途组织的标准组件目录:
- 布局:Row、Column、List - 排列其他组件
- 显示:Text、Image、Icon、Video、Divider - 显示信息
- 交互:Button、TextField、CheckBox、DateTimeInput、Slider - 用户输入
- 容器:Card、Tabs、Modal - 分组和组织内容
有关完整的组件库和示例,请参见组件参考。
静态与动态子元素¶
静态(explicitList) - 固定的子 ID 列表:
动态(template) - 从数据数组生成子元素:
对于 /items 中的每个项目,渲染 item-template。详见数据绑定。
填充值¶
组件通过两种方式获取值:
- 字面量 - 固定值:
{"text": {"literalString": "欢迎"}} - 数据绑定 - 来自数据模型:
{"text": {"path": "/user/name"}}
LLM 可以生成带有字面量值的组件,或将它们绑定到数据路径以获取动态内容。
组合界面¶
组件组合成界面(组件):
- LLM 通过
surfaceUpdate生成组件定义 - LLM 通过
dataModelUpdate填充数据 - LLM 通过
beginRendering发出渲染信号 - 客户端将所有组件渲染为原生组件
界面是一个完整、连贯的 UI(表单、仪表板、聊天等)。
增量更新¶
- 添加 - 发送带有新组件 ID 的新
surfaceUpdate - 更新 - 发送带有现有 ID 和新属性的
surfaceUpdate - 移除 - 更新父元素的
children列表以排除已移除的 ID
扁平结构使所有更新都成为简单的基于 ID 的操作。
自定义组件¶
除了标准目录之外,客户端还可以为特定领域的需求定义自定义组件:
- 如何:在你的渲染器中注册自定义组件类型
- 什么:图表、地图、自定义可视化、专用组件
- 安全性:自定义组件仍然是客户端可信目录的一部分
自定义组件从客户端的渲染器通告给 LLM。然后 LLM 可以在标准目录之外使用它们。
有关实现细节,请参见自定义组件指南。
最佳实践¶
- 描述性 ID:使用
"user-profile-card"而不是"c1" - 浅层次结构:避免深度嵌套
- 分离结构和内容:使用数据绑定,而不是字面量
- 使用模板重用:一个模板,通过动态子元素生成多个实例