AI Agent 的回复都是文本,最近尝试局部渲染成富文本,看了谷歌的 A2UI,用了 Copilot 的 AG-UI,试了 Vercel 的 json-render,个人感觉都不尽人意,太重太复杂,而且容易污染和被污染。在 claude.ai/chat 问了 Claude 是如何解决的,感觉是一个不错的方向。
技术栈
公开确认的部分(来自对 Anthropic 团队的访谈以及对生产环境代码的逆向工程):
- 前端壳层:React 应用,部署在
claude.ai。聊天界面通过 SSE(服务器推送事件)流式接收模型的 token。 - Markdown 层:标准 markdown 直接在聊天中行内渲染 —— 代码块、表格、列表、链接、数学公式(KaTeX 风格)。
- 制品 / 可视化沙箱:每个富格式输出都在一个具有完整站点进程隔离的沙箱 iframe 中渲染。这能保护用户主 Claude.ai 浏览会话免受恶意制品影响。同时还启用了严格的内容安全策略(CSP),用以限制和管控网络访问。
- 代码执行路径:使用了一个名为 React Runner 的库来渲染动态 React 代码 —— 它允许 JSX 字符串在运行时编译并渲染,无需构建步骤。
- 通信机制:父页面与 iframe 之间通过
window.postMessage()通信。iframe 位于独立源,无法触及父页面的会话。组件包含加载状态和错误状态(用于内容加载超时),以及一个守卫机制,确保组件运行在 iframe 内。如果不是,会重定向到父源。 - CDN 白名单:沙箱内只能访问少数 CDN(cdnjs、esm.sh、jsdelivr、unpkg)—— 这就是为什么 Chart.js、D3、Three.js 这些库能用,而其他东西全被拦掉。
- 存储能力(仅限 artifact,不是所有可视化):沙箱暴露了一个
window.storage键值 API,用于跨会话保留状态。 - 源隔离:制品 / 可视化解析的域名与
claude.ai主站不同,这让进程隔离是真隔离,而不是名义上的。
结构视图
输出流程
从你输入消息到渲染好的图表出现,中间到底发生了什么:
整套架构
整套架构都被一个核心约束塑造:模型可以生成可执行代码,而这些代码必须在用户能看到的地方运行,但绝不能危及用户会话。三个设计选择都从这里派生:
第一是 独立源的 iframe。因为 iframe 部署在与 claude.ai 不同的域名上,浏览器的同源策略加上完整站点进程隔离,意味着即便模型发出恶意 JavaScript,它也无路径触达你的认证 Cookie、其他标签页或对话历史。
第二是 CSP 白名单。沙箱不能任意访问互联网 —— 只有少数 CDN 可达。这阻止了数据外泄("把用户输入发到 attacker.com"),也避免 iframe 成为通往别处的跳板。这也是为什么有时候这些可视化加载不出某个库 —— 不在白名单里,请求会静默失败。
第三是 运行时编译 React Runner。大多数 React 应用在构建时编译 JSX —— 但模型只在你发送提示之后才生成 JSX 文本,根本没有构建步骤可用。React Runner 接收一段 JSX 字符串,在浏览器中即时编译并渲染。这就是聊天消息能在几百毫秒后变成可工作组件的魔法。
其它
有几件事我刻意没断言,因为没有公开记录:
- Anthropic 聊天 API 的具体后端服务
- iframe 源是否对所有用户共享,以及
- postMessage 协议的精确格式。
上面的架构是公开确认过的轮廓,具体实现细节仍留在 Anthropic 内部。