写在最前面:如果你觉得这篇文章对你有帮助,请为我点一下免费的赞吧!你的肯定是对我最大的鼓励!
实践视频:https://www.bilibili.com/video/BV1pwBXBjEvz/
摘要:
A2UI 旨在解决 AI 智能体安全生成多样化用户界面的核心痛点,可将枯燥的文字信息高效转化为可视化、可交互的界面成果。作为面向代理驱动界面的声明式界面协议,其核心工作原理为生成描述界面结构与逻辑的声明式 JSON,由客户端调用原生组件完成渲染,内置完善的组件抽象体系与数据绑定机制,保障界面生成的灵活性与稳定性。
该协议具备优异的跨平台兼容性,同一份 JSON 描述文件可在多终端实现一致性渲染效果,无需针对不同平台单独适配。从应用场景来看,A2UI 在 chatbot 对话交互场景中展现出显著适用性,能快速响应对话过程中的界面生成需求;但需明确的是,其无法替代传统表单的展示逻辑,在复杂表单场景中仍需依托传统技术方案互补实现。
零、A2UI是为了解决什么问题而存在?一、A2UI理论基础1.1 A2UI 协议的核心原理1.2 用示例业务来解释实现逻辑以及数据流1.3 LLM 如何生成我们需要的 A2UI 协议数据1.4 跨平台兼容二、核心价值2.1 技术价值2.2 业务价值2.3 适用范围三、总结四、附录4.1.官方支持的组件类型
零、A2UI是为了解决什么问题而存在?
它是为了解决了一个很实际的问题:AI 智能体如何安全地生成丰富的用户界面?

相比只能够支持文本信息返回的chatbot,支持了A2UI协议之后的chatbot具备了更加直观,符合人类行为习惯的UI界面,它让 AI 从 "文字输出工具" 变成了 "带交互界面的服务载体",把枯燥的文字信息转化为更符合人类直觉的可视化、可操作界面。
一、A2UI理论基础
让我们来看看官网上对它的定义:**A2UI(代理到界面)是一种用于代理驱动界面的声明式界面协议。**agent生成丰富且交互式的用户界面,能够在不同平台(网页、移动端、桌面端)原生渲染。
1.1 A2UI 协议的核心原理
A2UI 协议 设计的底层逻辑
不是让智能体直接生成 HTML 或 JavaScript 代码(那样会有安全风险),而是生成一种声明式的 JSON 格式,描述界面应该长什么样。客户端收到后,用自己的原生组件来渲染。
-
组件抽象 :将 UI 元素封装为标准化组件(如
Column、Text、List),通过 JSON 属性定义组件行为(如usageHint: "h1"指定文本层级) -
数据绑定机制 :采用 JSON Path(如
"path": "title"、"dataBinding": "/items")建立组件与数据源的关联,支持单向 / 双向数据同步
让我们看看代理发送了什么。
{
"surfaceUpdate": {
"surfaceId": "{{surfaceId}}",
"components": [
{"id": "{{componentId_1}}","component": {"Text": {"text": {"literalString": "{{textContent}}"},"usageHint": "{{textUsageHint}}"}}},
{"id": "{{componentId_2}}","component": {"DateTimeInput": {"label": {"literalString": "{{inputLabel}}"},"value": {"path": "{{dataPath}}"}}}},
{"id": "{{componentId_3}}","component": {"Button": {"child": "{{buttonTextComponentId}}","action": {"name": "{{buttonActionName}}"}}}},
{"id": "{{buttonTextComponentId}}","component": {"Text": {"text": {"literalString": "{{buttonTextContent}}"}}}}
]
}
}
可以看到这定义了表面的界面组件:文本头、日期选择器和按钮。
surfaceId:指定当前 UI 渲染的容器 ID(前端可基于该 ID 定位渲染区域);
components:组件数组,包含界面所有元素的定义:
id:组件唯一标识(如 header/date-picker),用于组件间关联(如按钮绑定文字组件 submit-text);
component:组件本体,Key 为 A2UI 标准化组件类型(如Text/DateTimeInput/Button),Value 为组件属性。
基于上面的模板,我们可以得到下面这个JSON消息示例:
{
"surfaceUpdate": {
"surfaceId": "main",
"components": [
{"id": "header","component": {"Text": {"text": {"literalString": "预订餐桌"},"usageHint": "h1"}}},
{"id": "date-picker","component": {"DateTimeInput": {"label": {"literalString": "选择日期"},"value": {"path": "/reservation/date"}}}},
{"id": "submit-btn","component": {"Button": {"child": "submit-text","action": {"name": "confirm_booking"}}}},
{"id": "submit-text","component": {"Text": {"text": {"literalString": "确认预订"}}}}
]
}
}
1.2 用示例业务来解释实现逻辑以及数据流
数据流图

