A2UI React Shell 实战:用 React 渲染 A2A 协议流式传输的 Agent UI(Restaurant Finder 示例全解)
2026/9/14 12:22:52 网站建设 项目流程

A2UI React Shell 实战:用 React 渲染 A2A 协议流式传输的 Agent UI(Restaurant Finder 示例全解)

【免费下载链接】a2ui项目地址: https://gitcode.com/GitHub_Trending/a2/a2ui

本指南以 samples/client/react/shell/README.md 为核心,完整讲解如何在 A2UI 仓库中搭建一个 React Shell:它作为前端外壳,通过 Agent-to-Agent(A2A)协议与 Python 编写的 Restaurant Finder Agent 通信,将 Agent 流式返回的 A2UI surfaces 实时渲染为可交互的 Web 界面。读完本文,你将掌握从零启动示例、理解「Vite 中间件代理 → A2A 客户端 → SSE 流解析 → MessageProcessor 渲染」整条链路,并能读懂 shell 的配置项、Mock 模式与安全边界。

一、示例概览:React 前端 + Python Agent 的分工

该示例由一个 React 外壳与一个 Python Agent 组成,二者通过 A2A 协议解耦:

  • React Shell(samples/client/react/shell):纯前端壳,负责输入查询、把用户动作(action)转发给 Agent、并把 Agent 返回的 A2UI 消息解析渲染成界面。它只认识 A2UI 消息协议,不关心 Agent 内部用的是什么 LLM 或框架。
  • Restaurant Finder Agent(samples/agent/adk/restaurant_finder):基于 Google ADK 编写的「餐厅搜索与订座」Agent,作为 A2A 服务器监听localhost:10002,通过 A2A 扩展头声明自己支持 A2UI(X-A2A-Extensions: https://a2ui.org/a2a-extension/a2ui/v0.9,见 middleware/a2a.ts),以application/a2ui+json类型的消息把 UI 定义推送给前端。

从仓库结构看,shell 的依赖充分体现了这一分层:package.json 中同时引用了@a2a-js/sdk(A2A 协议客户端)、@a2ui/react(React 渲染器)、@a2ui/web_core(A2UI 消息处理核心)与@a2ui/markdown-it(Markdown 渲染),React 19 + Vite 8 作为运行时底座。

二、前置条件(Prerequisites)

原文档明确列出三项前置,结合仓库可细化为:

  1. Node.js:用于yarn与 Vite 构建(仓库根目录使用 Yarn 工作区管理多包)。
  2. uv:Python 包管理器,用于启动 Agent(Agent 依赖通过 uv workspace 解析)。
  3. Python:版本要求以 Agent 的 pyproject.toml 中requires-python = ">=3.10"为准。
  4. LLM API Key:Agent 需要 Gemini API Key 才能执行真实查询(见下文第四步的环境变量配置)。

三、运行步骤:4 步启动完整示例

1. 安装并构建依赖

在仓库根目录执行:

yarn install yarn build:all

yarn install会按 Yarn 工作区解析并安装所有子包(React 渲染器、Web Core、Markdown 渲染器等);yarn build:all负责把@a2ui/react等 workspace 包先构建出来,供 shell 引用。shell 自身的构建脚本由 package.json 的wireit.build定义,其dependencies字段显式声明了「必须先构建renderers/react」这一包间依赖关系。

2. 启动 Agent

另开一个终端:

cd samples/agent/adk/restaurant_finder cp .env.example .env # 然后编辑 .env,填入 GEMINI_API_KEY(不要把 .env 提交进仓库) uv run .
  • .env.example 中只有两个可选变量:GEMINI_API_KEY(必填)与GOOGLE_GENAI_USE_VERTEXAI=TRUE(可选,改用 Vertex AI 后端)。
  • Agent 默认监听localhost:10002。可通过 Agent 侧 README 提供的命令自检:curl http://localhost:10002/.well-known/agent-card.json应返回 AgentCard;也可直接发送 JSON-RPC 形式的message/send请求验证消息通路。

3. 启动 React 开发服务器

再开一个终端:

cd samples/client/react/shell yarn dev

Vite 配置 表明:开发服务器固定占用端口5003strictPort: true),并通过a2aPlugin()/a2a路径的请求代理到运行在localhost:10002的 Agent 上。

