
A2UI v0.8 协议深度解析基于 JSONL 流式传输的 Agent 到 UI 渲染规范与实现指南【免费下载链接】a2ui项目地址: https://gitcode.com/GitHub_Trending/a2/a2uiA2UIAgent to UI是一套由服务器LLM Agent向客户端流式推送抽象 UI 定义的协议其核心是一个基于 JSON LinesJSONL的单向流与一套可扩展的组件目录Catalog机制。本文以仓库中 v0.8 协议规范a2ui_protocol.md为骨架结合 JSON Schema 与渲染器源码系统讲解协议的设计动因、消息类型、组件模型、数据绑定、事件回传与客户端实现要点帮助你理解并实际落地一个LLM 可直接生成、客户端可渐进渲染、跨平台复用的 UI 传输链路。一、协议定位与设计约束A2UI v0.8 是仓库中的Legacy遗留版本主要服务于向后兼容。官方在文档开头明确标注生产环境建议使用 v0.9.1Current同时可参考 v0.9Stable 与 v1.0Candidate从 v0.8 升级到 v0.9 的迁移要点见 Evolution Guide而 v0.8 对应的 A2A 扩展定义见 v0.8-a2a-extension.md。v0.8 协议的所有设计决策都追溯到 LLM 生成、感知性能与平台独立性这三类核心挑战需求设计结论LLM 易于生成使用声明式、扁平结构Adjacency List / 邻接表消息无状态、每条自包含UI 渐进渲染、响应迅速通过 JSONL/SSE 流式传输客户端逐行解析、边收边渲染平台无关客户端自定义 Widget Catalog协议只定义抽象组件树状态管理高效解耦组件与数据模型分离componentUpdate与dataModelUpdate各自独立通信架构健壮可扩展UI 走单向流SSE事件通过 A2A 消息回传其中最关键的设计驱动是协议必须易于被 Transformer LLM 生成声明式结构这是一个 Column包含这些 children而非命令式先加一个 Column再往里 append 一个 Text扁平组件列表给组件一个 ID后续用 ID 引用无需关心嵌套深度每条 JSONL 消息自包含LLM 可以随推理进度增量输出。二、三大解耦支柱组件树、数据模型与 Widget 注册表协议的核心哲学是解耦三个要素见 a2ui_protocol.md 的 §1.1组件树结构服务器通过surfaceUpdate消息下发的抽象组件树描述 UI 长什么样。数据模型状态服务器通过dataModelUpdate下发的 JSON 对象包含填充 UI 的动态值文本、布尔、列表等。Widget 注册表Catalog属于客户端应用的一部分是组件类型如Row、Text到原生控件实现的映射。服务器必须生成目标客户端注册表能识别的组件。这种分离带来的直接收益是改变 UI 中的一段文本不需要重发整个 UI 定义——结构只发一次后续只需发送携带变化数据的小体积dataModelUpdate。2.1 Surface多 UI 区域管理Surface是一块连续的屏幕区域协议用surfaceId唯一标识并管理它。单个 A2UI 流可以同时控制多个互相独立的 UI 区域每个 surface 拥有独立的根组件、独立的组件层级和独立的数据模型避免多个 surface 时 key 冲突。典型场景聊天应用中每一条 AI 生成回复渲染进对话历史的一个独立 surface另用一个常驻 surface 作为展示相关信息的侧边栏。surfaceId出现在beginRendering、surfaceUpdate、dataModelUpdate、deleteSurface等消息中用于把变更导向正确的区域。三、四种服务器到客户端消息协议规定每条 JSONL 消息是包含恰好一个动作属性的 JSON 对象。以下字段定义均可在 server_to_client.json 中逐条核对。3.1surfaceUpdateUI 结构的主要定义方式携带surfaceId与components数组{ surfaceUpdate: { surfaceId: main_content_area, components: [ { id: unique-component-id, component: { Text: { text: { literalString: Hello, World! } } } } ] } }components必填扁平组件实例列表minItems: 1。每个组件条目含必填的id本 surface 内唯一字符串用于父子引用与必填的component组件类型包装对象。Schema 中还允许可选的weightnumber对应 CSS 的flex-grow仅当组件是 Row/Column 的直接子级时可以设置。如果要用surfaceUpdate创建新 surfacesurfaceId必须是一个从未用于现有 surface 的新唯一标识。3.2dataModelUpdate唯一允许修改客户端数据模型的消息{ dataModelUpdate: { surfaceId: main_content_area, path: user, contents: [ { key: name, valueString: Bob }, { key: isVerified, valueBoolean: true }, { key: address, valueMap: [ { key: street, valueString: 123 Main St }, { key: city, valueString: Anytown } ] } ] } }path可选指向数据模型内的位置如/user/name省略或设为/时整个数据模型被替换。contents数据条目数组按邻接表组织。每条必须含key与恰好一个类型化value*属性valueString、valueNumber、valueBoolean、valueMap。valueMap用邻接表表示的嵌套 JSON 对象可以无限层级嵌套。3.3beginRendering渲染信号包含root根组件 ID与surfaceId均必填可选catalogId与styles样式对象additionalProperties: true。它的作用是防止未完成内容的闪烁客户端在收到它之前只缓冲组件与数据收到后才执行首次渲染保证初始视图连贯。{ beginRendering: { surfaceId: unique-surface-1, catalogId: https://my-company.com/inline_catalogs/temp-signature-pad-catalog, root: root-component-id } }3.4deleteSurface显式删除一个 surface 及其全部内容只需携带surfaceId。四、数据流模型从流到渲染再到事件回传协议由服务器到客户端的单向 UI 流与客户端到服务器的单个事件组成通常基于 Server-Sent EventsSSE承载 JSONL 流。完整时序服务器流服务器在 SSE 连接上开始发送 JSONL 流。客户端缓冲surfaceUpdate的组件定义按surfaceId存入MapString, Componentsurface 不存在则创建dataModelUpdate构建/更新内部 JSON 数据模型。渲染信号服务器发送带rootID 的beginRendering客户端此前只缓冲、不渲染收到后才进入就绪状态。客户端渲染从root出发递归地按组件 ID 查缓冲表解析数据绑定用WidgetRegistry实例化原生控件。用户交互与事件用户操作控件后客户端解析组件action.context中的绑定构造userActionJSON通过 A2A 消息发送给服务器。动态更新服务器处理后在原来的 SSE 流上发送新的surfaceUpdate/dataModelUpdate必要时deleteSurface客户端更新缓冲与数据模型并重渲染。仓库实现佐证renderers/web_core/src/v0_8/data/model-processor.ts中对每条消息先检查beginRendering再处理surfaceUpdate、dataModelUpdate其中注释明确指出若surfaceUpdate先于beginRendering到达surface 仍会被返回即组件被缓冲但只有在beginRendering之后才真正触发渲染——这与协议 §1.4 的缓冲-信号-渲染模型完全一致。五、组件模型与 Catalog 协商5.1 Catalog 定义与标准目录Catalog是服务器与客户端之间关于可渲染 UI的契约包含支持的组件类型、属性与样式由Catalog Definition Document定义。v0.8 的标准目录标识符为https://a2ui.org/specification/v0_8/standard_catalog_definition.json。Catalog ID 是简单字符串标识惯例是使用自己域名下的 URI便于调试、避免歧义与命名冲突。任何可能破坏 Agent 与渲染器兼容性的目录变更都必须分配新的catalogId否则会出现Agent 已更新而客户端未更新或反之的意外行为。v0.8 标准目录standard_catalog_definition.json内置的组件及其核心属性如下组件核心属性说明Texttext必填literalString/path、usageHinth1–h5/caption/body文本支持简单 Markdown不含 HTML/图片/链接Imageurl必填、altText、fitcontain/cover/fill/none/scale-down、usageHinticon/avatar/smallFeature/mediumFeature/largeFeature/header图片fit对应 CSSobject-fitIconname必填内置约 50 个命名图标accountCircle/add/arrowBack/…Video/AudioPlayerurl必填Audio 另有description音视频Row/Columnchildren必填explicitList/template、distribution、alignment布局容器Row 的distribution对应justify-contentalignment对应align-itemsListchildren必填、directionvertical/horizontal、alignment可滚动列表Cardchild必填卡片容器TabstabItems必填每项含title与child选项卡Divideraxishorizontal/vertical分隔线ModalentryPointChild、contentChild均必填弹窗含触发入口与内容Buttonchild必填、primary、action必填含name与可选context数组按钮action.context数组每项是keyvaluepath/literalString/literalNumber/literalBooleanCheckBoxlabel、value均必填复选框TextFieldlabel必填、text、textFieldTypedate/longText/number/shortText/obscured、validationRegexp文本输入支持正则校验DateTimeInputvalue必填、enableDate、enableTime日期/时间选择ISO 8601MultipleChoiceselections、options均必填、maxAllowedSelections、variantcheckbox/chips、filterable多选Slidervalue必填、label、minValue、maxValue滑块目录级styles定义了font字符串与primaryColor十六进制色值pattern 为^#[0-9a-fA-F]{6}$通过beginRendering.styles下发。5.2 协商三步流程第 1 步服务器声明能力。服务器在 A2A 协议的 Agent Card 中声明supportedCatalogIds字符串数组可选所有预定义目录 ID与acceptsInlineCatalogs布尔可选默认false。需要说明这不是严格契约只是给编排器与客户端识别 UI 能力匹配的信号运行时编排 Agent 可能把任务动态委派给支持更多目录的子 Agent因此客户端应把广告的supportedCatalogIds视为真实支持目录的子集。{ name: Restaurant Finder, capabilities: { extensions: [ { uri: https://a2ui.org/a2a-extension/a2ui/v0.8, params: { supportedCatalogIds: [ https://a2ui.org/specification/v0_8/standard_catalog_definition.json, https://my-company.com/a2ui/v0.8/my_custom_catalog.json ], acceptsInlineCatalogs: true } } ] } }第 2 步客户端声明支持目录。在发往服务器的每一条 A2A 消息的metadata字段中携带a2uiClientCapabilities对象supportedCatalogIds字符串数组必填若支持标准目录必须显式包含其 ID目录内容应编译进 Agent不得运行时下载以防恶意内容被动态注入提示词与inlineCatalogs对象数组可选完整目录定义文档仅当服务器声明acceptsInlineCatalogs: true时才能提供常用于本地开发快速迭代。{ metadata: { a2uiClientCapabilities: { supportedCatalogIds: [ https://a2ui.org/specification/v0_8/standard_catalog_definition.json, https://my-company.com/a2ui_catalogs/custom-reporting-catalog-1.2 ], inlineCatalogs: [ { catalogId: https://my-company.com/inline_catalogs/temp-signature-pad-catalog, components: { SignaturePad: { type: object, properties: { penColor: { type: string } } } }, styles: {} } ] } }, message: { prompt: { text: Find me a good restaurant } } }第 3 步服务器选择目录并渲染。服务器在beginRendering中用catalogId字段指定所选目录必须是客户端supportedCatalogIds或inlineCatalogs中的 ID若省略catalogId客户端必须默认使用该协议版本的标准目录。每个 surface 可以使用不同目录这在多 Agent 系统不同 Agent 支持不同目录中尤其灵活。5.3 为开发者解析 Schema构建 Agent 时建议使用解析后的 schema把目标组件目录合入server_to_client.json这样 LLM 能拿到所有组件、属性及目录样式的严格定义UI 生成更可靠通用server_to_client.json是抽象线协议解析后的 schema 才是生成工具。基于标准server_to_client_schema与custom_catalog_definition做替换的 JSON 操作逻辑component_properties custom_catalog_definition[components] style_properties custom_catalog_definition[styles] resolved_schema copy.deepcopy(server_to_client_schema) resolved_schema[properties][surfaceUpdate][properties][components][items][properties][component][properties] component_properties resolved_schema[properties][beginRendering][properties][styles][properties] style_properties仓库中的 server_to_client_with_standard_catalog.json 就是已把标准组件替换进去的解析后 schema 示例可直接对照。六、UI 组合邻接表模型与动态列表6.1 邻接表模型协议把 UI 定义为扁平组件列表树结构用 ID 引用隐式构建。容器组件Row、Column、List、Card通过属性引用子组件 ID客户端负责把所有组件存进 map如MapString, Component渲染时重建树。该模型允许服务器以任意顺序发送组件定义只要在发送beginRendering前所有必要组件都已到达。仓库中 00_simple-text.json 是最小可运行示例一个surfaceUpdateroot 为Text 一个beginRendering。6.2explicitListvstemplate容器组件的children对象必须恰好包含explicitList或template之一explicitList静态已知子级的组件 ID 有序数组。template从数据绑定列表动态渲染子级含必填的dataBinding数据模型中的列表路径如/user/posts与componentId缓冲中用作模板的组件 ID。客户端遍历dataBinding指向的列表为每一项渲染componentId组件并把该项数据提供给模板组件做相对数据绑定。{ type: object, description: Defines the children of a container component. Must contain exactly one of explicitList or template., properties: { explicitList: { type: array, description: An ordered list of component IDs that are direct children., items: { type: string, description: The ID of a child component. } }, template: { type: object, properties: { dataBinding: { $ref: #/definitions/DataPath }, componentId: { type: string } }, required: [dataBinding, componentId], additionalProperties: false } }, minProperties: 1, maxProperties: 1 }七、动态数据与状态管理BoundValue绑定任何可数据绑定的属性如Text的text都接受BoundValue对象定义字面量、数据路径或两者的组合{ type: object, description: A value that can be either a literal string or bound to the data model., properties: { literalString: { type: string }, path: { $ref: #/definitions/DataPath } }, minProperties: 1, additionalProperties: false }组件同样可以绑定数字literalNumber、布尔literalBoolean或数组literalArray。三种行为模式仅字面量值静态直接显示。text: { literalString: Hello }仅路径值动态渲染时从数据模型解析。text: { path: /user/name }路径 字面量初始化简写相当于隐式dataModelUpdate——客户端先把字面量写入path指定位置再把属性绑定到该路径。text: { path: /user/name, literalString: Guest }会初始化/user/name为 Guest 并绑定之一步完成设默认值 绑定。需要明确协议边界A2UI 支持直接的 1:1 绑定不含转换器格式化、条件判断等都不支持任何数据转换必须由服务器在发送dataModelUpdate前完成。八、事件处理userAction与error用户交互通过 A2A 消息回传保持主数据流单向。客户端发送的单个 JSON 对象是包装器必须恰好包含userAction或error之一见 client_to_server.json 的oneOf约束。8.1userAction结构字段类型必填说明namestring✓动作名取自组件action.name如submit_formsurfaceIdstring✓事件来源 surface 的 IDsourceComponentIdstring✓触发事件的组件 ID如my_buttontimestampstring✓ISO 8601 时间戳contextobject✓组件action.context中键值对经绑定解析后的结果解析过程与渲染绑定一致客户端遍历context数组解析所有字面量或数据绑定值构造context对象。8.2error消息客户端在渲染或数据绑定出错时发送内容灵活additionalProperties: true是给服务器的反馈通道。8.3 完整事件流示例组件定义surfaceUpdate中定义按钮及其动作{ surfaceUpdate: { surfaceId: main_content_area, components: [ { id: submit_btn, component: { Button: { child: submit_btn_text, action: { name: submit_form, context: [ { key: userInput, value: { path: /form/textField } }, { key: formId, value: { literalString: f-123 } } ] } } } } ] } }数据模型dataModelUpdate{path: form, contents: [{key: textField, valueString: User input text}]}用户点击submit_btn。客户端解析action.contextuserInput从/form/textField解析为 User input textformId取字面量。客户端发送POST 到事件端点{ userAction: { name: submit_form, surfaceId: main_content_area, sourceComponentId: submit_btn, timestamp: 2025-09-19T17:05:00Z, context: { userInput: User input text, formId: f-123 } } }服务器响应处理后若 UI 需要变化在独立的 SSE 流上发送新的surfaceUpdate/dataModelUpdate。仓库示例佐证specification/v0_8/json/catalogs/basic/examples/00_simple-login-form.json完整演示了数据模型初始化 → Column 布局含 TextField 双向绑定、按钮 action→ beginRendering的真实登录表单流00_interactive-button.json、02_email-compose.json等示例则覆盖了按钮交互与复杂布局。九、客户端实现要点健壮的 A2UI 客户端解释器应由以下模块组成协议 §6 清单JSONL Parser逐行读取流并解码每条 JSON。Message Dispatcher识别消息类型beginRendering、surfaceUpdate等并路由到对应处理器仓库实现见 model-processor.ts 的消息分发逻辑。Component BufferMapString, Component按id存组件由surfaceUpdate填充。Data Model StoreMapString, dynamic持有应用状态由dataModelUpdate构建修改。Interpreter State状态机跟踪是否可渲染如_isReadyToRender布尔由beginRendering置 true。Widget Registry开发者提供的MapString, WidgetBuilder把组件类型字符串映射到构建原生控件的函数。Binding Resolver把BoundValue如{path: /user/name}对照 Data Model Store 解析。Surface Manager基于surfaceId创建、更新、删除 surface 的逻辑。Event Handler暴露给 Widget Registry 的函数构造并发送客户端事件消息userAction到配置的 REST API 端点。从仓库的renderers/web_core/src/v0_8/目录结构看v0.8 渲染器被拆分为types/类型定义、schema/协议 schema 映射、data/模型处理、events/、styles/等模块与上述职责划分一一对应可作为实现参考。十、完整 JSON Schema协议提供了两套正式 JSON Schema是单条消息必须符合的权威定义服务器到客户端server_to_client_with_standard_catalog.json每条 JSONL 行必须是一个符合该 schema 的 JSON 对象包含整套基础目录组件组件可按客户端支持的目录替换该 schema 经过优化可适配各 LLM 的 structured output 模式直接生成。客户端到服务器client_to_server.json单条客户端事件消息的正式 schemaoneOf约束保证userAction与error二选一。通用版 server_to_client.json 则是抽象线协议不含具体组件配合 standard_catalog_definition.json 或自定义目录使用。十一、一个完整的端到端最小流综合全协议渲染一张用户资料卡只需下面 10 行 JSONL协议 §1.5 的完整示例{surfaceUpdate: {components: [{id: root, component: {Column: {children: {explicitList: [profile_card]}}}}]}} {surfaceUpdate: {components: [{id: profile_card, component: {Card: {child: card_content}}}]}} {surfaceUpdate: {components: [{id: card_content, component: {Column: {children: {explicitList: [header_row, bio_text]}}}}]}} {surfaceUpdate: {components: [{id: header_row, component: {Row: {alignment: center, children: {explicitList: [avatar, name_column]}}}}]}} {surfaceUpdate: {components: [{id: avatar, component: {Image: {url: {literalString: https://www.example.com/profile.jpg}}}}]}} {surfaceUpdate: {components: [{id: name_column, component: {Column: {alignment: start, children: {explicitList: [name_text, handle_text]}}}}]}} {surfaceUpdate: {components: [{id: name_text, component: {Text: {usageHint: h3, text: {literalString: A2A Fan}}}}]}} {surfaceUpdate: {components: [{id: handle_text, component: {Text: {text: {literalString: a2a_fan}}}}]}} {surfaceUpdate: {components: [{id: bio_text, component: {Text: {text: {literalString: Building beautiful apps from a single codebase.}}}}]}} {dataModelUpdate: {contents: {}}} {beginRendering: {root: root}}这个流演示了协议的全部要点扁平邻接表 ID 引用构建嵌套结构、dataModelUpdate初始化数据模型、beginRendering触发渲染。对照仓库中 basic 目录下的示例集你还可以找到天气、音乐播放器、任务卡片、用户资料、日历、航班状态、邮件撰写等真实场景的完整 JSONL 流作为 Agent 端生成与客户端调试的现成素材。延伸阅读本协议对应的 A2A 扩展规范见 v0.8-a2a-extension.md标准目录与自定义目录的变更记录见 custom_catalog_changes.mdv0.8 到 v0.9 的协议演进可参考 v0.9-evolution-guide.md。【免费下载链接】a2ui项目地址: https://gitcode.com/GitHub_Trending/a2/a2ui创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考