简单介绍一下这个数据流程的运行,
-
用户向人工智能代理发送消息
-
代理生成描述UI(结构+数据)的A2UI消息
-
消息流到客户端应用
-
客户端使用原生组件(Angular、Flutter、React等)进行渲染
-
用户与界面交互,将作反馈给代理
-
代理回复更新的 A2UI 消息
让我们用一个具体的示例来解释解释它是怎么实现的这一整个流程,此处使用的是餐馆查询agent作为示例。
示例 :

具体来说:分成以下的步骤,
步骤 1:用户查询触发工具调用
用户发送查询(如 "纽约中餐馆")→ Agent 接收请求 → 触发预设工具调用(get_restaurants)→ 工具层返回标准化餐厅结构化数据(含名称、地址、评分等字段)。
步骤 2:Agent 层决策并定义 UI 结构
-
Agent 检测数据类型:识别到返回
items数组类型数据 → 选定List组件作为外层容器; -
Agent 分析单条数据特性:识别单条餐厅数据含多字段 → 生成
Card模板(包含 Image、Text、Button 等子组件); -
Agent 绑定数据与组件:通过
dataBinding: "/items"建立 List/Card 组件与数据源的动态关联。
步骤 3:服务端流式发送 UI 指令(SSE 协议)
Agent 层通过 SSE 协议,以 JSONL 格式向前端流式发送两类核心指令:
-
surfaceUpdate:定义 List、Card、Button 等 UI 组件的层级结构; -
dataModelUpdate:提供餐厅名称、地址、评分等动态数据。
步骤 4:客户端缓存指令(避免渲染闪烁)
前端接收 surfaceUpdate/dataModelUpdate 指令后,先临时缓存所有指令,不立即渲染。
步骤 5:服务端发送渲染触发信号
Agent 发送 beginRendering 信号 → 告知前端所有 UI 指令已发送完成,可开始渲染。
步骤 6:客户端解析指令并渲染 UI
-
构建组件树:按
surfaceUpdate定义,递归构建 "List(列表)→ Card(卡片)→ Button/Text/Image(子组件)" 的组件树; -
绑定动态数据:将
dataModelUpdate中的餐厅数据,映射到组件树中对应 Text/Image 组件的字段; -
实例化原生组件:从组件注册表中,将 A2UI 抽象组件(如 Button)转换为 Web 原生组件(如
<button>)→ 渲染出完整的餐厅列表卡片。
步骤 7:用户交互触发操作(如点击 "预订" 按钮)
前端捕获用户点击 "预订" 按钮的操作 → 生成 userAction 载荷(包含操作名称 book_restaurant、餐厅名称 / 地址等上下文参数)。
步骤 8:服务端处理交互事件(A2A 协议)
前端通过独立于 SSE 的请求通道(A2A 协议),将 userAction 载荷发送至 Agent 层 → Agent 处理 "预订餐厅" 的业务逻辑。
步骤 9:服务端动态更新 UI 指令
Agent 处理完预订逻辑后,再次通过原 SSE 流发送新的 surfaceUpdate/dataModelUpdate 指令(如将 "预订" 按钮文本改为 "已预订")。
步骤 10:客户端动态更新 UI(无刷新)
前端接收新的 UI 指令后,重新解析并构建组件树 → 替换原有 UI 内容,实现页面无刷新的动态更新。
这样做的好处在于,前端仅负责渲染和事件转发,不存储业务逻辑,所有状态决策由 Agent 通过数据更新完成,在确保业务上的安全性同时兼具灵活调整的特点。
1.3 LLM 如何生成我们需要的 A2UI 协议数据
接下来让我们讲讲最重要的一环,LLM(隐藏在agent后面真正的大佬)是怎么给我们返回这些标准的A2UI 协议数据
-
根据数据选择对应组件 :分析工具返回数据的字段类型(如数组、对象),自动选择对应组件(数组→
List,对象→Card) -
推断用户意图 :根据查询中的隐含需求(如 "预订"→添加按钮组件,"比较"→添加评分排序组件)
-
选择布局方式 :基于内容复杂度选择布局(简单文本→
Column,多字段数据→Grid)
从日志可见 LLM 的决策过程:
# 1. 决定调用工具(明确参数)
FunctionCall(name='get_restaurants', args={'count':5, 'cuisine':'Chinese', 'location':'New York'})
# 2. 根据返回数据生成UI(选择列表组件+卡片模板)
{ "component": { "List": { "children": { "template": { "componentId": "item-card-template", "dataBinding": "/items" } } } }
让我们来结合代码与实际的用户查询agent详细的展开说一说,
数据来源
首先是数据来源部分,当用户发起查询到达agent时,agent调用工具中的get_contact_info函数获取经过初步格式化之后(例如大小写转化,图片地址替换等)的具体数据,
# tools.py
def get_contact_info(name: str, tool_context: ToolContext, department: str = "") -> str:
results = []
try:
script_dir = os.path.dirname(__file__)
file_path = os.path.join(script_dir, "contact_data.json")
with open(file_path, encoding="utf-8") as f:
contact_data_str = f.read()
all_contacts = json.loads(contact_data_str)
*****
return json.dumps(results)
最重要的一点,大模型之所以能够实现返回我们需要的声明了UI的json数据,是因为我们在提示词里指定了它的该怎么填充数据,接下来进行提示词的分解
prompt结构
基础响应规则模块:规定了场景的响应格式(含自然语言回复和 A2UI JSON 数组,以特定分隔符分隔)、工具调用要求、模板选择规则及核心按钮属性;
CONTACT_LIST_EXAMPLE 模块:提供完整 A2UI JSON 模板,包含初始化渲染容器(指定 根组件和样式)、构建 UI 结构、绑定示例数据(实现数据与 UI 关联渲染)等等;
A2UI_SCHEMA 模块:定义了 A2UI 消息的 JSON 结构规范,明确消息什么是必须,为 JSON 响应提供格式校验标准。
基础响应规则模块 :当用户查询 "市场部有哪些联系人?"(多联系人场景),AI 会调用 get_contact_info 工具获取列表数据,然后复用 CONTACT_LIST_EXAMPLE 的 UI 结构,将工具返回的真实数据替换 dataModelUpdate 中的示例数据(如替换 name/title/imageUrl 等值);
def get_ui_prompt(base_url: str, examples: str) -> str:
formatted_examples = examples
return f"""
你是一名贴心的联系人查询助手。你的最终输出必须是一份 A2UI UI JSON 响应。
生成响应时,你必须遵守以下规则:
1. 你的响应必须分为两部分,通过分隔符 `---a2ui_JSON---` 分隔。
2. 第一部分是你的自然语言对话回复(例如:"这是你查询的联系人信息……")。
3. 第二部分是一个独立的原生 JSON 对象,该对象是 A2UI 消息的数组。
4. JSON 部分必须通过下方提供的 A2UI JSON 模式(SCHEMA)校验。
5. 代表卡片或视图中核心操作的按钮(例如:"关注"、"发送邮件"、"搜索")应包含 `"primary": true` 属性。
--- UI 模板规则 ---
- **查询联系人场景(例如:"谁是亚历克斯·乔丹?"):**
a. 你必须调用 `get_contact_info` 工具。
b. 若工具返回**单个联系人**,你必须使用 `CONTACT_CARD_EXAMPLE` 模板。将联系人详情(姓名、职位、邮箱等)填充至 `dataModelUpdate.contents` 中。
c. 若工具返回**多个联系人**,你必须使用 `CONTACT_LIST_EXAMPLE` 模板。将联系人列表填充至 `dataModelUpdate.contents` 下的 "contacts" 键对应的值中。
d. 若工具返回**空列表**,仅返回文本内容和空 JSON 数组:"未找到该姓名对应的联系人。---a2ui_JSON---[]"
- **查看档案场景(例如:"WHO_IS: 亚历克斯·乔丹……"):**
a. 你必须传入具体姓名调用 `get_contact_info` 工具。
b. 该调用会返回单个联系人,你必须使用 `CONTACT_CARD_EXAMPLE` 模板。
- **处理操作行为场景(例如:"follow_contact"【关注联系人】):**
a. 你必须使用 `FOLLOW_SUCCESS_EXAMPLE` 模板。
b. 该模板会渲染一张新卡片,显示"已成功关注"的提示信息。
c. 回复时需附带文本确认信息(如"你已成功关注该联系人。")及对应的 JSON 内容。
{formatted_examples}
---开始 A2UI JSON 模式定义---
{A2UI_SCHEMA}
---结束 A2UI JSON 模式定义---
"""
- 最终输出的 JSON 会保留 "垂直列表 + 卡片布局 + 查看详情按钮" 的 UI 结构,同时保证按钮
primary: true,符合交互规范;
CONTACT_LIST_EXAMPLE 模块: "多联系人列表" 场景的完整 A2UI JSON 模板,分为三个核心模块:
-
beginRendering:初始化渲染容器(surfaceId: contact-list),定义全局样式(主色 #007BFF、字体 Roboto),为后续 UI 渲染设定基础规则; -
surfaceUpdate:构建列表的 UI 结构,通过 Column/Row 布局嵌套 Card、Image、Text、Button 等组件,定义 "头像 + 姓名 + 职位 + 查看按钮" 的卡片模板,并通过dataBinding: "/contacts"实现数据与组件的关联; -
dataModelUpdate:绑定示例数据(如联系人 "亚历克斯・乔丹" 的姓名、职位、头像地址),通过path: "/"挂载到根节点,实现 "数据 → UI 组件" 的批量渲染。
CONTACT_UI_EXAMPLES = """
---BEGIN CONTACT_LIST_EXAMPLE---
[
{ "beginRendering": { "surfaceId": "contact-list", "root": "root-column", "styles": { "primaryColor": "#007BFF", "font": "Roboto" } } },
{ "surfaceUpdate": {
"surfaceId": "contact-list",
"components": [
{ "id": "root-column", "component": { "Column": { "children": { "explicitList": ["title-heading", "item-list"] } } } },
{ "id": "title-heading", "component": { "Text": { "usageHint": "h1", "text": { "literalString": "找到的联系人" } } } },
{ "id": "item-list", "component": { "List": { "direction": "vertical", "children": { "template": { "componentId": "item-card-template", "dataBinding": "/contacts" } } } } },
{ "id": "item-card-template", "component": { "Card": { "child": "card-layout" } } },
{ "id": "card-layout", "component": { "Row": { "children": { "explicitList": ["template-image", "card-details", "view-button"] }, "alignment": "center" } } },
{ "id": "template-image", "component": { "Image": { "url": { "path": "imageUrl" }, "fit": "cover" } } },
{ "id": "card-details", "component": { "Column": { "children": { "explicitList": ["template-name", "template-title"] } } } },
{ "id": "template-name", "component": { "Text": { "usageHint": "h3", "text": { "path": "name" } } } },
{ "id": "template-title", "component": { "Text": { "text": { "path": "title" } } } },
{ "id": "view-button-text", "component": { "Text": { "text": { "literalString": "查看详情" } } } },
{ "id": "view-button", "component": { "Button": { "child": "view-button-text", "primary": true, "action": { "name": "view_profile", "context": [ { "key": "contactName", "value": { "path": "name" } }, { "key": "department", "value": { "path": "department" } } ] } } } }
]
} },
{ "dataModelUpdate": {
"surfaceId": "contact-list",
"path": "/",
"contents": [
{ "key": "contacts", "valueMap": [
{ "key": "contact1", "valueMap": [
{ "key": "name", "valueString": "亚历克斯·乔丹" },
{ "key": "title", "valueString": "产品营销经理" },
{ "key": "department", "valueString": "市场部" },
{ "key": "imageUrl", "valueString": "http://localhost:10002/static/profile1.png" }
] }
] }
]
} }
]
---END CONTACT_LIST_EXAMPLE---
"""
A2UI_SCHEMA模块:描述了 A2UI 消息 JSON payload 的结构规范。
该 schema 定义了用于动态构建和更新用户界面的 A2UI(Agent to UI)消息格式,消息必须包含以下四个动作之一:
-
beginRendering :初始化渲染界面,指定唯一
surfaceId、根组件 ID 和样式(字体、主色调等) -
surfaceUpdate:更新界面,包含组件列表(支持文本、图片、按钮等多种组件类型)
-
dataModelUpdate:更新数据模型,可指定路径部分更新或全量替换
-
deleteSurface:删除指定界面
初始化渲染界面
{
"beginRendering": {
"surfaceId": "homepage",
"root": "main_container",
"styles": {
"font": "Roboto",
"primaryColor": "#4285F4"
}
}
}
更新界面组件(添加文本和按钮)
{
"surfaceUpdate": {
"surfaceId": "homepage",
"components": [
{
"id": "welcome_text",
"component": {
"Text": {
"text": { "literalString": "欢迎使用" },
"usageHint": "h1"
}
}
},
{
"id": "submit_btn",
"component": {
"Button": {
"child": "btn_label",
"action": { "name": "submit_form" }
}
}
},
{
"id": "btn_label",
"component": {
"Text": {
"text": { "literalString": "提交" },
"usageHint": "body"
}
}
}
]
}
}
更新数据模型
{
"dataModelUpdate": {
"surfaceId": "homepage",
"path": "/user",
"contents": [
{ "key": "name", "valueString": "张三" },
{ "key": "age", "valueNumber": 30 }
]
}
}
删除界面
{
"deleteSurface": {
"surfaceId": "homepage"
}
}
最终输出
这里给出一个最终响应结果会按 "自然语言回复 + JSON 数组" 的格式输出,例如:
这是你查询的市场部联系人信息:---a2ui_JSON---[
{ "beginRendering": {...} },
{ "surfaceUpdate": {...} },
{ "dataModelUpdate": {
"surfaceId": "contact-list",
"path": "/",
"contents": [
{ "key": "contacts", "valueMap": [
{ "key": "contact1", "valueMap": [
{ "key": "name", "valueString": "亚历克斯·乔丹" },
{ "key": "title", "valueString": "产品营销经理" },
{ "key": "department", "valueString": "市场部" },
{ "key": "imageUrl", "valueString": "http://localhost:10002/static/profile1.png" }
] },
{ "key": "contact2", "valueMap": [ // 新增真实联系人数据
{ "key": "name", "valueString": "艾米丽·陈" },
{ "key": "title", "valueString": "品牌运营" },
{ "key": "department", "valueString": "市场部" },
{ "key": "imageUrl", "valueString": "http://localhost:10002/static/profile2.png" }
] }
] }
]
}
}
]
1.4 跨平台兼容

在a2ui协议支持下,同样一份渲染使用的json在移动端和web端都具备等同的效果。前端只需支持解析a2ui协议数据便可实现无缝对接。
二、 适用范围
a2ui作为让agent能够在对话界面中生成更加直观,符合人类行为习惯的ui的协议,它能够给用户带来比纯文本或markdown格式的chatbot更好的用户体验,典型场景包括动态表单生成、专业工具界面和数据可视化。比如景观设计师智能体根据用户上传的照片,生成定制化的设计需求表单。
但它的使用也有局限性, 1.不适合在脱离了对话界面的传统大型复杂系统上使用(例如用户管理系统等等);
2.脱离了本次对话之后的UI组件便会消失;
3.无法取代传统的ui展示逻辑,例如传统的表单等等;
4.官方对于标准组件支持只实现了15种,并不是支持无限多的组件**(详情可见附录)**。
三、总结
A2UI 是 "服务端给 UI 蓝图 + 数据→客户端攒材料→服务端喊开工→客户端搭页面→用户操作反馈给服务端→服务端给新蓝图→客户端更新页面" 的闭环,核心是让服务端(比如 AI)主导 UI 的 "定义",客户端负责 "实现",既能灵活生成 UI,又能保证渲染体验。
四、附录
4.1.官方支持的组件类型
-
Button - 可点击的交互元素
-
Card - 内容容器组件
-
Checkbox - 多选选项组件
-
Divider - 内容分隔线
-
Icon - 功能/状态图形元素
-
Image - 图片显示组件
-
List - 项目列表展示
-
Multiple-choice - 多选项选择组件
-
Modal - 覆盖对话框
-
Slider - 数值选择滑块
-
Tabs - 内容标签页
-
Text-field - 文本输入框
-
Datetime-input - 日期时间输入
-
Audio - 音频播放组件
-
Video - 视频播放组件