前天 Anthropic 在 Claude 里面上線了基于生成式 UI 的新交互。
可以幫你在聊天信息流里面用地嗎可視化的方式介紹一些概念和信息,遠(yuǎn)比原來的純文本要好理解。
![]()
我之前就一直在看類似的方案,剛好 Claude 發(fā)了,我就感覺我也得加緊做了。
同時(shí)剛好也可以逆向參考一下他的方案。
瘋狂 PUA 了兩天 Codex 和 Claude 還真讓我搞出來了!
![]()
這個(gè)功能能讓 AI 直接在聊天里畫交互式圖表,流式輸出,邊生成邊渲染。
以前讓 AI 寫網(wǎng)頁,得等整個(gè)頁面代碼全部生成完才能渲染,等半天。
現(xiàn)在不一樣了。
你能看著圖表一筆一筆在畫布上畫出來,SVG 節(jié)點(diǎn)一個(gè)接一個(gè)冒出來。
生成過程本身就很震撼,而且生成完直接就能交互。
直接在我的 Agent 產(chǎn)品 Code Pilot 里面體驗(yàn):https://github.com/op7418/CodePilot
這篇內(nèi)容我就會(huì)介紹一下它的用法,以及具體的實(shí)現(xiàn)過程和一些注意事項(xiàng)。
有哪些好玩的用法
數(shù)據(jù)分析:數(shù)字終于能看懂了
比如讓它畫一個(gè)"美國和伊朗沖突每天成本估算"的圖表。
以前 AI 給你一大段文字,數(shù)字關(guān)系根本看不清。
現(xiàn)在直接出圖表,每部分金額多少一目了然,文字和圖表混在一起輸出,該解釋的解釋,該畫的畫。
![]()
![]()
小工具:寫個(gè)可交互的計(jì)算器啥的
讓它做一個(gè)復(fù)利計(jì)算器。
拖滑塊改初始金額、改投資年限,下面的圖表和數(shù)字實(shí)時(shí)變化。
這不是靜態(tài)圖片,是真的能交互的小工具。
貸款計(jì)算、單位換算這類東西都能做。
![]()
架構(gòu)圖:程序員最愛
你可以讓他幫你畫某個(gè)項(xiàng)目的架構(gòu),或者某一個(gè)實(shí)現(xiàn)方案的可視化。
比如這里我讓它畫 API 到 JWT 身份驗(yàn)證的完整流程。
特性對(duì)比、流程圖、層級(jí)結(jié)構(gòu)全是圖形化的,比看文字描述理解架構(gòu)快太多了。
![]()
分析線上數(shù)據(jù)
還有個(gè)玩法直接丟一個(gè) GitHub 倉庫鏈接給它,它自己抓數(shù)據(jù)然后可視化分析。
比如這里我就把我自己的項(xiàng)目地址 Codepilot 發(fā)給他讓他分析。
星數(shù)、fork 數(shù)、技術(shù)棧、架構(gòu)設(shè)計(jì)、核心模塊,全部畫成圖表。
一眼就能看清楚項(xiàng)目全貌,比讀一大段文字強(qiáng)多了。
![]()
可以進(jìn)行交互和深度解釋
這個(gè)最強(qiáng)的是他跟模型結(jié)合的相當(dāng)緊密,不是一頓輸出就完事了。
你可以跟他生成的示意圖進(jìn)行交互,讓他進(jìn)行更詳細(xì)的解釋。
比如這里我讓他解釋季風(fēng)和洋流的關(guān)系。
![]()
如果我們想更詳細(xì)的了解就可以點(diǎn)擊那個(gè)洋流機(jī)制的按鈕。
就會(huì)自動(dòng)向當(dāng)前的模型發(fā)送指令,繼續(xù)幫你生成洋流機(jī)制的示意圖。
![]()
當(dāng)然我們可以進(jìn)行更加復(fù)雜的交互,比如常見的物理數(shù)學(xué)公式的可視化。
這種對(duì)于學(xué)生來說非常好用,每個(gè)參數(shù)都可以通過滑塊和輸入控制,動(dòng)畫立刻會(huì)發(fā)生變化。
![]()
國產(chǎn)模型支持
Codepilot 實(shí)現(xiàn)之后不只是 Claude 能用。
Kimi K2.5、Minimax M2.5、Anthropic 原生模型都跑得起來。
K2.5 畫的圖形我覺得甚至比 Sonnet 4.6 還好看,架構(gòu)分析也很詳細(xì)。
如果用這個(gè)功能我推薦首選 K2.5 試試。
好,到這里,模型的玩法基本上展示完了。
如果你不關(guān)心是如何實(shí)現(xiàn)的,可以直接去裝個(gè) Codepilot,愉快地玩耍了。
如何實(shí)現(xiàn)的
![]()
Claude 怎么做的
Claude.ai 官方用的是 tool_use 機(jī)制。
模型調(diào)用一個(gè)專用 tool 輸出結(jié)構(gòu)化的 widget 內(nèi)容,
前端解析 tool 調(diào)用的 input 參數(shù)來渲染。
這個(gè)方案在 Claude.ai 自己的架構(gòu)下沒問題。
但搬到 CodePilot 就不行了,原因有三個(gè):
第一,SDK 限制。
CodePilot 用 Claude Agent SDK 的 preset: 'claude_code' 模式,
沒法注冊(cè)自定義 tool。
SDK 暴露的是 text delta 流,tool 層面擴(kuò)展不了。
第二,流式體驗(yàn)。
tool_use 的結(jié)果要等 input_json_delta 拼完才能渲染,
不支持 HTML 增量渲染。
代碼圍欄方式下,HTML 隨文本流式到達(dá),邊生成邊預(yù)覽。
第三,渲染隔離。
Claude.ai 用 Shadow DOM 做隔離。
我們選了 sandbox iframe。
iframe 隔離更徹底——完全獨(dú)立的 JS 執(zhí)行環(huán)境,
CSP 精確控制資源加載,
不存在樣式泄漏和腳本逃逸。
我們?cè)趺醋龅?/p>
觸發(fā):代碼圍欄
模型輸出一段特殊的 Markdown 代碼圍欄來觸發(fā)渲染:
show-widget
{"title":"training_flow","widget_code":"
..."}
這個(gè)格式復(fù)用了 CodePilot 已有的代碼圍欄模式
(image-gen-request、batch-plan 等),
前端 parser 鏈天然支持。
![]()
渲染:sandbox iframe
每個(gè) widget 渲染在一個(gè) sandbox="allow-scripts" 的 iframe 里。
iframe 的 srcdoc 是一個(gè)精心構(gòu)建的 receiver 頁面。
CSP 策略只放行 4 個(gè) CDN 域名的外部腳本,
connect-src 'none' 禁止所有網(wǎng)絡(luò)請(qǐng)求。
通過 postMessage 接收內(nèi)容更新。
流式預(yù)覽階段發(fā) widget:update,不執(zhí)行腳本。
最終渲染發(fā) widget:finalize,執(zhí)行腳本。
ResizeObserver 監(jiān)聽內(nèi)容高度變化,
通過 postMessage 報(bào)告給父頁面。
所有 點(diǎn)擊被攔截,
轉(zhuǎn)發(fā)給父頁面在新窗口打開。
主題同步靠監(jiān)聽父頁面的 class 變化,
實(shí)時(shí)切換深色/淺色模式。
![]()
CSS 變量橋接
這是讓 widget 跟應(yīng)用視覺融合的關(guān)鍵。
CodePilot 用 OKLCH 色彩空間的 CSS 變量。
Anthropic 的 widget 設(shè)計(jì)指南用 --color-background-primary 這類標(biāo)準(zhǔn)變量名。
橋接層在 iframe 初始化時(shí),
把 CodePilot 的變量值注入 iframe 的 :root。
模型按指南寫的 CSS 就能直接用當(dāng)前主題的顏色。
深色模式切換時(shí),
父頁面檢測到 class 變化,
重新算變量值推給 iframe。
![]()
流式渲染
這是整個(gè)實(shí)現(xiàn)里最復(fù)雜的部分。
模型逐 token 生成。
任意時(shí)刻收到的 widget 代碼都可能是不完整的 JSON、
不完整的 HTML、不完整的 還沒到時(shí),
sanitizeForStreaming 剝離了開標(biāo)簽,
但標(biāo)簽內(nèi)的 JavaScript 代碼變成了裸文本節(jié)點(diǎn),
被瀏覽器渲染成可見內(nèi)容。
修復(fù):在 StreamingMessage 的 partial code 提取后,
檢測最后一個(gè) 有沒有匹配的 。
沒有就在 位置截?cái)唷?br/>widget 指南規(guī)定 script 放最后,截?cái)嗖挥绊懸曈X內(nèi)容。
截?cái)嗥陂g展示 shimmer 遮罩,
狀態(tài)欄顯示"正在為可視化添加交互動(dòng)畫"。
iframe Ready 競態(tài)
極少數(shù)情況下 widget 完全不渲染,停在 0px 高度。
WidgetRenderer 通過 useEffect 注冊(cè) message 事件監(jiān)聽。
但 iframe 的 receiver script 加載完就立刻發(fā) widget:ready。
如果 iframe 加載速度快于 React effect 執(zhí)行,
widget:ready 在監(jiān)聽器注冊(cè)之前就發(fā)出去了,
iframeReady 永遠(yuǎn)不會(huì)變成 true。
修復(fù):在 iframe 元素上加 onLoad 回調(diào)兜底。
onLoad 觸發(fā)時(shí) receiver script 必然已執(zhí)行完,
是可靠的就緒信號(hào)。
React 組件樹穩(wěn)定性
widget 在圍欄閉合瞬間閃一次。
兩個(gè)問題疊在一起:
流式 partial widget 沒有 React key,
閉合后獲得 key="w-0",key 變了導(dǎo)致 remount。
shimmer overlay 用外包
實(shí)現(xiàn),
改變了組件樹結(jié)構(gòu),type 變了又導(dǎo)致 remount。
修復(fù):給 partial widget 算穩(wěn)定的 key
(w-N,N 是在最終 segments 數(shù)組中的預(yù)期位置),
跟閉合后的 key 一致。
shimmer overlay 移進(jìn) WidgetRenderer 內(nèi)部,
通過 showOverlay prop 控制。
組件樹始終是 ,不變。
整個(gè)生成式 UI 系統(tǒng),
難的不是"讓一段 HTML 在 iframe 里跑起來"。那很簡單。
真正的復(fù)雜度在于:
讓這個(gè) iframe 在流式傳輸、組件生命周期切換、主題變化這些狀態(tài)轉(zhuǎn)換中,
保持視覺上的穩(wěn)定。
每一個(gè)"閃一下""跳一下""消失一下",
都要去理解 React 的 reconciliation、
瀏覽器的渲染管線、
postMessage 的時(shí)序。
最終效果是:
用戶看到模型的回復(fù)里自然地穿插著圖表和示意圖。
就像它們本來就該在那里。
這是今天的內(nèi)容。如果覺得對(duì)你有幫助的話,可以幫我點(diǎn)個(gè)贊,或者是發(fā)給有需要的朋友。
特別聲明:以上內(nèi)容(如有圖片或視頻亦包括在內(nèi))為自媒體平臺(tái)“網(wǎng)易號(hào)”用戶上傳并發(fā)布,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。
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.