一、A2UI:让 AI 学会“描述”界面

1.1 背景:AI 很聪明,但交互方式还很原始

在大语言模型能力不断突破的今天,AI Agent 早已不只是简单的问答机器——它具备了规划、记忆、工具调用等复杂能力。然而,Agent 与用户之间的交互方式,却始终停留在最基础的聊天框模式。

设想一个场景:当你告诉 AI 助手“帮我订一张周五下午去上海的机票”,它本可以立刻为你生成一个包含日期选择器、航班列表和选座按钮的完整表单,让你一键完成预订。但在传统模式下,AI 只能输出一段冗长的文字:“请选择出发日期、选择航班、填写乘客信息……”用户疲于应付,AI 的智能算力也被浪费在这来来回回的文字拉锯之中。

问题的根源在于:Agent“知道”要什么样的界面,但没有办法直接生成并呈现给用户——它只能输出文本,把“搭界面”这件事推给用户自己去完成。

1.2 目标与核心设计

正是在这一背景下,Google 于 2025 年 12 月正式开源了 A2UI(Agent-to-User Interface) 协议。A2UI 的核心目标是规范 AI 如何生成视觉响应,使其从单纯的文本输出转向能够直接生成表单、按钮等用户界面元素,从而全面提升用户体验。

A2UI 采用了一种全新的设计思路:Agent 不再输出可执行的前端代码,而是输出一套结构化的 JSON 数据,用于描述“需要什么界面组件”“组件有什么属性”“组件之间的层级关系”。换言之,Agent 做的是“描述”,而非“编程”——它向客户端声明“这里需要一个输入框、一个按钮”,至于这个输入框在 iOS、Android 还是 Web 上长什么样,由各平台的渲染器自行决定。

这一设计带来了 A2UI 的四大核心特性:

  • 安全性:Agent 输出的是数据而非代码,客户端仅从本地“组件白名单”(Component Catalog)中取用和渲染被授权的组件,从根本上杜绝了代码注入和 XSS 攻击等风险。
  • 跨平台:同一份 JSON 蓝图可以发送给 Web(React/Angular)、iOS(SwiftUI)或 Android(Flutter),UI 的外观完全由客户端的原生样式决定,保证视觉一致性。
  • 流式增量更新:A2UI 支持流式 JSONL 响应,可以通过 updateComponentsupdateDataModel 等操作实现组件的增量添加与数据模型的按路径更新,让 Agent 可以先展示框架再填充数据,或根据用户填写的表单动态生成下一步,无需刷新整个界面。
  • 标准化组件目录:A2UI v0.9 定义了一套标准的基础组件目录(Basic Catalog),包含 TextColumnButtonCardRowList 等结构化原语。

A2UI 不是孤立存在的规范。它的 Web 渲染器(React、Lit、Angular)共享同一套底层核心库 @a2ui/web_core,该库提供了消息处理器、状态管理和数据绑定逻辑,确保跨框架的协议处理一致性。同时,A2UI 可以与 A2A 协议、SSE、WebSocket 等多种传输层结合使用。

1.3 解决的核心问题

A2UI 以一种极为优雅的方式解决了 AI 动态生成界面的三难困境:如何在保证安全的前提下,实现界面描述的跨平台可移植,同时又能让客户端保持对视觉风格的完全控制?

传统做法中,让 Agent 生成 HTML/JSX 代码虽然可行,但存在明显的安全隐患(恶意脚本注入、样式破坏等)和兼容性问题。A2UI 的路线是:让 Agent 只输出“声明式描述”(数据),真正画 UI 的责任回到客户端;客户端只渲染你允许的组件,Agent 想象力再丰富也只能在你划的框里跳舞。

1.4 应用前景

A2UI 的出现标志着一种“模型即应用”的新范式:未来的应用可能只有一个入口,当你想订票时,AI 现场为你组装一个订票界面;当你想分析数据时,AI 现场为你组装一个仪表盘。

