A2UI 完整学习指南(含 Java 后端 + 前端实战示例)

A2UI 完整学习指南(含 Java 后端 + 前端实战示例)

这是可直接运行的完整版教程,包含:

  1. A2UI 核心知识(精简版)
  2. Java 后端:生成 A2UI 流式 JSON 接口(Spring Boot)
  3. 前端:原生 JS / Vue / React 三选一渲染 A2UI
  4. 完整交互流程(用户请求→Java生成UI→前端渲染→交互回传)

一、A2UI 核心速览(必看)

1. 是什么

  • Agent-to-UI :AI 智能体用 JSON 描述界面 ,前端原生渲染
  • 安全:不执行代码,只渲染可信组件
  • 跨平台:Java 后端生成一份 JSON,Web/安卓/iOS/桌面都能渲染

2. 核心结构

  • beginRendering:开始渲染界面
  • surfaceUpdate:更新 UI 组件(按钮、输入框、卡片等)
  • dataModelUpdate:更新数据(自动同步到界面)
  • 传输格式:JSONL(每行一个 JSON,流式输出)

二、Java 后端实战(Spring Boot + A2UI 流式接口)

功能

Java 接口直接输出 A2UI 流式 JSON,前端 SSE 接收并渲染界面。

1. pom.xml 依赖

xml 复制代码
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>
</dependencies>

2. Java 控制器(生成 A2UI 流)

java 复制代码
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import java.time.Duration;

@RestController
public class A2uiController {

    // 流式返回 A2UI JSONL
    @GetMapping(value = "/a2ui/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> a2uiStream() {
        return Flux.interval(Duration.ofMillis(300))
                .take(3)
                .map(index -> switch (index.intValue()) {
                    // 1. 开始渲染
                    case 0 -> """
                            {"beginRendering":{"surfaceId":"booking"}}
                            """;
                    // 2. 渲染界面组件
                    case 1 -> """
                            {"surfaceUpdate":{"surfaceId":"booking","components":[{"id":"title","component":{"Text":{"text":"餐厅预订","style":{"fontSize":20,"fontWeight":"bold"}}}},{"id":"row","component":{"Row":{"children":["label","input"],"gap":12}}},{"id":"label","component":{"Text":{"text":"人数:"}}},{"id":"input","component":{"TextField":{"placeholder":"请输入人数","bind":"/people"}}},{"id":"btn","component":{"Button":{"text":"确认预订","action":{"type":"submit","value":"booking"}}}}]}}
                            """;
                    // 3. 初始化数据
                    case 2 -> """
                            {"dataModelUpdate":{"data":{"people":2}}}
                            """;
                    default -> "";
                })
                .filter(s -> !s.isEmpty());
    }

    // 接收前端交互事件
    @GetMapping("/a2ui/action")
    public String receiveAction(String data) {
        System.out.println("前端用户操作:" + data);
        return "success";
    }
}

3. 运行

启动 Spring Boot,访问:

复制代码
http://localhost:8080/a2ui/stream

会看到 A2UI 流式 JSON 输出,前端可直接接收渲染。


三、前端渲染 A2UI(3 种任选)

方案 1:原生 JS + SSE(最简单,无框架)

直接复制到 HTML 即可运行

