<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>MCP Apps on AI博士 万戈</title>
        <link>https://www.yesmiracle.net/tags/mcp-apps/</link>
        <description>AI博士万戈的技术博客，聚焦 Agentic AI、AI Infra 与 Agent Security，分享 AI 基础设施与工程落地实践。</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>zh-cn</language>
        <managingEditor>admin@yesmiracle.net (万戈)</managingEditor>
        <webMaster>admin@yesmiracle.net (万戈)</webMaster>
        <lastBuildDate>Thu, 08 Oct 2026 14:20:00 +1000</lastBuildDate><atom:link href="https://www.yesmiracle.net/tags/mcp-apps/index.xml" rel="self" type="application/rss+xml" /><item>
        <title>GPT-6 的回答里开始长出按钮了：生成式 UI 拆解，以及 A2UI 想当成的那层「标准」！</title>
        <link>https://www.yesmiracle.net/post/20261008-generative-ui-a2ui-protocol-deep-dive/</link>
        <pubDate>Thu, 08 Oct 2026 14:20:00 +1000</pubDate>
        <author>admin@yesmiracle.net (万戈)</author>
        <guid>https://www.yesmiracle.net/post/20261008-generative-ui-a2ui-protocol-deep-dive/</guid>
        <description>&lt;img src="https://www.yesmiracle.net/post/20261008-generative-ui-a2ui-protocol-deep-dive/cover.svg" alt="Featured image of post GPT-6 的回答里开始长出按钮了：生成式 UI 拆解，以及 A2UI 想当成的那层「标准」！" /&gt;&lt;p&gt;10 月 7 日，OpenAI 开始给 ChatGPT 推 GPT-6，顺手带了一个叫 Intelligent UI 的功能。官方措辞很轻：回答不再只是文字，可以是图表、按钮、表单，甚至是当场生成的一个小工具。&lt;/p&gt;
&lt;p&gt;MacRumors 那篇实测里有个例子我印象很深——你问一辆七速自行车，它给你的不是先铺五百字，而是一张可以点的车示意图，点哪个部件、展开哪个部件的说明。有媒体还描述了一段做饭计划：一张食材图、一个「几个人吃」的控件、跟着人数自动重算的用量、一个复制购物清单的按钮、再加一份可勾选的备菜清单。&lt;/p&gt;
&lt;p&gt;这东西在消费端是第一次铺到十亿级用户的默认界面里，但它在工程圈不是新概念。它有个名字——生成式 UI（Generative UI）。所以我没急着写「GPT-6 发布」，而是把三样东西翻了一遍：OpenAI 的发布说明、Google 的 A2UI v0.9 规范（a2ui.org 上那一版），还有几篇拆架构的长文。下面把读到的和我自己的判断揉在一起写。&lt;/p&gt;
&lt;h2 id=&#34;先把三个被混在一起的词分开&#34;&gt;先把三个被混在一起的词分开&lt;/h2&gt;
&lt;p&gt;「AI 帮你做界面」这件事，其实有三种完全不同的东西，媒体经常混着讲。&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;概念&lt;/th&gt;
          &lt;th&gt;本质&lt;/th&gt;
          &lt;th&gt;例子&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;AI 生成 UI 代码&lt;/td&gt;
          &lt;td&gt;在设计期生成代码，人审完再提交&lt;/td&gt;
          &lt;td&gt;让模型吐出一个 &lt;code&gt;Dashboard.tsx&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;生成式 UI&lt;/td&gt;
          &lt;td&gt;运行期、按用户这次的问题，由模型挑选/组合界面&lt;/td&gt;
          &lt;td&gt;在聊天流里渲染一张天气卡片&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;自适应 UI&lt;/td&gt;
          &lt;td&gt;界面按行为/上下文变化，未必经过模型&lt;/td&gt;
          &lt;td&gt;按用户分群切 A/B 布局&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;生成式 UI 是中间那个。它的关键不是「模型会写前端」，而是&lt;strong&gt;把界面的决定推迟到运行期&lt;/strong&gt;——传统 UI 每一个屏幕都是开发者提前手搭的，生成式 UI 把这一步交给了模型，而且是每一次提问都重新决定。&lt;/p&gt;
