返回博客

OpenClaw 源码解读(5): Live Canvas 与 A2UI 渲染机制揭秘

2026年3月13日

深入 OpenClaw 源码的 vendor/a2ui 目录,拆解 Live Canvas 实时画布与 A2UI 引擎的四大核心指令(Push、Reset、Eval、Snapshot),看 AI Agent 如何突破纯文本,直接在用户屏幕上渲染交互式 UI。


在前面的四篇文章里,我们一直在和“文本”打交道:怎么收发消息、怎么路由分发、怎么把长文本切块。但你有没有觉得,纯文本的交互形式,很多时候是非常低效的?

试想一下,如果你让 AI 帮你对比三款手机的参数。纯文本 AI 会给你生成一段密密麻麻的 Markdown 表格;但一个理想的个人助理,应该直接在屏幕上给你弹出一个精美的对比图表,甚至带有可以点击的高亮按钮。

这就是 OpenClaw 引入 Live Canvas(实时画布) 和 A2UI(Agent-to-UI)引擎 的初衷。今天,老金就带大家翻开源码的 vendor/a2ui 目录,看看这只“龙虾”是如何在你的屏幕上作画的。

Live Canvas:由 Agent 驱动的视觉工作区

在 OpenClaw 的设计理念中,Live Canvas 被定义为 “由 Agent 驱动的视觉工作区(Agent-driven visual workspace)”

传统的 Web 开发是:程序员写好 UI 组件 -> 部署上线 -> 用户点击交互。

而 A2UI 的模式是:Agent 根据当前对话的上下文,实时编写/拼装 UI 组件 -> 推送到用户的屏幕上 -> 用户与生成的 UI 交互 -> 交互结果回传给 Agent。

这就相当于给 Agent 分配了一块屏幕专属区域。在这块区域里,Agent 不再只能“说话”,它还可以“展示”。这种机制让 OpenClaw 的 WebChat 客户端和移动端节点,从一个单纯的聊天器,变成了一个动态的超级 App(Super App)容器。

A2UI 宿主引擎:前端的“木偶线”

要让 Agent 控制前端,就必须有一套标准化的通信协议。在 OpenClaw 中,这套协议运行在我们之前讲过的 Gateway WebSocket 之上。

我们来看看前端宿主引擎(Host Engine)是如何监听和处理这些视觉指令的。

// ui/src/engine/A2UIHost.ts 

export interface A2UIPayload {
  action: 'push' | 'reset' | 'eval' | 'snapshot';
  content?: string;     // UI 组件代码或数据
  script?: string;      // 需要执行的 JS 脚本
  componentId?: string; // 目标组件 ID
}

export class A2UIHost {
  // 监听来自网关的 A2UI 指令
  public handleIncomingInstruction(payload: A2UIPayload) {
    switch (payload.action) {
      case 'push':
        this.renderComponent(payload.content, payload.componentId);
        break;
      case 'reset':
        this.clearCanvas();
        break;
      case 'eval':
        this.evaluateScript(payload.script);
        break;
      case 'snapshot':
        this.takeAndUploadSnapshot();
        break;
    }
  }
}

如源码所示,A2UI 的核心动作被精简为了四个原语:推送(push)、重置(reset)、求值(eval)与快照截图(snapshot)。我们来逐一拆解。

核心指令源码级拆解

增量渲染:Push 与 Reset

当 Agent 决定展示一个图表时,它不会发送普通的聊天消息,而是会调用特定的 Tool,生成一段特定的结构化 UI 代码(比如基于某种微前端协议或受限的 React/HTML 结构),然后触发 push。

// agent 端的视角:调用 A2UI 工具
async function showWeatherChart(data: any) {
  const uiCode = generateWeatherCard(data);
  
  // 通过网关向当前绑定的 Session 客户端发送 push 指令
  await gateway.sendA2UICommand(currentSessionId, {
    action: 'push',
    content: uiCode,
    componentId: 'weather_widget_01'
  });
}

前端收到 push 后,会将其挂载到 Canvas 的 DOM 树上。如果 Agent 发现话题变了,原来的天气卡片不需要了,它就会发送 reset 指令,前端直接清空 Canvas 区域,恢复为白板状态。

动态逻辑:Eval (求值)

光有静态的 UI 还不够,有时候 Agent 需要在客户端执行一些前端逻辑(例如:启动一个倒计时、播放一段特定的动画、或者监听某个按钮的点击)。

eval 指令允许 Agent 向客户端下发一段受限的 JavaScript 脚本。

// ui/src/engine/A2UIHost.ts (片段)

private evaluateScript(scriptCode: string) {
  try {
    // ⚠️ 生产环境中,这里绝不是简单的 eval()
    // OpenClaw 使用了安全的沙箱环境 (如 iframe 或 Web Worker) 
    // 并注入了受限的 A2UI API 供脚本调用
    const sandboxEnv = this.createSandbox();
    sandboxEnv.execute(scriptCode);
  } catch (err) {
    // 执行失败时,将错误回传给 Agent,让大模型自行 debug 修改代码!
    this.reportErrorToAgent(err.message);
  }
}

让大模型在客户端跑代码听起来很疯狂,但通过极其严格的沙箱隔离(限制 DOM 访问、限制网络请求),这赋予了 Agent 无与伦比的交互灵活性。而且它如果写 bug 报错了,还能自动捕获异常重新生成,这才是真正的 AI 程序员。

闭环的视觉:Snapshot (快照)

这是 Live Canvas 中最画龙点睛的一笔。

Agent 虽然把 UI 推送到了前端,但它自己是个运行在后端的程序,它“看”不到最终渲染出来的效果是否正确。如果排版错乱了怎么办?

为此,A2UI 提供了 snapshot 功能。Agent 可以主动要求前端:“嘿,把现在的 Canvas 截图发给我看看。”

// ui/src/engine/A2UIHost.ts (片段)

import html2canvas from 'html2canvas';

private async takeAndUploadSnapshot() {
  const canvasElement = document.getElementById('a2ui-live-canvas');
  if (!canvasElement) return;

  // 1. 将前端 DOM 渲染为图片
  const canvas = await html2canvas(canvasElement);
  const base64Image = canvas.toDataURL('image/jpeg');

  // 2. 将图片作为视觉输入 (Vision Input) 回传给 Agent
  await this.gatewayClient.uploadVisionContext({
    image: base64Image,
    description: 'Current Live Canvas State'
  });
}

有了 Snapshot,大模型的视觉能力(Vision)就有了用武之地。Agent 可以检查自己生成的图表有没有文字重叠,颜色搭不搭配,从而在下一轮对话中自我修正。

总结

OpenClaw 的 Live Canvas 不是简单地在聊天框里塞入富文本,而是构建了一个完整的 Agent 控制循环: Push/Reset (画图) -> Eval (注入灵魂) -> Snapshot (检查效果)。

通过 vendor/a2ui 这一套精妙的机制,AI 助手彻底打破了第四面墙,直接与用户的屏幕空间产生了实质性的互动。

讲完了 AI 的“画笔”,下一篇,我们将探讨 AI 的“手”和“眼”。敬请期待下一篇,看看 OpenClaw 是如何控制本地浏览器并跨会话协调任务的。我们下期见!