前天 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.