4. 打开界面

浏览器访问 http://localhost:5003(或点击终端打印的链接),即可在输入框输入如「Top 5 Chinese restaurants in New York」之类的查询(这是 restaurant.ts 中的默认 placeholder),看到 Agent 流式返回的餐厅列表、订座表单与确认页。

四、链路剖析一:Vite 中间件如何桥接 A2A

/a2a代理是整套架构的中枢,实现在 middleware/a2a.ts:

  1. 客户端复用:首次请求时通过A2AClient.fromCardUrl('http://localhost:10002/.well-known/agent-card.json')拉取 AgentCard 并建立 A2A 客户端,此后复用单例;fetchWithCustomHeader为每个请求注入X-A2A-Extensions头,声明本次会话启用了 A2UI v0.9 扩展。
  2. 请求体判定与转发:中间件解析 POST 到/a2a的原始请求体——若为合法 JSON,则视为 UI 事件(用户点击按钮产生的 action),包装成kind: 'data'mimeType: 'application/a2ui+json'的 Part;否则视为纯文本查询,包装成kind: 'text'的 Part。二者都生成随机messageIdrole: 'user'的标准 A2A 消息。
  3. 流式响应(默认开启)ENABLE_STREAMING环境变量不为'false'时启用client.sendMessageStream,把 Agent 的status-updatemessage两类事件中的 parts 逐块写成text/event-streamdata: {...}\n\n格式)转发给浏览器。
  4. 降级与非流式兜底:若关闭流式,则改用sendMessage一次性拿到完整 Task 结果,以 JSON 返回;出错时按「响应头是否已发送」分别返回{error}或流式错误帧。
  5. 防护细节:请求体上限MAX_PAYLOAD_SIZE = 1024 * 1024(1 MB),超限直接返回 413 并销毁请求,防止异常 Shell 耗尽开发服务器内存;同时客户端断开时(res.destroyed)会停止继续向 Agent 拉取数据。

五、链路剖析二:浏览器端的 SSE 流解析(client.ts)

src/client.ts 中的A2UIClient负责把中间件转发来的流解析回 A2UI 消息:

  • 按事件流边界切分:用\r?\n\r?\n切分 SSE 帧,把最后一个不完整块留存在 buffer 中,等待下个数据块拼接。
  • Part 级解析:每帧是一个 Part 数组,逐项判断——kind: 'error'直接抛出错误;kind: 'data'则取出其中的 A2UI 消息。
  • 关键的去重逻辑:A2A 的 status-update 事件携带累积式的 parts,createSurface会在每个 chunk 里重复出现,若不去重会导致 MessageProcessor 抛出「Surface already exists」异常。因此seenSurfaceIds集合会记录已转发过的 surfaceId,重复的 createSurface 直接跳过。
  • 非流式兜底:当响应 Content-Type 不是text/event-stream时走response.json()分支,同样按 Part 数组提取 A2UI 消息。

六、链路剖析三:React 渲染层(App.tsx 与 MessageProcessor)

src/App.tsx 完成了从消息到界面的最后一公里:

  • MessageProcessor 装配:用new MessageProcessor([basicCatalog], action => ...)创建处理器——basicCatalog是 v0.9 基础组件目录,回调中把用户的 action 包装成{version: 'v0.9', action}客户端消息再发回 Agent,实现「按钮点击 → 下一轮 UI」的闭环(见 src/App.tsx)。
  • Surface 生命周期管理:订阅onSurfaceCreated/onSurfaceDeleted,把 surfaces 同步进 React state;sendAndProcess在发起新一轮请求前会清空旧 surfaces。
  • 流式增量渲染client.sendonChunk回调里,每个 chunk 先交给processor.processMessages更新底层 SurfaceModel,再追加进消息列表;A2uiSurface组件通过useSyncExternalStore订阅各自的 surface,因此无需手动触发重渲染。
  • UI 状态机:无消息时显示搜索表单(含 hero 背景与标题);请求中显示转动的 loading 文案;出错时展示错误条;有 surface 时在<section className="surfaces">中逐个渲染<A2uiSurface>
  • 深浅色模式:跟随系统prefers-color-scheme初始化,也可点右上角按钮手动切换。