&lt;p&gt;一句话的机制定义：生成式 UI = 把工具调用的结果直接接上一个 UI 组件。agent 调了 &lt;code&gt;searchFlights()&lt;/code&gt;，系统不再把 JSON 塞回 prompt 让模型用散文「复述」一遍，而是直接渲染一张带真实数据的航班卡片。&lt;/p&gt;
&lt;h2 id=&#34;三层控制光谱这一层最容易被忽略的分类&#34;&gt;三层控制光谱：这一层最容易被忽略的分类&lt;/h2&gt;
&lt;p&gt;2026 年的各个框架，基本都落在这条光谱上——往右是给 agent 更大的自由度，代价是风险和控制成本。我把它整理成一张表：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;模式&lt;/th&gt;
          &lt;th&gt;agent 返回什么&lt;/th&gt;
          &lt;th&gt;优点&lt;/th&gt;
          &lt;th&gt;代价&lt;/th&gt;
          &lt;th&gt;适合&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;Static（预置组件）&lt;/td&gt;
          &lt;td&gt;工具名 + 数据&lt;/td&gt;
          &lt;td&gt;安全、一致、简单&lt;/td&gt;
          &lt;td&gt;死板，每个场景都得先造组件&lt;/td&gt;
          &lt;td&gt;金融、医疗、SaaS&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Declarative（声明式，A2UI）&lt;/td&gt;
          &lt;td&gt;JSON 规格（蓝图）&lt;/td&gt;
          &lt;td&gt;有护栏的灵活、跨端&lt;/td&gt;
          &lt;td&gt;每个规格要一套渲染器&lt;/td&gt;
          &lt;td&gt;大多数生产应用&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Open-ended（开放式，MCP Apps）&lt;/td&gt;
          &lt;td&gt;HTML / iframe / 小应用&lt;/td&gt;
          &lt;td&gt;复杂工具能力最强&lt;/td&gt;
          &lt;td&gt;安全风险、难移植、样式不统一&lt;/td&gt;
          &lt;td&gt;内部工具、开发者平台&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;三层的差别，说穿了是**「谁掌握『能出现什么组件』这件事」**。&lt;/p&gt;
&lt;p&gt;Static 是开发者说了算：agent 只能在你的组件库里挑，永远画不出你没建的东西。Open-ended 是 agent 说了算：它直接把整个界面（HTML、iframe、甚至一个嵌进去的小应用）丢过来，前端只是个容器。Declarative 在中间——agent 交的是一份描述「要什么」的 JSON 蓝图，前端拿自己的组件目录去渲染。agent 不交代码，只交意图；呈现的控制权还在前端手里。&lt;/p&gt;
&lt;p&gt;顺便分清一个经常被搞错的层：AG-UI 不是界面格式。它是一个事件与状态协议，坐在三层模式&lt;strong&gt;下面&lt;/strong&gt;——负责工具生命周期（started → streaming → finished/failed 的信号）、把用户的点击/表单提交路由回去、以及实时同步 agent 状态和 UI。正是有了这一层，一个运行时（比如 CopilotKit）才能同时支持三种模式。&lt;/p&gt;
&lt;h2 id=&#34;a2ui-到底是一份什么样的规范&#34;&gt;A2UI 到底是一份什么样的规范&lt;/h2&gt;
&lt;p&gt;Declarative 这一档，现在最像「公共标准」的是 Google 的 A2UI（Agent-to-User Interface），v0.9 是 2026 年 4 月发的。它的哲学就一句：&lt;strong&gt;从 demo 到生产，需要干净的关注点分离&lt;/strong&gt;。agent 生成一份 UI 规格，客户端用&lt;strong&gt;自己的&lt;/strong&gt;组件目录渲染。agent 不需要知道你在用 React 还是 Flutter，前端也不需要因为 agent 想展示新东西就加组件。&lt;/p&gt;
&lt;p&gt;我去读了它现在的规范页，先说一个很多人没注意到的现状：&lt;strong&gt;v0.9.1 是当前稳定版，v1.0 还在 release candidate，v0.8 已经标成 legacy&lt;/strong&gt;。也就是说这层标准自己还在动。&lt;/p&gt;
&lt;h3 id=&#34;消息信封现在有四个不是一个&#34;&gt;消息信封：现在有四个，不是一个&lt;/h3&gt;
&lt;p&gt;每一条从服务端流下来的 JSON，都是「一个信封，里面恰好只有一个键」。现在的规范里这四个键是：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;消息&lt;/th&gt;
          &lt;th&gt;作用&lt;/th&gt;
          &lt;th&gt;v0.8 时代的旧名&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;createSurface&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;创建一个新的界面表面，开始渲染&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;beginRendering&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;updateComponents&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;增改界面树上的组件&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;surfaceUpdate&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;updateDataModel&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;往已声明的组件里喂数据&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;dataModelUpdate&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;deleteSurface&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;销毁一个表面&lt;/td&gt;
          &lt;td&gt;—&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;这里有个值得记的细节：那篇在网上传得很广的 GenUI 长文，用的还是 v0.8 的三个旧名（&lt;code&gt;surfaceUpdate&lt;/code&gt; / &lt;code&gt;dataModelUpdate&lt;/code&gt; / &lt;code&gt;beginRendering&lt;/code&gt;）。&lt;strong&gt;规范把消息名整套改掉了，二手文章还没追上。&lt;/strong&gt; 这本身就是「生态还在变」的证据——你要真拿一篇文章去接 SDK，很可能对不上版本。&lt;/p&gt;