典型应用场景包括:

  • 智能客服与在线服务:在用户提出复杂需求时,AI 动态生成表单、预约界面或服务卡片,大幅减少对话轮次,提升转化率。
  • 企业数据分析与 BI:用户提出“为什么本月销量下滑”,AI 动态生成包含趋势折线图、地区对比柱状图和关键结论的数据故事界面。
  • 对话式应用的动态交互:任何需要多轮信息收集的场景——报修、购票、问卷填写——都能被 A2UI 大幅简化。
  • 多代理协作界面:A2UI 支持复杂的多代理架构,多个 Agent 可以协作生成和展示统一的动态界面。

从产业链角度看,Google Gemini 应用已率先展示 A2UI 的“动态视图”功能。A2UI 已演进到 0.10.0 版本,生态包括 React、Lit、Angular、Flutter 等多个框架的官方渲染器。

二、AGenUI:让 A2UI 在手机端跑起来的原生框架

如果说 A2UI 是一份“界面的设计蓝图规范”,那么 AGenUI 就是让这份蓝图在移动端真正落地的“施工队”。

2.1 背景与目标

2026 年 5 月,高德地图与阿里千问 C 端应用团队联合发布了 AGenUI——这是行业首个覆盖 iOS、Android、HarmonyOS 三端的端云一体原生 A2UI 开源框架。AGenUI 基于 Google A2UI 最新开放协议构建,进一步补齐了“这些描述如何在手机上跑起来”的端侧原生渲染能力。

2.2 核心架构与技术特性

AGenUI 采用端云一体架构:云侧通过 Agent Skill 生成 A2UI JSON,降低大模型的 Token 消耗和输出不确定性;端侧依托跨平台 C++ Core 统一处理协议解析、状态管理与布局计算,在 iOS、Android 和鸿蒙三端直接渲染为原生组件。

其核心采用Streaming-first 流式架构,支持组件到达即刻挂载,实现“边生成边呈现”;配合最小化节点差分更新与独立线程异步渲染,高频增量更新也不会卡主线程。对开发者而言,AGenUI 内置 22 个基础组件和 45 项 CSS 属性,支持组件、功能调用及主题的三维定制。其 Theme 系统支持 Design Token,模型只需输出语义描述,端侧即可自动映射为符合品牌规范的具体样式。

鸿蒙版 AGenUI 的表现尤为亮眼:深度适配鸿蒙 1+8+N 全场景分布式架构,无需为不同设备单独适配调试,直连鸿蒙系统级渲染、动效及分布式核心能力,与小艺、鸿蒙意图框架天然协同。性能方面,鸿蒙版 AGenUI 相较 iOS、Android 端对应版本,渲染性能提升 20%,内存占用降低 18%。

2.3 解决的问题

传统 AI 智能交互多依赖纯文本对话或简易网页界面,即使是本地原生应用中要展示 AI 生成的界面,也需要为每个平台独立开发 UI 代码,开发成本高昂、体验难以一致。AGenUI 正是为解决这一痛点而生:开发者接入 SDK 后,即可将 Agent 的输出直接渲染为可交互的原生卡片,无需为不同平台分别写 UI 代码。一套通用界面协议即可无缝适配手机、平板、车机、智慧屏等多种终端设备。

2.4 应用前景与生态

AGenUI 的开源发布受到行业广泛关注。高德与千问联手,本质上是“复杂场景”与“AI 交互”的结合——高德长期深耕地图导航、本地生活等真实世界的复杂服务,千问则在大规模 AI 应用入口与 Agent 交互上持续投入。

典型应用场景与前景:

  • 车机智能助手:乘客在车内说出“找沿途的充电桩”,AGenUI 动态生成包含地图标注、充电桩状态和导航按钮的卡片,在车机屏幕上原生渲染。
  • 多设备协同服务:用户在手机上规划行程,界面自动流转到车载大屏;一份协议适配鸿蒙手机、平板、车机、智慧屏等多终端,无需重复开发。
  • 本地生活服务:在地图应用中搜索美食时,AI 动态生成店铺卡片、评分展示、优惠券领取按钮和导航入口。
  • 企业级跨平台应用:企业内部工具可通过 AGenUI 实现一套描述、多端一致的原身体验,大幅降低移动端开发成本。

