【a2ui协议】AI智能体如何安全生成交互界面

写在最前面:如果你觉得这篇文章对你有帮助,请为我点一下免费的赞吧!你的肯定是对我最大的鼓励!

实践视频: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 元素封装为标准化组件(如 ColumnTextList),通过 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 用示例业务来解释实现逻辑以及数据流

数据流图

简单介绍一下这个数据流程的运行,

  1. 用户向人工智能代理发送消息

  2. 代理生成描述UI(结构+数据)的A2UI消息

  3. 消息流到客户端应用

  4. 客户端使用原生组件(Angular、Flutter、React等)进行渲染

  5. 用户与界面交互,将作反馈给代理

  6. 代理回复更新的 A2UI 消息

让我们用一个具体的示例来解释解释它是怎么实现的这一整个流程,此处使用的是餐馆查询agent作为示例。

示例

具体来说:分成以下的步骤,

步骤 1:用户查询触发工具调用

用户发送查询(如 "纽约中餐馆")→ Agent 接收请求 → 触发预设工具调用(get_restaurants)→ 工具层返回标准化餐厅结构化数据(含名称、地址、评分等字段)。

步骤 2:Agent 层决策并定义 UI 结构

  1. Agent 检测数据类型:识别到返回 items 数组类型数据 → 选定 List 组件作为外层容器;

  2. Agent 分析单条数据特性:识别单条餐厅数据含多字段 → 生成 Card 模板(包含 Image、Text、Button 等子组件);

  3. Agent 绑定数据与组件:通过 dataBinding: "/items" 建立 List/Card 组件与数据源的动态关联。

步骤 3:服务端流式发送 UI 指令(SSE 协议)

Agent 层通过 SSE 协议,以 JSONL 格式向前端流式发送两类核心指令:

  1. surfaceUpdate:定义 List、Card、Button 等 UI 组件的层级结构;

  2. dataModelUpdate:提供餐厅名称、地址、评分等动态数据。

步骤 4:客户端缓存指令(避免渲染闪烁)

前端接收 surfaceUpdate/dataModelUpdate 指令后,先临时缓存所有指令,不立即渲染。

步骤 5:服务端发送渲染触发信号

Agent 发送 beginRendering 信号 → 告知前端所有 UI 指令已发送完成,可开始渲染。

步骤 6:客户端解析指令并渲染 UI

  1. 构建组件树:按 surfaceUpdate 定义,递归构建 "List(列表)→ Card(卡片)→ Button/Text/Image(子组件)" 的组件树;

  2. 绑定动态数据:将 dataModelUpdate 中的餐厅数据,映射到组件树中对应 Text/Image 组件的字段;

  3. 实例化原生组件:从组件注册表中,将 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)消息格式,消息必须包含以下四个动作之一:

  1. beginRendering :初始化渲染界面,指定唯一 surfaceId、根组件 ID 和样式(字体、主色调等)

  2. surfaceUpdate:更新界面,包含组件列表(支持文本、图片、按钮等多种组件类型)

  3. dataModelUpdate:更新数据模型,可指定路径部分更新或全量替换

  4. 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.官方支持的组件类型

  1. Button - 可点击的交互元素

  2. Card - 内容容器组件

  3. Checkbox - 多选选项组件

  4. Divider - 内容分隔线

  5. Icon - 功能/状态图形元素

  6. Image - 图片显示组件

  7. List - 项目列表展示

  8. Multiple-choice - 多选项选择组件

  9. Modal - 覆盖对话框

  10. Slider - 数值选择滑块

  11. Tabs - 内容标签页

  12. Text-field - 文本输入框

  13. Datetime-input - 日期时间输入

  14. Audio - 音频播放组件

  15. Video - 视频播放组件

相关推荐
科技快报4 小时前
联想现场演示天禧AI 3.5多模态交互,YOGA Pro 16 Aura AI元启版提供坚实算力支撑
人工智能·microsoft
AC赳赳老秦4 小时前
DeepSeek + Excel 实战:多表联动分析与异常数据自动预警教程
microsoft·rabbitmq·excel·etcd·memcached·memcache·deepseek
天庭鸡腿哥4 小时前
输入序列号,可激活正版软件!
microsoft·macos·visual studio·everything
AI题库4 小时前
NopCommerce 4.9.3开发实战 1.2 开发环境搭建指南(.NET 9+ & Visual Studio 2022)
ide·microsoft·.net·visual studio
互联网江湖4 小时前
Agent“黑灰产”时代:快手关直播,钉钉“拔电”?
人工智能·microsoft
枫叶丹44 小时前
【Qt开发】Qt事件(三)-> QMouseEvent 鼠标事件
c语言·开发语言·c++·qt·microsoft·计算机外设
TheSumSt4 小时前
Python丨课程笔记Part5:更多进阶部分
笔记·python·microsoft
个案命题20 小时前
鸿蒙ArkUI状态管理新宠:@Local装饰器全方位解析与实战
microsoft·华为·harmonyos
洛水如云1 天前
电脑数据备份实用极简指南:内置工具 + 专业软件高效上手
windows·microsoft·电脑