How to Use A2UI + Vue to Enable Large Models to Generate Interactive Interfaces

This article details how a unified AI assistant framework built for Bilibili's advertising business evolves from plain text output to generating fully interactive UI by leveraging Google’s A2UI protocol, a custom Vue renderer, double‑validation mechanisms, SSE dual‑channel streaming, and a wrapper component system, providing concrete examples and architectural diagrams.

Bilibili Tech
Bilibili Tech
Bilibili Tech
How to Use A2UI + Vue to Enable Large Models to Generate Interactive Interfaces

Introduction

In Bilibili's commercial advertising business we built a unified AI assistant framework that outputs Markdown‑rendered content for various scenarios. As large models shift from chat to task execution, we needed a stable way for the AI to generate truly interactive interfaces, prompting the development of a generative UI system based on the Google A2UI protocol, a custom Vue renderer, and a complete agent toolchain.

1. Bilibili Commercial Advertising AI Assistant Framework

We support three business scenarios: ad‑placement assistant, product‑help assistant, and data‑analysis assistant. The framework provides a standardized front‑end with a unified dialog interface and Markdown rendering, allowing business teams to implement only the required interfaces.

1.1 Unified AI Assistant Framework

Standardized front‑end framework with visual styles and extensible components.

Markdown rendering for rapid content presentation.

1.2 Evolution from “Readable” to “Interactive”

Simple text responses cannot satisfy needs such as displaying key‑metric cards, trend charts, or structured forms that users can edit directly. Template‑based components like ::: ProductCard ::: work for fixed UI shapes but tightly couple the agent to front‑end templates, preventing the agent from describing UI structures autonomously.

2. Generative UI Approach

2.1 Why Choose A2UI

Standardized and community‑supported protocol reduces technical debt.

Declarative JSON‑Schema description fits large‑model generation.

Incremental updates via dataModelUpdate / surfaceUpdate.

Framework‑agnostic: the same UI description can be rendered in Vue, React, or mini‑programs.

Security through component whitelist; the client renders the UI.

2.2 Overall Architecture

3. Backend + Agent Design

3.1 Request Parameters

Each business request carries a capability boundary declaration.

3.2 Runtime Schema Assembly

Components and actions are dynamically assembled based on business identifiers and a whitelist, enabling capability isolation, dynamic extension, and controlled output.

3.3 Double‑Validation Mechanism

Because model output can be unstable, A2UI JSON must pass two layers of validation:

Structural validation : message type, component whitelist, complete attributes, and action whitelist.

Transition validation : each Surface (an independent UI instance) follows a state machine (uncreated → created → interactive). Rules prevent updates to non‑existent surfaces, duplicate beginRendering, and mismatched surfaceId.

3.4 SSE Dual‑Channel Output

We separate the text stream and structured data into two independent channels. If the a2ui_message channel loses data, the front‑end can fall back to the text stream for degraded display or retry using the finish event, ensuring reliable UI rendering.

4. Front‑End Universal Rendering SDK

4.1 Overall Structure

Based on Google’s open‑source A2UI protocol and React renderer, we implemented a Vue renderer distributed as an npm package. Business teams can install the package and start using it immediately.

4.2 Message Processor

The processor generates a unique signature for each A2UI JSON message per surface, skipping already processed messages to guarantee idempotency. It also handles out‑of‑order or reversed delivery, tolerating occasional message loss with minimal UI inconsistency.

4.3 DataModel Design

A unified state store supports path‑level read/write and data binding. Example structure:

{
  "/form/name": "Zhang San",
  "/form/email": "[email protected]",
  "/cart/items": [{"id": 1, "name": "Product A"}],
  "/ui/loading": false
}

Components reference data via paths; any change automatically updates all bound components.

4.4 Wrapper Component System

Business teams add custom Vue components following a unified specification. The SDK provides a wrapper function that automatically parses node attributes, binds DataModel paths, builds action callbacks, and handles common states like loading and error.

Automatic attribute parsing.

Automatic DataModel binding.

Automatic action callback construction.

Unified handling of loading and error states.

4.5 Action Closure

Component actions are split into front‑end and back‑end handling. The SDK supplies two default back‑end actions; custom actions can be defined by the business.

User interaction triggers an action.

Action bubbles to the business layer.

Business layer calls a back‑end HTTP API.