AGenUI 正在推动生成式 UI 成为鸿蒙生态的系统级标准能力,而 A2UI 与 AGenUI 的共同愿景,是推动 AI 应用从“文本式交互”走向“生成式 UI 交互”。

三、最小化实现:两个可运行的 Demo

理解了 A2UI 和 AGenUI 的理论框架之后,最直观的理解方式莫过于亲手运行一个真实的 Demo。以下提供两个最小化实现的 Demo,分别对应纯前端模拟和后端驱动两种模式,均可直接运行并快速体验 A2UI 协议的完整工作流程。

两个 Demo 均严格遵循 A2UI v0.9 协议规范。v0.9 使用三种核心消息类型来构建界面:

  • createSurface:初始化一个 UI 表面(Surface),并设置组件目录。
  • updateComponents:在表面中添加或更新组件。
  • updateDataModel:按指定路径更新数据模型。

3.1 Demo 1:纯前端模拟版(单 HTML 文件,符合 v0.9 协议)

该 Demo 将所有代码写入一个 HTML 文件,无需任何构建工具或后端服务。它使用自研的轻量级消息处理器和渲染器,完整解析 A2UI v0.9 标准消息,并将组件树动态渲染为真实界面。消息内容是硬编码的示例 JSON,模拟 Agent 的输出。适合在没有后端环境的电脑上快速理解 A2UI 的核心机制。

完整 HTML 代码

