[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$f4j3v7vC1CpVrFOqLmPJEZH_MO9yHjWeGudn9l8amwSY":3},{"item":4,"related":48},{"id":5,"type":6,"title":7,"slug":8,"summary":9,"body":10,"coverUrl":11,"productScreenshots":12,"productLinks":13,"authorName":14,"authorUrl":15,"authorSubject":16,"category":17,"tags":22,"sourceLabel":39,"sourceName":40,"sourceUrl":40,"status":41,"seoTitle":40,"seoDescription":40,"canonicalUrl":40,"isFeatured":42,"sno":43,"sortOrder":44,"publishedAt":45,"updatedAt":46,"createdAt":47},"9c77fbc0-6422-4ae3-bf41-9a592e3a53e1","article","A2UI：AI Agent 为什么不应该只返回一段文字","a2ui","A2UI 把 Agent 的界面意图与客户端的组件实现分开：模型选择要展示的卡片、表单或动作，宿主应用负责白名单、权限、渲染和安全。本文用订票场景讲清 A2UI 的四层结构、跨端复用、流式更新与落地护栏。","## 从文字到界面：Agent 为什么需要一层 UI 协议\n\n聊天机器人返回一段文字，用户还要自己判断下一步做什么；但在真实产品里，用户往往需要的是一组可以直接操作的选项：选择日期、确认金额、填写地址、比较方案，或者继续编辑一份表单。\n\n这就是 A2UI（Agent-to-User Interface）试图解决的问题。它不是让模型随意生成 HTML，也不是把整个前端交给模型，而是让 Agent 用一种声明式格式表达“我想给用户展示什么”，再由宿主应用用自己已有的组件库把它渲染出来。\n\n![A2UI 项目概念图](https:\u002F\u002Fopengraph.githubassets.com\u002F1\u002Fgoogle\u002FA2UI)\n\nGoogle 在 2026 年发布的 A2UI 0.9，强调了一个很实用的分工：Agent 负责理解意图和选择界面结构，应用负责组件实现、样式、权限与交互安全。A2UI 可以通过 MCP、WebSocket、REST、A2A 等不同传输方式工作，但它本身更像“界面声明层”，而不是又一个网络传输协议。可以先看[官方介绍](https:\u002F\u002Fdevelopers.googleblog.com\u002Fen\u002Fa2ui-v0-9-generative-ui\u002F)，再把它放回自己的前端架构里理解。\n\n## 为什么纯文本不够用了\n\n假设用户说：“帮我订下周去上海的高铁，最好下午出发。”纯文本 Agent 可能返回一串车次。用户还需要复制车次、确认时间、选择座位，再回到对话里输入“就这个”。\n\n如果 Agent 能返回一个经过审核的车次卡片，卡片里有“出发时间”“到达时间”“余票状态”和“确认”按钮，交互就从“读一段答案”变成了“完成一个任务”。\n\n但直接让模型生成 HTML 或 JavaScript 有三个问题：\n\n- 模型可能生成宿主应用没有实现的组件。\n- 任意脚本会扩大 XSS、数据外传和权限越界风险。\n- Web、Flutter、原生移动端各自有不同的组件体系，HTML 很难直接复用。\n\nA2UI 的关键取舍是：只允许 Agent 从一个组件目录里选择组件，并用数据绑定和动作描述它们如何组合。模型表达的是 UI 意图，最终执行仍由客户端掌控。\n\n## A2UI 的四层结构\n\n可以把一次 A2UI 响应拆成四层：\n\n1. **Agent 层**：理解用户问题，决定需要展示卡片、表单还是列表。\n2. **Schema 层**：用版本化的结构描述组件树、数据和动作。\n3. **Catalog 层**：规定当前应用允许使用哪些组件，以及每个组件需要什么字段。\n4. **Renderer 层**：把声明转换成 React、Lit、Angular、Flutter 或其他宿主框架的真实组件。\n\n这四层让“生成内容”和“执行内容”分开。Agent 可以说“需要一个日期选择器”，但不能凭空执行一个未登记的浏览器 API。\n\n```mermaid\nflowchart LR\n    U[\"用户意图\"] --> A[\"Agent 理解任务\"]\n    A --> S[\"生成版本化 UI 声明\"]\n    S --> V{\"通过 Schema 与 Catalog 校验?\"}\n    V -->|否| F[\"降级为文本或修复\"]\n    V -->|是| R[\"宿主 Renderer 渲染\"]\n    R --> I[\"用户操作组件\"]\n    I --> E[\"动作回传 Agent 或业务 API\"]\n```\n\n## 一个具体例子：订票卡片怎么生成\n\nAgent 不需要返回完整 HTML，可以只表达类似下面的意图：\n\n```json\n{\n  \"surface\": \"train_options\",\n  \"components\": [\n    {\n      \"type\": \"option_card\",\n      \"data\": {\n        \"departure\": \"2026-08-12 15:20\",\n        \"arrival\": \"2026-08-12 19:48\",\n        \"price\": 553,\n        \"available\": true\n      },\n      \"actions\": [\"select_train\"]\n    }\n  ]\n}\n```\n\n真正的客户端还要做几件事：检查 `option_card` 是否在白名单里，验证价格和车次数据来自可信工具，确认 `select_train` 是否需要登录或二次确认，然后才把它映射成产品自己的卡片组件。\n\n这也是 A2UI 与“模型直接写前端代码”的根本区别：模型输出的是受限的数据，不是可以立即执行的程序。\n\n## 为什么组件目录比“万能组件”更重要\n\n组件目录不是简单的 UI 列表，它实际上是 Agent 的能力边界。\n\n一个金融应用可以只开放余额卡片、转账表单和收款人选择器；一个客服系统可以开放订单时间线、退款原因选项和人工转接按钮。不同用户、设备和权限，还可以使用不同的目录。\n\n这样做有三个好处：\n\n- **安全**：Agent 不能调用目录之外的组件和动作。\n- **一致**：生成式交互仍然遵守产品的设计系统。\n- **可演进**：升级客户端组件时，不必要求 Agent 学会新的 HTML 细节。\n\n但组件目录也会带来维护成本。每新增一个组件，都要补齐 schema、校验规则、渲染器、无障碍语义和失败回退。如果目录太小，Agent 只能输出僵硬的卡片；如果目录太大，模型更容易选错或生成难以测试的组合。\n\n## 声明的不只是组件，还有数据和动作\n\n把 A2UI 简化成“模型返回一棵组件树”还不够。一个真正能工作的界面声明，至少要回答三件事：显示什么、数据从哪里来、用户操作后发生什么。\n\n| 部分 | 解决的问题 | 典型约束 |\n| --- | --- | --- |\n| Component | 画出卡片、表单、列表还是进度状态 | 必须存在于 Catalog |\n| Data | 给组件填充价格、时间、状态和选项 | 字段类型、来源和权限可验证 |\n| Action | 用户点击后向谁发送什么事件 | 动作白名单、参数校验、确认级别 |\n\n例如“确认订票”不应该只是一个叫 `confirm` 的字符串。客户端至少要检查车次 ID 是否来自当前查询结果、价格是否仍然有效、用户是否登录，以及这个动作是否需要二次确认。A2UI 负责表达动作意图，但最终的业务授权仍然应该发生在服务端。\n\n还要区分两类数据：**Agent 生成的数据**和**工具返回的事实数据**。前者可以是标题、解释和排序建议；后者则可能是余额、库存和订单状态。后者不能因为模型把它写进 JSON 就自动变成可信事实，最好携带来源标识和过期时间，由客户端或服务端再次验证。\n\n## A2UI 与三种相邻方案有什么区别\n\n**直接生成 HTML \u002F JavaScript**：自由度最高，但安全边界最差。模型生成的代码需要经过沙箱、静态检查和运行时隔离，复杂度很快超过“做一个界面”的收益。\n\n**固定 JSON Schema**：比 HTML 安全，也容易解析，但如果 schema 只描述数据、不描述组件语义，前端仍要为每一种业务自定义协议。A2UI 更强调组件目录、版本协商和跨端渲染。\n\n**MCP Apps 一类的工具 UI**：可以让工具返回一个交互式资源，适合把工具自己的小界面带进宿主。A2UI 更像一层面向 Agent 的通用界面声明，适合由宿主统一控制组件和设计系统。二者可以组合，并不是非此即彼。\n\n实际选型可以这样判断：如果 UI 主要属于某个工具，优先考虑工具资源；如果 UI 要跨多个 Agent、多个设备，并且必须服从宿主设计系统，A2UI 的抽象更合适。\n\n## 流式渲染与跨端复用\n\nA2UI 0.9 的一个重要方向是流式更新：客户端不一定要等 Agent 生成完整结果后才渲染，可以先显示骨架，再逐步补齐数据或组件。对于需要搜索、比价和多轮工具调用的任务，这会明显降低“什么都没发生”的等待感。\n\n跨端复用则依赖各端的 Renderer。Web 端可以映射到 React，移动端可以映射到 Flutter，二者共享的是组件语义和数据，而不是具体的 DOM。前提是各端的组件目录足够一致，否则同一个“日期选择器”可能在不同设备上出现不同能力。\n\n流式界面还要处理“半成品状态”。例如 Agent 先生成了一个空的结果卡片，后来工具调用失败；客户端应该把卡片标记为“暂时无法获取”，而不是保留一个看起来像最终结果的旧状态。一个成熟的声明格式通常需要区分 `loading`、`partial`、`complete` 和 `error`，并携带可恢复动作，例如“重试查询”或“改用文字回答”。\n\n这会改变前端测试方式。过去测试的是“点击按钮后组件是否出现”，现在还要测试：声明版本不兼容时是否降级、数据缺失时是否显示错误、Agent 重复发送同一事件时是否幂等、用户在流式更新中点击时状态是否一致。\n\n## 无障碍与设计系统不能交给模型猜\n\n生成式 UI 很容易只关注“能不能显示”，忽略“能不能被所有人操作”。组件目录应该直接绑定无障碍语义：按钮的可访问名称、表单字段的标签、错误提示与输入框的关联、键盘焦点顺序和屏幕阅读器状态。\n\n同样，颜色、间距、字体和交互反馈最好来自设计系统 token，而不是让模型自由生成。Agent 可以选择“警告状态”或“强调操作”，但不应该自己决定用什么十六进制颜色。这样既保持视觉一致，也避免模型在不同回答中生成一套套互相冲突的 UI。\n\n## 一条更稳的落地路线\n\n不要一开始就让 Agent 生成任意页面。可以按四步推进：\n\n1. 选一个闭环任务，例如筛选商品或填写报销单。\n2. 只开放 5–8 个组件，每个组件配 schema、示例和失败状态。\n3. 先让 Agent 只生成“组件选择 + 数据填充”，动作由固定代码处理。\n4. 用真实任务记录无效组件率、校验失败率、用户完成率和人工接管率，再扩大目录。\n\n这样可以把问题拆开：如果用户没完成任务，到底是 Agent 选错组件、数据不可信、动作失败，还是流程本身设计得太长。没有这些指标，生成式 UI 很容易变成一组看起来漂亮但无法完成业务的卡片。\n\n## 适合什么时候使用\n\nA2UI 更适合这些场景：\n\n- Agent 需要引导用户完成多步骤任务。\n- 同一套 Agent 要服务 Web、移动端和桌面端。\n- 产品已经有成熟设计系统，希望 AI 复用现有组件。\n- 需要对 Agent 能展示和执行的 UI 做权限控制。\n\n如果只是问答、摘要或一次性文本生成，直接返回 Markdown 通常更简单。不要为了“看起来像 AI”而把每个回答都包装成动态界面。\n\n## 落地时的四个护栏\n\n第一，**所有组件和动作都采用白名单**。未知类型直接拒绝，不要尝试“宽松解析”。\n\n第二，**把数据权限放在工具和服务端**。UI 声明里的 `price`、`balance`、`status` 只能作为展示数据，不能成为业务决策的最终依据。\n\n第三，**动作需要幂等和确认机制**。支付、下单、删除、发消息等副作用操作，不应因为 Agent 重试或用户重复点击而执行两次。\n\n第四，**始终保留文本回退**。客户端版本过旧、schema 不兼容、数据校验失败时，用户至少应该得到一段清楚的文字说明，而不是空白区域。\n\n## 结语：Agent 的下一层抽象是“意图”，不是“代码”\n\nA2UI 的价值不在于让模型生成更漂亮的卡片，而在于重新划分边界：Agent 负责决定“用户此刻需要什么交互”，客户端负责决定“这个交互以什么安全、可访问、可维护的方式呈现”。\n\n如果你准备尝试它，建议先选一个窄流程，例如“筛选商品”或“填写报销单”，建立小型组件目录，给每个动作加校验和审计，再逐步扩大范围。生成式 UI 的可靠性，最终取决于目录和边界设计，而不只是模型聪不聪明。\n\n**动手前的检查清单：**\n\n- 是否能把每个可生成组件写成明确 schema？\n- 是否有未知组件、未知动作的拒绝路径？\n- 是否支持客户端版本协商和文本降级？\n- 是否对副作用动作做了确认、幂等和审计？\n- 是否用真实用户任务而不是 Demo 截图评估体验？\n\n## 一手资料\n\n- [A2UI 0.9 官方发布说明](https:\u002F\u002Fdevelopers.googleblog.com\u002Fen\u002Fa2ui-v0-9-generative-ui\u002F)\n- [A2UI 项目主页](https:\u002F\u002Fa2ui.org\u002F)\n- [Google 对 Agent-driven UI 的介绍](https:\u002F\u002Fdevelopers.googleblog.com\u002Fintroducing-a2ui-an-open-project-for-agent-driven-interfaces\u002F)","\u002Fuploads\u002F2026-08-05\u002Fb6291296-7a17-44a7-b288-83ebc0072068.jpg",[],[],"Foundit AI","https:\u002F\u002Ffoundit.cn","f39339b1-aaa6-4e86-b0c2-a6e6a21113b5",{"id":18,"name":19,"slug":20,"description":21},"6179d3b6-dc34-4483-9ded-3cd9f1b37a47","科普","abbreviation","介绍各领域新兴概念",[23,27,31,35],{"id":24,"name":25,"slug":26},"88d2bc27-0e0f-468a-b907-2991cb97b87b","人工智能","ai",{"id":28,"name":29,"slug":30},"7c76bfc2-f80f-4ee0-a95d-27bd8708b434","技术","slug",{"id":32,"name":33,"slug":34},"a202d639-99a6-488a-a712-4d4c6ffd7e15","开发","dev",{"id":36,"name":37,"slug":38},"4c2bbea6-eab7-40a8-8447-1de478ff7749","分析","analyse","资料来源",null,"published",false,67,0,"2026-08-02T00:00:00.000Z","2026-08-05T03:13:23.142Z","2026-08-05T02:12:50.323Z",[49,59,68],{"id":50,"type":6,"title":51,"slug":52,"summary":53,"coverUrl":54,"authorName":55,"sno":56,"publishedAt":57,"createdAt":58},"9ccde95c-d754-4808-91f7-488f392e3eeb","你的品牌在AI眼里到底存不存在？这套系统说了算","automated-geo-monitoring-system","靠手动抽查来验证GEO效果，本质上是在跟概率玩游戏。赢一次，不代表能一直赢。","\u002Fuploads\u002F2026-08-07\u002Fdf111c0d-f14a-4b2a-8347-141e96b71654.jpg","Foundit",1,"2026-08-07T00:00:00.000Z","2026-08-07T04:31:30.843Z",{"id":60,"type":6,"title":61,"slug":62,"summary":63,"coverUrl":64,"authorName":14,"sno":65,"publishedAt":66,"createdAt":67},"544fc658-c911-4de6-93b0-d2520087119a","MCP：AI 的「USB-C」时刻","mcp-ai-usb-c-moment","以前每个 AI 应用都要为 GitHub、数据库、日历各写一套私有连接器，这是 M×N 的集成噩梦，直到 MCP 的出现","https:\u002F\u002Foxqtewbrpuiouqqjrvdv.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Fpublic-media\u002F2026-07-19\u002Fc3c06d99-a0ac-40ac-a283-7e77aabb4c4c.jpg",46,"2026-07-20T00:00:00.000Z","2026-07-19T16:13:40.316Z",{"id":69,"type":6,"title":70,"slug":71,"summary":72,"coverUrl":73,"authorName":55,"sno":74,"publishedAt":75,"createdAt":76},"f00e5274-8e0b-40fe-af3b-b6a8d0183b9c","一张贴纸就能骗过视觉 AI？对抗样本不是魔法","adversarial-examples-fool-visual-ai","人眼仍能认出的物体，经过精心设计的微小扰动或标记后，机器却可能改变判断。这类输入称为对抗样本。本文解释攻击者如何利用模型的决策边界，现实攻击为何比实验更难，以及为什么目前不存在一劳永逸的防御。","\u002Fuploads\u002F2026-09-08\u002F151f887b-c5f1-4647-b369-cdda8a1e1b4f.jpg",50,"2026-09-03T00:00:00.000Z","2026-08-14T03:06:09.765Z"]