前天 Anthropic 在 Claude 里面上线了基于生成式 UI 的新交互。
可以帮你在聊天信息流里面用地吗可视化的方式介绍一些概念和信息,远比原来的纯文本要好理解。
![]()
我之前就一直在看类似的方案,刚好 Claude 发了,我就感觉我也得加紧做了。
同时刚好也可以逆向参考一下他的方案。
疯狂 PUA 了两天 Codex 和 Claude 还真让我搞出来了!
![]()
这个功能能让 AI 直接在聊天里画交互式图表,流式输出,边生成边渲染。
以前让 AI 写网页,得等整个页面代码全部生成完才能渲染,等半天。
现在不一样了。
你能看着图表一笔一笔在画布上画出来,SVG 节点一个接一个冒出来。
生成过程本身就很震撼,而且生成完直接就能交互。
直接在我的 Agent 产品 Code Pilot 里面体验:https://github.com/op7418/CodePilot
这篇内容我就会介绍一下它的用法,以及具体的实现过程和一些注意事项。
有哪些好玩的用法
数据分析:数字终于能看懂了
比如让它画一个"美国和伊朗冲突每天成本估算"的图表。
以前 AI 给你一大段文字,数字关系根本看不清。
现在直接出图表,每部分金额多少一目了然,文字和图表混在一起输出,该解释的解释,该画的画。
![]()
![]()
小工具:写个可交互的计算器啥的
让它做一个复利计算器。
拖滑块改初始金额、改投资年限,下面的图表和数字实时变化。
这不是静态图片,是真的能交互的小工具。
贷款计算、单位换算这类东西都能做。
![]()
架构图:程序员最爱
你可以让他帮你画某个项目的架构,或者某一个实现方案的可视化。
比如这里我让它画 API 到 JWT 身份验证的完整流程。
特性对比、流程图、层级结构全是图形化的,比看文字描述理解架构快太多了。
![]()
分析线上数据
还有个玩法直接丢一个 GitHub 仓库链接给它,它自己抓数据然后可视化分析。
比如这里我就把我自己的项目地址 Codepilot 发给他让他分析。
星数、fork 数、技术栈、架构设计、核心模块,全部画成图表。
一眼就能看清楚项目全貌,比读一大段文字强多了。
![]()
可以进行交互和深度解释
这个最强的是他跟模型结合的相当紧密,不是一顿输出就完事了。
你可以跟他生成的示意图进行交互,让他进行更详细的解释。
比如这里我让他解释季风和洋流的关系。
![]()
如果我们想更详细的了解就可以点击那个洋流机制的按钮。
就会自动向当前的模型发送指令,继续帮你生成洋流机制的示意图。
![]()
当然我们可以进行更加复杂的交互,比如常见的物理数学公式的可视化。
这种对于学生来说非常好用,每个参数都可以通过滑块和输入控制,动画立刻会发生变化。
![]()
国产模型支持
Codepilot 实现之后不只是 Claude 能用。
Kimi K2.5、Minimax M2.5、Anthropic 原生模型都跑得起来。
K2.5 画的图形我觉得甚至比 Sonnet 4.6 还好看,架构分析也很详细。
如果用这个功能我推荐首选 K2.5 试试。
好,到这里,模型的玩法基本上展示完了。
如果你不关心是如何实现的,可以直接去装个 Codepilot,愉快地玩耍了。
如何实现的
![]()
Claude 怎么做的
Claude.ai 官方用的是 tool_use 机制。
模型调用一个专用 tool 输出结构化的 widget 内容,
前端解析 tool 调用的 input 参数来渲染。
这个方案在 Claude.ai 自己的架构下没问题。
但搬到 CodePilot 就不行了,原因有三个:
第一,SDK 限制。
CodePilot 用 Claude Agent SDK 的 preset: 'claude_code' 模式,
没法注册自定义 tool。
SDK 暴露的是 text delta 流,tool 层面扩展不了。
第二,流式体验。
tool_use 的结果要等 input_json_delta 拼完才能渲染,
不支持 HTML 增量渲染。
代码围栏方式下,HTML 随文本流式到达,边生成边预览。
第三,渲染隔离。
Claude.ai 用 Shadow DOM 做隔离。
我们选了 sandbox iframe。
iframe 隔离更彻底——完全独立的 JS 执行环境,
CSP 精确控制资源加载,
不存在样式泄漏和脚本逃逸。
我们怎么做的
触发:代码围栏
模型输出一段特殊的 Markdown 代码围栏来触发渲染:
show-widget
{"title":"training_flow","widget_code":"
..."}
这个格式复用了 CodePilot 已有的代码围栏模式
(image-gen-request、batch-plan 等),
前端 parser 链天然支持。
![]()
渲染:sandbox iframe
每个 widget 渲染在一个 sandbox="allow-scripts" 的 iframe 里。
iframe 的 srcdoc 是一个精心构建的 receiver 页面。
CSP 策略只放行 4 个 CDN 域名的外部脚本,
connect-src 'none' 禁止所有网络请求。
通过 postMessage 接收内容更新。
流式预览阶段发 widget:update,不执行脚本。
最终渲染发 widget:finalize,执行脚本。
ResizeObserver 监听内容高度变化,
通过 postMessage 报告给父页面。
所有 点击被拦截,
转发给父页面在新窗口打开。
主题同步靠监听父页面的 class 变化,
实时切换深色/浅色模式。
![]()
CSS 变量桥接
这是让 widget 跟应用视觉融合的关键。
CodePilot 用 OKLCH 色彩空间的 CSS 变量。
Anthropic 的 widget 设计指南用 --color-background-primary 这类标准变量名。
桥接层在 iframe 初始化时,
把 CodePilot 的变量值注入 iframe 的 :root。
模型按指南写的 CSS 就能直接用当前主题的颜色。
深色模式切换时,
父页面检测到 class 变化,
重新算变量值推给 iframe。
![]()
流式渲染
这是整个实现里最复杂的部分。
模型逐 token 生成。
任意时刻收到的 widget 代码都可能是不完整的 JSON、
不完整的 HTML、不完整的 还没到时,
sanitizeForStreaming 剥离了开标签,
但标签内的 JavaScript 代码变成了裸文本节点,
被浏览器渲染成可见内容。
修复:在 StreamingMessage 的 partial code 提取后,
检测最后一个 有没有匹配的 。
没有就在 位置截断。
widget 指南规定 script 放最后,截断不影响视觉内容。
截断期间展示 shimmer 遮罩,
状态栏显示"正在为可视化添加交互动画"。
iframe Ready 竞态
极少数情况下 widget 完全不渲染,停在 0px 高度。
WidgetRenderer 通过 useEffect 注册 message 事件监听。
但 iframe 的 receiver script 加载完就立刻发 widget:ready。
如果 iframe 加载速度快于 React effect 执行,
widget:ready 在监听器注册之前就发出去了,
iframeReady 永远不会变成 true。
修复:在 iframe 元素上加 onLoad 回调兜底。
onLoad 触发时 receiver script 必然已执行完,
是可靠的就绪信号。
React 组件树稳定性
widget 在围栏闭合瞬间闪一次。
两个问题叠在一起:
流式 partial widget 没有 React key,
闭合后获得 key="w-0",key 变了导致 remount。
shimmer overlay 用外包
实现,
改变了组件树结构,type 变了又导致 remount。
修复:给 partial widget 算稳定的 key
(w-N,N 是在最终 segments 数组中的预期位置),
跟闭合后的 key 一致。
shimmer overlay 移进 WidgetRenderer 内部,
通过 showOverlay prop 控制。
组件树始终是 ,不变。
整个生成式 UI 系统,
难的不是"让一段 HTML 在 iframe 里跑起来"。那很简单。
真正的复杂度在于:
让这个 iframe 在流式传输、组件生命周期切换、主题变化这些状态转换中,
保持视觉上的稳定。
每一个"闪一下""跳一下""消失一下",
都要去理解 React 的 reconciliation、
浏览器的渲染管线、
postMessage 的时序。
最终效果是:
用户看到模型的回复里自然地穿插着图表和示意图。
就像它们本来就该在那里。
这是今天的内容。如果觉得对你有帮助的话,可以帮我点个赞,或者是发给有需要的朋友。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.