&lt;h3 id=&#34;组件树是邻接表&#34;&gt;组件树是「邻接表」&lt;/h3&gt;
&lt;p&gt;A2UI 的界面模型很有意思：它不是嵌套的树，而是&lt;strong&gt;一个扁平的组件数组 + 靠 id 引用拼出结构&lt;/strong&gt;，也就是邻接表。容器组件（&lt;code&gt;Row&lt;/code&gt;、&lt;code&gt;Column&lt;/code&gt;、&lt;code&gt;List&lt;/code&gt;、&lt;code&gt;Card&lt;/code&gt;）用属性指向子组件的 id，客户端把全部组件存进一个 Map，渲染时再重建树。&lt;/p&gt;
&lt;p&gt;这条设计带来一个直接的好处：&lt;strong&gt;服务端可以乱序发组件&lt;/strong&gt;。只要 &lt;code&gt;id&lt;/code&gt; 为 &lt;code&gt;root&lt;/code&gt; 的那个组件到了，客户端就能先把树画出来，剩下的边到边补。规范里明确写着——组件树里必须&lt;strong&gt;有且仅有一个&lt;/strong&gt; id 是 &lt;code&gt;root&lt;/code&gt; 的组件，在它出现之前，其它更新都不会有可见效果，会被先缓冲着。&lt;/p&gt;
&lt;p&gt;还有两个约束值得记：&lt;code&gt;surfaceId&lt;/code&gt; 和 &lt;code&gt;catalogId&lt;/code&gt; 一旦创建就固定，想改只能删掉重建；&lt;code&gt;surfaceId&lt;/code&gt; 在渲染器整个生命周期里必须全局唯一。规范甚至专门提了子 agent 的坑——多个 subagent 一起管 surface 时，要么给 surfaceId 加 agent 名前缀，要么强制用 UUID，否则会撞。&lt;/p&gt;
&lt;p&gt;下面这段是真从规范里抄出来的 &lt;code&gt;updateComponents&lt;/code&gt;（我截了一部分），一个联络表单：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;version&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;v0.9&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;updateComponents&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;surfaceId&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;contact_form_1&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;components&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      { &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;root&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;component&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Card&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;child&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;form_container&amp;#34;&lt;/span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      { &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;form_container&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;component&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Column&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;children&amp;#34;&lt;/span&gt;: [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;header_row&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name_row&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;email_group&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;submit_button&amp;#34;&lt;/span&gt;] },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      { &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;email_field&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;component&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;TextField&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;label&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Email&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;value&amp;#34;&lt;/span&gt;: { &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;path&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/contact/email&amp;#34;&lt;/span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;checks&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          { &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;call&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;required&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;args&amp;#34;&lt;/span&gt;: { &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;value&amp;#34;&lt;/span&gt;: { &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;path&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/contact/email&amp;#34;&lt;/span&gt; } },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;message&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Email is required.&amp;#34;&lt;/span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          { &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;call&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;email&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;args&amp;#34;&lt;/span&gt;: { &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;value&amp;#34;&lt;/span&gt;: { &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;path&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/contact/email&amp;#34;&lt;/span&gt; } },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;message&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Please enter a valid email address.&amp;#34;&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ] },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      { &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;submit_button&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;component&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Button&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;child&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;submit_button_label&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;variant&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;primary&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;action&amp;#34;&lt;/span&gt;: { &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;event&amp;#34;&lt;/span&gt;: { &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;submitContactForm&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;context&amp;#34;&lt;/span&gt;: { &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;formId&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;contact_form_1&amp;#34;&lt;/span&gt; } } } }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;数据用另一个信封喂进去：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{ &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;version&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;v0.9&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;updateDataModel&amp;#34;&lt;/span&gt;: { &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;surfaceId&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;contact_form_1&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;path&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/contact&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;value&amp;#34;&lt;/span&gt;: { &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;email&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;john.doe@example.com&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;subscribe&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt; } } }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;几个能看出设计取舍的点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;结构和数据是分开的。&lt;/strong&gt; &lt;code&gt;updateComponents&lt;/code&gt; 声明「有哪些组件」，&lt;code&gt;updateDataModel&lt;/code&gt; 单独喂值。这样刷新数据不用重建整棵树——它是流式渲染能顺滑的地基。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据绑定用 JSON Pointer&lt;/strong&gt;（RFC 6901），&lt;code&gt;&amp;quot;path&amp;quot;: &amp;quot;/contact/email&amp;quot;&lt;/code&gt; 就是标准指针。A2UI 只对它做了一处扩展：为了让列表模板能渲染，允许不以下划线开头的&lt;strong&gt;相对路径&lt;/strong&gt;——这是对严格 RFC 6901 的一次有意偏离。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;交互分两种。&lt;/strong&gt; &lt;code&gt;action.event&lt;/code&gt; 是把事件发回服务端（服务端动作）；&lt;code&gt;action.functionCall&lt;/code&gt; 是在客户端执行本地函数（比如 &lt;code&gt;openUrl&lt;/code&gt;）。输入组件是双向绑定的。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;集成是一段五步的-python&#34;&gt;集成是一段五步的 Python&lt;/h3&gt;
&lt;p&gt;Google 的 devblog 给了「Hello World」式的集成，真的很短：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install a2ui-agent-sdk
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 1. 定义目录（用基础的，或者带你自己的）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;my_catalog &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; CatalogConfig&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;from_path(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    name&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;lt;MY_CATALOG_NAME&amp;gt;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    catalog_path&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;file:///path/to/catalog.json&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 2. 初始化 schema 管理器（管 A2UI 的版本）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;schema_manager &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; A2uiSchemaManager(version&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;0.9&amp;#34;&lt;/span&gt;, catalogs&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;[my_catalog])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 3. 生成系统提示词&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;system_instruction &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; schema_manager&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;generate_system_prompt(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    role_description&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;You are a helpful assistant great at generating UI...&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 4. 初始化你的 LLM agent&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;my_agent &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; AnyAgentFrameworkLLMAgent(instruction&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;system_instruction)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 5. 执行并流式吐 UI&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;handle_turn&lt;/span&gt;(user_query):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    llm_response &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; my_agent&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;respond(user_query)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    selected &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; schema_manager&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get_selected_catalog()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; parse_response_to_parts(llm_response, selected&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;validator)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;SDK 还承诺了三件生产级的事：&lt;strong&gt;版本协商&lt;/strong&gt;（按客户端能力动态挑版本）、&lt;strong&gt;动态目录&lt;/strong&gt;（运行时按用户权限/设备约束切不同目录）、&lt;strong&gt;弹性流式&lt;/strong&gt;（边生成边解析并「修复」LLM 输出，让客户端在 JSON 还没闭合时就能开始画）。&lt;/p&gt;
&lt;h3 id=&#34;目录本身是一份-json-schema而且传输是解耦的&#34;&gt;「目录」本身是一份 JSON Schema，而且传输是解耦的&lt;/h3&gt;
&lt;p&gt;A2UI 里的目录（Catalog）不是一张组件清单那么简单，它是一份 JSON Schema 文档。规范要求：一份目录定义要同时写 &lt;code&gt;$id&lt;/code&gt;（给 JSON Schema 工具用）和 &lt;code&gt;catalogId&lt;/code&gt;（给 A2UI SDK 做目录协商用），而且两个值要设成&lt;strong&gt;同一个 URI&lt;/strong&gt;。&lt;code&gt;catalogId&lt;/code&gt; 虽然习惯写成 URI 的样子，但它只是个字符串标识，不要求真能解析到某个资源——它存在的全部意义，是「让客户端和服务端对同一个目录达成共识」。规范里的基础目录（Basic Catalog）只是入门的起点，它自己都建议：绝大多数生产应用应该定义自己的目录，去对齐自家设计系统。&lt;/p&gt;
&lt;p&gt;另一个常被忽略的设计是&lt;strong&gt;传输解耦&lt;/strong&gt;。A2UI 不绑定任何传输层，规范原话是「A2UI over MCP、WebSockets、REST、AG-UI、A2A，或者随便你想用的什么」。同一份界面规格，换传输不用重写——这也是它敢叫「框架无关标准」的底气。&lt;/p&gt;
&lt;h3 id=&#34;v09-最关键的一处变化prompt-first&#34;&gt;v0.9 最关键的一处变化：prompt-first&lt;/h3&gt;
&lt;p&gt;这条我觉得比消息改名重要得多。A2UI 从 v0.8 的 structural-output 路线，换成了 &lt;strong&gt;prompt-first&lt;/strong&gt;——把 schema 直接塞进 prompt 当上下文，而不是指望模型走结构化输出。&lt;/p&gt;
&lt;p&gt;规范自己列了得失。好处有两个：schema 不再受 structured output 格式的约束，能写得更丰富更可读；schema 被拆成独立的模块（&lt;code&gt;common_types.json&lt;/code&gt;、&lt;code&gt;catalogs/basic/catalog.json&lt;/code&gt;、&lt;code&gt;server_to_client.json&lt;/code&gt;），更好维护。&lt;/p&gt;
&lt;p&gt;代价它也没藏：&lt;strong&gt;模型不再被 schema 硬约束了，所以校验必须自己做，而且要够狠。&lt;/strong&gt; 规范原话大意是，这需要健壮的错误处理和纠正——系统得能发现出入、尝试修复再渲染，或者直接让模型重试。&lt;/p&gt;
&lt;h2 id=&#34;安全declarative-为什么比-executable-稳&#34;&gt;安全：declarative 为什么比 executable 稳&lt;/h2&gt;
&lt;p&gt;这一段我认为是团队选型时最该抄下来的。A2UI 的防御是叠起来的四层：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;数据不是命令。&lt;/strong&gt; agent 的输出是一种声明式数据格式，没有 &lt;code&gt;eval()&lt;/code&gt;，没有内嵌脚本。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;目录白名单。&lt;/strong&gt; agent 只能引用预先批准的目录里的组件——它没法凭空造一个 &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;目录可按权限切。&lt;/strong&gt; 同一个 agent，未登录用户看到的是最小目录，管理员看到的是完整目录。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;每个规格进场前都过 Schema Manager 校验。&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;对比一下 Open-ended 那档（MCP Apps）：agent 返回的是任意 HTML / iframe，等于把 XSS 和界面伪装（UI-redressing）的入口直接递给了模型——尤其是当内容里混进了不受信任的用户输入时。&lt;/p&gt;
&lt;p&gt;我的立场很明确：&lt;strong&gt;任何碰到敏感数据或不可逆动作（支付、删除、改权限）的流程，都别让它跑 Open-ended。&lt;/strong&gt; 老老实实待在 Static 或 Declarative，目录卡紧一点。「模型很聪明所以没事」不是安全模型。&lt;/p&gt;
&lt;h2 id=&#34;我的三个判断&#34;&gt;我的三个判断&lt;/h2&gt;
&lt;p&gt;拆完规范，说三个我自己的结论——不是转述厂商的说法。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第一，这一层的胜负手不是模型能力，是「目录（catalog）」。&lt;/strong&gt; 生成式 UI 谁都能做，难的是「默认能出现哪些组件」这件事由谁定义。目录是这层的词汇表——你定义了目录，就等于定义了所有人用生成式 UI 时的默认积木。这跟 MCP 那边 tools registry 之争是同一个结构：协议是免费的，目录是权力。谁先把默认目录做成事实标准，谁就拿到了分发权。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第二，OpenAI 和 Google 走的是两条不同的路，而两条路的护城河都在分发，不在技术。&lt;/strong&gt; OpenAI 的 Intelligent UI 是 &lt;strong&gt;native 目录 + 编译器&lt;/strong&gt;：自家一套可流式渲染的原生组件，配一个「边生成边编译」的管道（据 MacRumors 报道，图表可以先画出坐标轴、数字随后到），闭环、可控，但封闭。Google 的 A2UI 是 &lt;strong&gt;开放目录 + 任意渲染器&lt;/strong&gt;：React、Flutter、Angular、Lit 四个官方渲染器，agent 不用知道你在用什么端。两条路线最终都会靠自家产品的分发碾压——ChatGPT 铺十亿用户，Android/Chrome 后台就替你铺 A2UI。创业公司在这条光谱上只剩两个切口：垂直领域的&lt;strong&gt;目录&lt;/strong&gt;（比如专门给金融、医疗做的组件集），和某个端的&lt;strong&gt;渲染器&lt;/strong&gt;。做通用协议层，你没有分发。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第三，什么情况下它会变成泡沫，条件很清楚。&lt;/strong&gt; 生成式 UI 的核心卖点是「感知延迟」——200ms 出现的骨架，比 2 秒后出现的完整段落感觉快得多，哪怕总时间没变。但 v0.9 的 prompt-first 把「校验」的成本推给了你：模型输出不再被 schema 约束，你得自己 heal、重试。&lt;strong&gt;如果校验和重试的开销，把流式渲染省下来的那点感知延迟吃回去了，卖点就塌了。&lt;/strong&gt; 我暂时不认为这会普遍发生——但这是这条路线最该盯的数据，而不是 benchmark 分数。&lt;/p&gt;
&lt;h2 id=&#34;如果你现在就要选这是我会用的顺序&#34;&gt;如果你现在就要选，这是我会用的顺序&lt;/h2&gt;
&lt;p&gt;一句话版决策树：流程碰敏感数据或不可逆动作 → &lt;strong&gt;Static&lt;/strong&gt;（预置组件、目录卡死）；需要跨端（web + 移动 + 桌面）→ &lt;strong&gt;Declarative / A2UI&lt;/strong&gt;（JSON 规格、共享目录）；工具复杂到必须要深度定制 UI → 才考虑 &lt;strong&gt;Open-ended&lt;/strong&gt;，而且只在内部环境用。&lt;/p&gt;
&lt;p&gt;2026 年更靠谱的做法不是二选一，是&lt;strong&gt;叠层&lt;/strong&gt;：用 A2UI 管跨端组件，用 AG-UI 管事件与状态，用 MCP 管工具接入。三层各管一段，谁也不用假装自己能管全部。&lt;/p&gt;
&lt;p&gt;（举个已经落地的组合：Oracle 最近上的 Agent Spec，就是把它自己的 Agent Spec + AG-UI + A2UI 叠在一起——Agent Spec 定义「跑什么」，AG-UI 承载「交互怎么走」，A2UI 定义「用户碰到的界面长什么样」。堆栈里任何一层都能换实现，体验不变。这种「各管一段」的分工，是这层生态开始成型的信号。）&lt;/p&gt;
&lt;h2 id=&#34;写在最后&#34;&gt;写在最后&lt;/h2&gt;
&lt;p&gt;我写完这篇的时候，还在想一个没答案的问题：生成式 UI 会不会让「界面」这个词本身消失？&lt;/p&gt;
&lt;p&gt;过去我们说的界面，是开发者提前搭好、所有人共享的一层壳。生成式 UI 把它变成了&lt;strong&gt;一次性的、为这一次对话临时长出来的东西&lt;/strong&gt;——用完就扔。这对产品是好事，对「设计系统」这门手艺却未必：如果每次都是模型现搭，那品牌一致性、可访问性这些东西，靠谁保证？&lt;/p&gt;
&lt;p&gt;我的答案是目录。目录就是那个「不许模型自由发挥」的边界——它既是安全阀，也是设计系统的延续。谁能把自己的设计系统变成 agent 的默认目录，谁就在生成式 UI 时代保住了自己的壳。这一仗，现在刚刚开打。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;相关阅读&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.yesmiracle.net/post/20260726-agent-to-agent-protocol-guide/&#34; &gt;《Agents 互联！A2A/MCP/ANP/ACP 四大协议详解，还有一张看不见的攻击面地图！》&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.yesmiracle.net/post/20260727-mcp-protocol-security-model/&#34; &gt;《MCP 协议安全模型剖析：从 STDIO 到 Tool 的四层防御体系》&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.yesmiracle.net/post/20261008-agent-approval-permission-layer/&#34; &gt;《「在聊天框里点确认」撑不住了：Agent 的审批和权限，正在长成独立的一层》&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        </item>
        
    </channel>
</rss>