Mock 模式:不启动 Agent 也能跑通 UI

在 URL 后加?mock=true即进入纯前端演示模式(见 src/App.tsx):getMockResponse根据 action 名称返回模拟消息——book_restaurant生成订座表单、submit_booking生成确认页、默认返回餐厅列表(数据与 mock/restaurantMessages.ts 中的餐厅样例一致),并模拟 800ms 网络延迟。页面左上角会显示Mock Mode徽标。这是快速体验 A2UI 渲染能力、或为前端调试提供稳定数据源的便捷方式。

七、Shell 配置项详解(AppConfig)

Shell 是「通用外壳 + 应用配置」的模式:只需实现 src/configs/types.ts 中的AppConfig接口,即可复用同一外壳承载不同应用(当前注册了restaurant,见 src/configs/index.ts)。完整配置项如下:

配置项类型是否必填说明
keystring应用唯一标识(如'restaurant'
titlestring页面标题,同时写入document.title
placeholderstring输入框占位提示文本
backgroundstring页面背景 CSS(可通过--background自定义变量覆盖)
heroImagestring浅色模式 hero 图片路径
heroImageDarkstring深色模式 hero 图片路径,缺省回退到heroImage
loadingTextstring \| string[]请求中的加载文案;传数组则每 2 秒轮换一次
serverUrlstringAgent 服务器地址(如http://localhost:10002
themeTheme主题覆盖(类型来自@a2ui/react

实际示例见 src/configs/restaurant.ts:title: 'Restaurant Finder'、四段轮换的loadingText,以及一组用light-dark()适配明暗两套配色、由四组径向渐变叠加线性渐变组成的背景。启动时 URL 的app参数决定加载哪个配置(默认restaurant)。

八、安全注意事项(务必阅读)

原文档对安全边界有明确且强制的说明,此处完整继承并强调:

该示例代码仅用于演示 A2UI 与 A2A 协议机制。生产环境中,必须把任何不受你直接控制的 Agent 视为潜在不可信实体

  • 把 Agent 传来的所有运营数据当作不可信输入——包括其 AgentCard、消息、artifacts 与任务状态。恶意 Agent 可以在字段(如nameskills.description)中植入精心构造的数据,若未经净化直接拼接进 LLM 提示词,可能引入提示注入(prompt injection)攻击。
  • 收到的 UI 定义与数据流同样不可信:恶意 Agent 可能伪装合法界面实施钓鱼,通过属性值注入恶意脚本(XSS),或生成极端复杂的布局拖垮客户端性能(DoS)。如果应用支持 iframe、web view 等可选内嵌内容,还需额外防范跳转到恶意外部站点。
  • 开发者责任:未正确校验数据、未严格沙箱化渲染内容都可能引入严重漏洞。开发者必须落实输入净化(input sanitization)、Content Security Policy(CSP)、对内嵌内容的严格隔离以及安全的凭据管理。

九、延伸阅读

  • Agent 侧完整说明与 A2A 消息自检命令:samples/agent/adk/restaurant_finder/README.md
  • A2UI v0.9 协议规范:specification/v0_9/docs/a2ui_protocol.md、specification/v0_9/docs/a2ui_extension_specification.md
  • React 渲染器源码与测试:renderers/react/src/v0_9、renderers/react/tests/v0_9
  • A2UI 与 MCP 应用的集成指南:docs/public/guides/a2ui-in-mcp-apps.md
  • 其他语言/框架的 Shell 实现可对照:samples/client/angular、samples/client/lit

【免费下载链接】a2ui项目地址: https://gitcode.com/GitHub_Trending/a2/a2ui

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询