Back‑end returns dataModelUpdate or surfaceUpdate.

Front‑end performs an incremental refresh.

5. Integration with Existing Frameworks

We extend, rather than replace, existing AI assistants. The agent returns a finish event containing the standard component A2UIMessage.vue, which can be used directly as a template. ::: a2ui-message {"message": [...]} Integration steps for business teams:

Implement business components and integrate the SDK.

Register component packages.

Backend or agent calls the A2UI service to generate messages.

Benefits:

Business teams focus on logic, not UI implementation.

Scenario onboarding time reduced from days to hours.

Agents shift from “filling data formats” to “understanding UI description language”.

True UI generation capability.

Front‑back decoupling and protocol standardization.

Reusable infrastructure across multiple businesses.

6. Demo Examples

6.1 Chart Example

User prompt: “Show the sales trend of five car manufacturers over the past 12 months.”

{
  "sessionId": "session-xxx",
  "messages": [
    {"createSurface": {"surfaceId": "surface-xxx"}},
    {"updateComponents": {
      "surfaceId": "surface-xxx",
      "components": [
        {"id": "root", "component": "Card", "child": "chart-container"},
        {"id": "sales-chart", "component": "Charts", "type": "line", "title": "近12个月车企销量趋势", "options": {
          "xAxis": {"data": ["1月", "2月", ...]},
          "series": [
            {"name": "车企A", "data": [12500, 13200, ...]},
            {"name": "车企B", "data": [10200, 10800, ...]}
          ]
        }}
      ]
    }}
  ]
}

Rendered result:

6.2 Form Example

User prompt: “Generate a user registration form.”

{
  "sessionId": "session-xxx",
  "messages": [
    {"createSurface": {"surfaceId": "surface-xxx", "root": "root"}},
    {"updateDataModel": {"surfaceId": "surface-xxx", "value": {"userName": "", "userEmail": "", "userPhone": ""}}},
    {"updateComponents": {"surfaceId": "surface-xxx", "components": [
      {"id": "root", "component": "Card", "child": "form-column"},
      {"id": "form-column", "component": "Column", "children": ["title-text", "name-field", "email-field", "phone-field", "submit-btn"]},
      {"id": "title-text", "component": "Text", "text": "用户报名信息填写"},
      {"id": "name-field", "component": "TextField", "label": "姓名", "placeholder": "请输入姓名"},
      {"id": "email-field", "component": "TextField", "label": "邮箱", "placeholder": "请输入邮箱"},
      {"id": "phone-field", "component": "TextField", "label": "手机号", "placeholder": "请输入手机号"},
      {"id": "submit-btn", "component": "Button", "text": "提交报名", "action": {"name": "marketing.submitLead"}}
    ]}}
  ]
}

Rendered result:

After user submission, the front‑end sends the data back to the back‑end:

// 发送消息示例:后端读取注释中的数据作为结果
用户提交了数据<!-- {"action":"marketing.submitLead","data":{"userName":"小明","userEmail":"[email protected]","userPhone":"12345612345"}} -->

The back‑end processes the data and returns an update to replace the form with a success message:

{
  "sessionId": "session-xxx",
  "messages": [
    {"updateDataModel": {"surfaceId": "surface-xxx", "value": {}}},
    {"updateComponents": {"surfaceId": "surface-xxx", "components": [
      {"id": "success-icon", "component": "Text", "text": "✓"},
      {"id": "title-text", "component": "Text", "text": "报名成功"},
      {"id": "message-text", "component": "Text", "text": "感谢您的报名,我们会尽快与您联系。"},
      {"id": "form-column", "component": "Column", "children": ["success-icon", "title-text", "message-text"]}
    ]}}
  ]
}

Front‑end receives the message and updates the UI accordingly.

7. Conclusion

The A2UI solution is already deployed in several business lines and is iterating quickly. Because the protocol is still early and large‑model capabilities are evolving, we currently adopt a hybrid mode: A2UI handles generic interactions while complex components are implemented by the business. As the protocol and model abilities mature, generative UI will unlock stronger capabilities and richer scenarios, and we plan to explore applications on mobile and mini‑program platforms.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

LLMAgentVueSSEGenerative UIA2UIRuntime Schema
Bilibili Tech
Written by

Bilibili Tech

Provides introductions and tutorials on Bilibili-related technologies.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.