html 复制代码
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>A2UI 渲染</title>
    <style>
        body{padding:20px;max-width:400px;margin:0 auto}
        .row{display:flex;gap:12px;align-items:center;margin:10px 0}
        button{padding:8px 16px;background:#007bff;color:white;border:none;border-radius:4px}
        input{padding:6px;width:100px}
    </style>
</head>
<body>
    <div id="root"></div>

    <script>
        const root = document.getElementById('root');
        const dataModel = { people: 2 };

        // 1. 连接 SSE 接收 A2UI 流
        const es = new EventSource('http://localhost:8080/a2ui/stream');
        es.onmessage = e => {
            const msg = JSON.parse(e.data);
            handleA2UIMessage(msg);
        };

        // 2. 处理 A2UI 消息
        function handleA2UIMessage(msg) {
            if (msg.beginRendering) {
                root.innerHTML = `<div id="${msg.beginRendering.surfaceId}"></div>`;
            }
            if (msg.surfaceUpdate) {
                renderComponents(msg.surfaceUpdate.components);
            }
            if (msg.dataModelUpdate) {
                Object.assign(dataModel, msg.dataModelUpdate.data);
                renderComponents(); // 数据更新重渲染
            }
        }

        // 3. 渲染 A2UI 组件
        function renderComponents(components = []) {
            const surface = document.getElementById('booking');
            if(!surface) return;
            surface.innerHTML = '';

            components.forEach(comp => {
                const key = Object.keys(comp.component)[0];
                const props = comp.component[key];

                if(key === 'Text'){
                    const el = document.createElement('div');
                    el.innerText = props.text;
                    el.style.fontSize = props.style?.fontSize + 'px';
                    el.style.fontWeight = props.style?.fontWeight;
                    surface.appendChild(el);
                }

                if(key === 'Row'){
                    const el = document.createElement('div');
                    el.className = 'row';
                    surface.appendChild(el);
                }

                if(key === 'TextField'){
                    const el = document.createElement('input');
                    el.placeholder = props.placeholder;
                    el.value = dataModel[props.bind.replace('/','')];
                    el.oninput = e => {
                        dataModel.people = e.target.value;
                    };
                    surface.appendChild(el);
                }

                if(key === 'Button'){
                    const el = document.createElement('button');
                    el.innerText = props.text;
                    el.onclick = () => {
                        fetch(`http://localhost:8080/a2ui/action?data=${JSON.stringify(dataModel)}`);
                        alert('提交成功:' + JSON.stringify(dataModel));
                    };
                    surface.appendChild(el);
                }
            });
        }
    </script>
</body>
</html>

方案 2:Vue3 渲染 A2UI(推荐企业级)

vue 复制代码
<template>
  <div id="booking" style="padding:20px;max-width:400px">
    <div v-for="comp in components" :key="comp.id">
      <div v-if="comp.component.Text" :style="comp.component.Text.style">
        {{ comp.component.Text.text }}
      </div>
      <div class="row" v-if="comp.component.Row" style="display:flex;gap:12px;margin:10px 0">
        <component :is="renderChild(id)" v-for="id in comp.component.Row.children" :key="id" />
      </div>
      <input v-if="comp.component.TextField" v-model="dataModel.people" placeholder="输入人数" />
      <button v-if="comp.component.Button" @click="submit">
        {{ comp.component.Button.text }}
      </button>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue';

const components = ref([]);
const dataModel = ref({ people: 2 });

onMounted(() => {
  const es = new EventSource('http://localhost:8080/a2ui/stream');
  es.onmessage = e => {
    const msg = JSON.parse(e.data);
    if(msg.surfaceUpdate) components.value = msg.surfaceUpdate.components;
    if(msg.dataModelUpdate) dataModel.value = msg.dataModelUpdate.data;
  };
});

const submit = () => {
  fetch('http://localhost:8080/a2ui/action?data=' + JSON.stringify(dataModel.value));
  alert('提交:' + JSON.stringify(dataModel.value));
};
</script>

方案 3:React 渲染 A2UI

jsx 复制代码
import { useEffect, useState } from 'react';

function A2uiRender() {
  const [components, setComponents] = useState([]);
  const [data, setData] = useState({ people: 2 });

  useEffect(() => {
    const es = new EventSource('http://localhost:8080/a2ui/stream');
    es.onmessage = e => {
      const msg = JSON.parse(e.data);
      if (msg.surfaceUpdate) setComponents(msg.surfaceUpdate.components);
      if (msg.dataModelUpdate) setData(msg.dataModelUpdate.data);
    };
  }, []);

  const submit = () => {
    fetch('http://localhost:8080/a2ui/action?data=' + JSON.stringify(data));
    alert('提交:' + JSON.stringify(data));
  };

  return (
    <div style={{ padding: 20, maxWidth: 400 }}>
      {components.map(comp => (
        <div key={comp.id}>
          {comp.component.Text && <div style={comp.component.Text.style}>{comp.component.Text.text}</div>}
          {comp.component.Row && <div style={{ display: 'flex', gap: 12, margin: '10px 0' }}></div>}
          {comp.component.TextField && <input value={data.people} onChange={e => setData({...data, people:e.target.value})} />}
          {comp.component.Button && <button onClick={submit}>{comp.component.Button.text}</button>}
        </div>
      ))}
    </div>
  );
}

export default A2uiRender;

四、完整运行流程

  1. 启动 Java Spring Boot 后端
  2. 打开前端 HTML / Vue / React 页面
  3. 前端自动接收 A2UI 流
  4. 界面自动渲染:
    • 标题
    • 人数输入框(默认 2 人)
    • 提交按钮
  5. 点击按钮 → 数据回传给 Java 后端

五、A2UI 常用组件(Java 可直接生成)

json 复制代码
// 文本
{"Text":{"text":"你好"}}

// 按钮
{"Button":{"text":"确认","action":{"type":"submit"}}}

// 输入框
{"TextField":{"bind":"/name"}}

// 布局
{"Row":{"children":["id1","id2"]}}
{"Column":{"children":["id1","id2"]}}

// 卡片
{"Card":{"children":["title","content"]}}

六、学习总结

  • A2UI = 界面协议,不是框架
  • Java 后端:生成 JSONL 流
  • 前端:解析 JSON → 渲染原生界面
  • 优势:安全、跨平台、AI 易生成、体验原生
相关推荐
王莎莎-MinerU1 小时前
Agent 时代,科学数据 API 需要重新设计
大数据·前端·数据库·人工智能·个人开发
程序猿乐锅1 小时前
【MySQL | 第五篇】 MySQL 性能分析:如何查询慢 SQL
java·sql·mysql
jingling5551 小时前
自建技术博客实战(三):工具专栏——地图定位、声音复刻与 rembg 抠图
android·开发语言·前端·ai·nextjs
lee_curry1 小时前
tomcat+springmvc+spring源码流通过程
java·spring·tomcat·springmvc
w1wi1 小时前
【兼职】边学边练的AI网站
java·人工智能·ai·ai编程·ai写作
basketball6161 小时前
C++进阶:1. 引用折叠规则
java·开发语言·c++
404号扳手1 小时前
Java 进阶知识(七)
java·后端
小小小小宇1 小时前
Chrome 插件在新开页生效
前端
橘子味的冰淇淋~1 小时前
优化前端性能之从“全局引入”改为“按需引入”
前端·javascript·vue.js