创建一个文件 demo1.html,复制以下内容:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>A2UI Demo 1:纯前端模拟版(符合 v0.9 协议)</title>
    <script src="https://cdn.jsdelivr.net/npm/react@18.2.0/umd/react.development.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/react-dom@18.2.0/umd/react-dom.development.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/@babel/standalone/babel.min.js"></script>
    <style>
        body { font-family: system-ui; background: #f0f2f5; margin: 0; padding: 20px; }
        .container { max-width: 800px; margin: 0 auto; background: white; border-radius: 24px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); }
        .header { background: linear-gradient(135deg, #1e3c72, #2a5298); color: white; padding: 20px; text-align: center; }
        .ui-area, .log-area { padding: 24px; }
        .log-area { background: #f9fafb; border-top: 1px solid #e5e7eb; border-radius: 0 0 24px 24px; }
        button { background: #2a5298; color: white; border: none; padding: 10px 20px; border-radius: 40px; font-size: 1rem; cursor: pointer; margin: 8px 8px 8px 0; transition: 0.2s; }
        button:hover { background: #1e3c72; }
        .card { background: #f9fafb; border-radius: 16px; padding: 16px; margin: 16px 0; border: 1px solid #e5e7eb; }
        .text-large { font-size: 1.5rem; font-weight: bold; }
        .text-small { font-size: 0.875rem; color: #6b7280; }
        .row { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 16px; }
        .log-entry { font-family: monospace; font-size: 0.875rem; padding: 4px 0; border-bottom: 1px solid #e5e7eb; }
    </style>
</head>
<body>
<div id="root"></div>

<script type="text/babel">
    // ============================================================
    // 1. 符合 A2UI v0.9 协议的消息序列(完全遵循官方格式)
    // ============================================================
    const catalogId = "https://a2ui.org/specification/v0_9/basic_catalog.json";
    
    const a2uiMessageSequence = [
        {
            version: "v0.9",
            createSurface: {
                surfaceId: "main",
                catalogId: catalogId
            }
        },
        {
            version: "v0.9",
            updateComponents: {
                surfaceId: "main",
                components: [
                    { id: "root", component: "Column", children: ["title", "desc", "menuGrid", "actionHint"] },
                    { id: "title", component: "Text", text: { literalString: "🍽️ 智能点餐助手(v0.9 协议)" }, usageHint: "h2" },
                    { id: "desc", component: "Text", text: { literalString: "点击按钮,界面与数据联动。这是完全符合 A2UI v0.9 规范的消息驱动 UI。" } },
                    { id: "menuGrid", component: "Column", children: ["pizzaBtn", "burgerBtn", "saladBtn"] },
                    { id: "pizzaBtn", component: "Button", child: "pizzaText", action: { name: "select_pizza" } },
                    { id: "pizzaText", component: "Text", text: { literalString: "🍕 经典意式披萨 - ¥48" } },
                    { id: "burgerBtn", component: "Button", child: "burgerText", action: { name: "select_burger" } },
                    { id: "burgerText", component: "Text", text: { literalString: "🍔 安格斯牛肉汉堡 - ¥42" } },
                    { id: "saladBtn", component: "Button", child: "saladText", action: { name: "select_salad" } },
                    { id: "saladText", component: "Text", text: { literalString: "🥗 凯撒沙拉 - ¥28" } },
                    { id: "actionHint", component: "Text", text: { literalString: "✅ 验证:每次点击都会记录到下方日志区,证明消息驱动成功。", usageHint: "caption" } }
                ]
            }
        },
        {
            version: "v0.9",
            updateDataModel: {
                surfaceId: "main",
                path: "/",
                value: {}
            }
        }
    ];

    // ============================================================
    // 2. 简单的 A2UI 消息处理器 + 组件渲染器
    // ============================================================
    class SimpleA2UIProcessor {
        constructor() {
            this.surfaces = new Map(); // surfaceId -> { components: Map(id->comp), rootId, dataModel }
        }

        processMessages(messages) {
            for (const msg of messages) {
                if (msg.createSurface) {
                    const { surfaceId, catalogId } = msg.createSurface;
                    this.surfaces.set(surfaceId, {
                        components: new Map(),
                        rootId: null,
                        dataModel: {}
                    });
                }
                if (msg.updateComponents) {
                    const { surfaceId, components } = msg.updateComponents;
                    const surface = this.surfaces.get(surfaceId);
                    if (surface) {
                        for (const comp of components) {
                            surface.components.set(comp.id, comp);
                            if (comp.component === "Column" || comp.component === "Row" || comp.component === "Card") {
                                if (!surface.rootId && comp.id === "root") surface.rootId = comp.id;
                            }
                        }
                    }
                }
                if (msg.updateDataModel) {
                    const { surfaceId, path, value } = msg.updateDataModel;
                    const surface = this.surfaces.get(surfaceId);
                    if (surface) {
                        // 简单实现:整个数据模型替换(实际应按 path 更新)
                        surface.dataModel = value;
                    }
                }
            }
        }

        getSurface(surfaceId) {
            return this.surfaces.get(surfaceId);
        }
    }

    // 组件渲染器(将 processor 中的组件树转换为 React 元素)
    const A2UIComponentRenderer = ({ surface, processor, onAction }) => {
        if (!surface || !surface.rootId) return null;
        
        const renderComponent = (compId) => {
            const comp = surface.components.get(compId);
            if (!comp) return null;
            
            const childrenIds = comp.children || (comp.child ? [comp.child] : []);
            
            switch (comp.component) {
                case "Text":
                    const text = comp.text?.literalString || "";
                    const hint = comp.usageHint;
                    let className = "";
                    if (hint === "h2") className = "text-large";
                    if (hint === "caption") className = "text-small";
                    return <p key={comp.id} className={className}>{text}</p>;
                case "Button":
                    const labelComp = comp.child ? surface.components.get(comp.child) : null;
                    const label = labelComp?.text?.literalString || "Button";
                    return (
                        <button key={comp.id} onClick={() => onAction(comp.action?.name)}>
                            {label}
                        </button>
                    );
                case "Column":
                    return (
                        <div key={comp.id}>
                            {childrenIds.map(childId => renderComponent(childId))}
                        </div>
                    );
                case "Row":
                    return (
                        <div key={comp.id} className="row">
                            {childrenIds.map(childId => renderComponent(childId))}
                        </div>
                    );
                case "Card":
                    return (
                        <div key={comp.id} className="card">
                            {childrenIds.map(childId => renderComponent(childId))}
                        </div>
                    );
                default:
                    return null;
            }
        };
        
        return renderComponent(surface.rootId);
    };

    // ============================================================
    // 3. React App 组件
    // ============================================================
    const App = () => {
        const [processor] = React.useState(() => {
            const p = new SimpleA2UIProcessor();
            p.processMessages(a2uiMessageSequence);
            return p;
        });
        const [surface, setSurface] = React.useState(() => processor.getSurface("main"));
        const [logs, setLogs] = React.useState([]);

        const refreshSurface = () => setSurface(processor.getSurface("main"));

        const handleAction = (actionName) => {
            let message = '';
            switch (actionName) {
                case 'select_pizza': message = '您选择了披萨'; break;
                case 'select_burger': message = '您选择了汉堡'; break;
                case 'select_salad': message = '您选择了沙拉'; break;
                default: message = `触发了动作: ${actionName}`;
            }
            alert(message);
            const timestamp = new Date().toLocaleTimeString();
            setLogs(prev => [{ time: timestamp, action: actionName }, ...prev]);
            refreshSurface();
        };

        return (
            <div className="container">
                <div className="header">
                    <h1>🧪 Demo 1: 纯前端模拟版(符合 A2UI v0.9 协议)</h1>
                    <p>消息格式:createSurface + updateComponents + updateDataModel | 自研渲染器</p>
                </div>
                <div className="ui-area">
                    {surface ? (
                        <A2UIComponentRenderer surface={surface} processor={processor} onAction={handleAction} />
                    ) : (
                        <p>加载界面中...</p>
                    )}
                </div>
                <div className="log-area">
                    <h3>📋 交互记录(验证成功标志)</h3>
                    {logs.length === 0 ? (
                        <p className="text-small">还没有点击记录,点击上方按钮试试看~</p>
                    ) : (
                        logs.map((log, idx) => (
                            <div key={idx} className="log-entry">
                                [{log.time}] 触发了 action: {log.action}
                            </div>
                        ))
                    )}
                    <p className="text-small" style={{ marginTop: '16px' }}>
                        ✅ 验证方法:点击任意按钮 → 弹出提示框 + 下方出现记录 → 证明 A2UI 消息被正确解析,UI 与交互完全分离。
                    </p>
                    <p className="text-small">
                        📐 协议符合性:消息使用 v0.9<code>createSurface</code><code>updateComponents</code><code>updateDataModel</code>,组件 id 唯一,action 命名规范。
                    </p>
                </div>
            </div>
        );
    };

    ReactDOM.createRoot(document.getElementById("root")).render(<App />);
</script>
</body>
</html>
验证逻辑讲解

要确认这个 Demo 成功体现了 A2UI 协议的核心能力,可以按以下步骤验证:

  1. 界面渲染验证:打开页面后,应看到标题、描述、三个点餐按钮以及底部提示文字。所有界面元素都来自 a2uiMessageSequence 这个 JSON 对象,而非硬编码在 HTML 中。你可以尝试修改 a2uiMessageSequence 中某个按钮的文字(比如把 “🍕 经典意式披萨 - ¥48” 改成任意其他文字),保存并刷新页面,按钮文字会立即变化。这证明了“界面由 JSON 描述,动态渲染”

  2. 交互与数据分离验证:点击任意按钮,会弹出一个提示框(如“您选择了披萨”),同时在页面下方的“交互记录”区域会新增一条记录,包含触发时间和 action 名称。这证明了按钮的 action.name 被正确传递给回调函数,UI 组件本身不包含业务逻辑,符合 A2UI 的“动作与行为分离”原则。

  3. 协议格式验证:打开浏览器开发者工具,查看控制台无报错。消息序列中包含标准的 version: "v0.9"createSurfaceupdateComponentsupdateDataModel 等字段,组件使用 id 引用和 children/child 建立层级关系。这些结构与 A2UI 官方规范 完全一致。

  4. 扩展性验证:你可以尝试向 components 数组中增加一个新的按钮,例如:

    { id: "coffeeBtn", component: "Button", child: "coffeeText", action: { name: "select_coffee" } },
    { id: "coffeeText", component: "Text", text: { literalString: "☕ 美式咖啡 - ¥18" } }
    

    并记得将其加入某个 children 数组(如 menuGridchildren)。刷新页面后,新按钮会自动出现,无需修改任何渲染逻辑。这证明了 A2UI 的可扩展性

通过以上验证,你可以确信这个 Demo 完整实现了 A2UI 协议的核心理念:界面由声明式 JSON 描述,客户端动态渲染,交互通过 action 回调与数据模型分离

3.2 Demo 2:后端驱动版(FastAPI + DeepSeek + React 工程)

这个版本将真正的 AI 智能体置于后端,前端通过 API 获取动态生成的 A2UI 消息,实现真实的对话驱动界面更新。Demo 2 需要 Node.js 环境和 Python 环境,适合希望深入开发真实 A2UI 应用的开发者。

后端环境搭建(FastAPI + DeepSeek)

后端目录结构:

a2ui-backend/
├── .env
├── requirements.txt
├── main.py
└── agent_logic.py

环境变量(.env):

DEEPSEEK_API_KEY=sk-your-deepseek-key
DEEPSEEK_BASE_URL=https://api.deepseek.com/v1
DEEPSEEK_MODEL=deepseek-chat
HOST=0.0.0.0
PORT=8000

依赖文件(requirements.txt):

fastapi==0.115.6
uvicorn[standard]==0.34.0
python-dotenv==1.0.1
openai==1.59.3
pydantic==2.10.4

后端主程序(main.py):

import os
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from typing import List, Dict, Any
from dotenv import load_dotenv
from agent_logic import get_agent_response

load_dotenv()
app = FastAPI()
app.add_middleware(CORSMiddleware, allow_origins=["*"])

class ChatRequest(BaseModel):
    session_id: str
    user_message: str

@app.post("/chat")
async def chat(req: ChatRequest):
    try:
        messages = get_agent_response(req.session_id, req.user_message)
        return messages
    except Exception as e:
        raise HTTPException(500, str(e))

if __name__ == "__main__":
    import uvicorn
    uvicorn.run("main:app", host=os.getenv("HOST", "0.0.0.0"), 
                port=int(os.getenv("PORT", "8000")), reload=True)

Agent 逻辑(agent_logic.py) —— 完整代码已在前文中提供,它实现了会话状态机、调用 DeepSeek 生成菜单和感谢语,并返回符合 A2UI v0.9 规范的消息序列。由于篇幅,这里不再重复粘贴,但可参考前文的完整实现。

前端项目搭建
npm create vite@latest a2ui-demo2 -- --template react
cd a2ui-demo2
npm install
npm install @a2ui/react @a2ui/web_core

前端主组件(src/App.jsx):

import { useState, useEffect, useCallback } from 'react';
import { MessageProcessor } from '@a2ui/web_core/v0_9';
import { A2uiSurface, basicCatalog } from '@a2ui/react/v0_9';

const SESSION_ID = crypto.randomUUID?.() || Date.now().toString();

function App() {
  const [surfaces, setSurfaces] = useState([]);
  const [processor] = useState(() => new MessageProcessor([basicCatalog]));

  useEffect(() => {
    const sync = () => setSurfaces(Array.from(processor.model.surfacesMap.values()));
    const createdSub = processor.onSurfaceCreated(sync);
    const deletedSub = processor.onSurfaceDeleted(sync);
    return () => { createdSub.unsubscribe(); deletedSub.unsubscribe(); };
  }, [processor]);

  const sendToBackend = useCallback(async (userMessage) => {
    try {
      const response = await fetch('http://localhost:8000/chat', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ session_id: SESSION_ID, user_message: userMessage })
      });
      const messages = await response.json();
      processor.processMessages(messages);
    } catch (err) {
      console.error("后端连接失败", err);
    }
  }, [processor]);

  useEffect(() => {
    sendToBackend("点餐");
  }, [sendToBackend]);

  const handleAction = useCallback((action) => {
    const actionMap = {
      'select_pizza': '披萨',
      'select_burger': '汉堡',
      'select_salad': '沙拉',
      'confirm_order': '确认下单',
      'cancel_order': '重新选择',
      'restart': '返回首页'
    };
    const userMsg = actionMap[action.name];
    if (userMsg) sendToBackend(userMsg);
  }, [sendToBackend]);

  return (
    <div className="max-w-2xl mx-auto p-4">
      <h1 className="text-2xl font-bold mb-4 text-center">🤖 Demo 2: 后端驱动版 (DeepSeek)</h1>
      <div className="bg-white rounded-lg shadow-md p-4">
        {surfaces.length === 0 && <div className="text-gray-500">等待 AI 生成界面...</div>}
        {surfaces.map(surface => (
          <A2uiSurface key={surface.id} surface={surface} onAction={handleAction} />
        ))}
      </div>
      <p className="text-sm text-gray-500 mt-4 text-center">
        💡 该版本连接真实后端,菜单和感谢语由 DeepSeek 动态生成。
      </p>
    </div>
  );
}

export default App;
运行 Demo 2
  1. 启动后端:cd a2ui-backend && python main.py
  2. 启动前端:cd a2ui-demo2 && npm run dev

访问前端地址,体验完整的 AI 驱动界面生成流程。后端返回的消息格式与 Demo 1 完全一致,同样严格遵循 A2UI v0.9 协议,但内容由 DeepSeek 动态生成,每次刷新菜单都可能不同,确认订单后会生成个性化感谢语。

3.3 两个 Demo 的对比总结

特性 Demo 1(纯前端模拟) Demo 2(后端驱动)
运行方式 单 HTML 文件,双击打开 需要 Node.js + Python 环境
消息来源 硬编码 JSON 序列 后端动态生成(调用 DeepSeek)
AI 动态性 无(固定菜单) 每次对话菜单不同,感谢语个性化
核心依赖 自研轻量处理器 官方 @a2ui/react + web_core
协议版本 v0.9 完整符合 v0.9 完整符合
适用场景 快速理解 A2UI 消息格式与渲染原理 真实 AI 对话应用开发

四、参考链接

A2UI 官方资源

  • 官网:https://a2ui.org/
  • GitHub 组织:https://github.com/a2ui
  • 协议规范 v0.9:https://a2ui.org/specification/v0_9/
  • Quickstart(快速入门):https://a2ui.org/quickstart/
  • Web 核心库:https://github.com/a2ui/a2ui-web-core
  • React 渲染器
    • npm:https://www.npmjs.com/package/@a2ui/react
    • GitHub:https://github.com/a2ui/a2ui-react

AGenUI

  • 官网:https://genui.amap.com/
  • GitHub 开源地址:https://github.com/AGenUI/AGenUI

其他参考

  • A2A 协议(Agent-to-Agent):https://a2a-protocol.org
  • Google A2UI 官方公告:https://developers.googleblog.com/a2ui
  • 阿里千问 + 高德 AGenUI 发布文章:https://www.amap.com/opening/news/agenui
  • 技术解读文章
    • 《A2UI 完整学习指南(从原理到代码)》
    • 《Google A2UI 项目深度解析》
    • 《领先 Google 官方发布!我们开源了首个 A2UI React 完整实现》
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