news 2026/9/13 13:40:33

CopilotKit 开放生成式 UI(高级):在沙箱 iframe 中调用宿主端沙箱函数

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CopilotKit 开放生成式 UI(高级):在沙箱 iframe 中调用宿主端沙箱函数

CopilotKit 开放生成式 UI(高级):在沙箱 iframe 中调用宿主端沙箱函数

【免费下载链接】CopilotKitThe Frontend Stack for Agents & Generative UI. React, Angular, Mobile, Slack, and more. Makers of the AG-UI Protocol项目地址: https://gitcode.com/GitHub_Trending/co/CopilotKit

导读

本文围绕 CopilotKit 与 CrewAI Conversational Flows 集成项目中的open-gen-ui-advanced(开放生成式 UI 高级版)Demo 展开,讲解"完全开放"的生成式 UI 模式:Agent 直接生成一段 HTML/CSS/JavaScript 并渲染进沙箱 iframe,同时通过宿主端注册的沙箱函数(sandbox functions)与页面本体完成双向交互。读完本文,你将掌握openGenerativeUI运行时标志与前端 Provider 配置、宿主沙箱函数的注册与调用协议(Websandbox.connection.remote.*),以及如何用 E2E 测试验证"iframe 内点击 → 宿主端执行 → 结果回写 iframe"的完整链路。

一、场景定位:从"受约束渲染"到"完全开放生成"

在 CopilotKit 的生成式 UI 体系中,open-gen-ui-advanced属于"开放生成式 UI"(Open Generative UI)一档。与基于预定义组件库或 A2UI Schema 的受约束模式不同,这种模式下的 Agent 拥有极大自由度:它直接产出可运行的 HTML/CSS/JavaScript,前端将其安全地渲染在一个沙箱 iframe 中。而"高级"(Advanced)版本在open-gen-ui基础之上更进一步——Agent 生成的 iframe 内 UI 可以反向调用宿主页面注册的沙箱函数,形成"宿主 ↔ 沙箱"的双向桥接。

该能力在 manifest.yaml 中被登记为独立 featureopen-gen-ui-advanced,其描述为 "Agent-authored UI that invokes host sandbox functions from inside the iframe"(Agent 编写的 UI 从 iframe 内部调用宿主沙箱函数),highlight 文件为:

  • page.tsx
  • sandbox-functions.ts
  • copilotkit-ogui 路由

二、QA 验收清单与整体数据流

仓库中的 QA 文档 open-gen-ui-advanced.md 给出了该功能的手工验收步骤:

  1. 访问/demos/open-gen-ui-advanced页面;
  2. 点击 "Calculator (calls evaluateExpression)" 建议词;
  3. 验证沙箱化计算器 UI 在 iframe 中渲染;
  4. 操作计算器(如计算3 + 4.5),验证结果由宿主端evaluateExpression更新;
  5. 点击 "Ping the host (calls notifyHost)",验证按钮触发远程调用并在沙箱中显示时间戳。

背后对应的完整数据流为:

  1. 用户点击建议词 → 聊天组件将消息发送给 Agent(CrewAI Conversational Flow);
  2. Agent 流式返回一个generateSandboxedUi工具调用,其中携带 HTML/CSS 以及通过jsFunctions声明的 in-iframe 点击处理器;
  3. 运行时的OpenGenerativeUIMiddleware(由openGenerativeUI: { agents: [...] }开启)将该工具调用流转换为open-generative-ui活动事件;
  4. 前端CopilotKitProvider 传入openGenerativeUI配置后,内置的OpenGenerativeUIActivityRenderer将 Agent 编写的 HTML/CSS 挂载进带sandbox属性的 iframe;
  5. iframe 内 UI 通过Websandbox.connection.remote.<name>(args)调用宿主端沙箱函数,宿主执行完成后把返回值回传给 iframe 内调用方。

这一流程的代码级注释记录在 page.tsx 的头部注释中。

三、后端运行时:为开放生成式 UI 单独开一条路由

openGenerativeUI是运行时(Runtime)级别的标志,一旦开启会在 probe 响应中全局设置openGenerativeUIEnabled: true,这会影响默认运行时上的其他按 Demo 工具注册。因此仓库为开放生成式 UI Demo 专门隔离了一条运行时路由 copilotkit-ogui/route.ts。

该路由的关键配置如下:

import { CopilotRuntime, createCopilotRuntimeHandler } from "@copilotkit/runtime/v2"; import { HttpAgent } from "@ag-ui/client"; const AGENT_URL = process.env.AGENT_URL || "http://localhost:8000"; function createAgent() { return new HttpAgent({ url: `${AGENT_URL}/conversational_flows/frontend-tools`, }); } const agents: Record<string, AbstractAgent> = { "open-gen-ui": createAgent(), "open-gen-ui-advanced": createAgent(), }; export const POST = async (req: NextRequest) => { const copilotHandler = createCopilotRuntimeHandler({ runtime: new CopilotRuntime({ agents, openGenerativeUI: { agents: ["open-gen-ui", "open-gen-ui-advanced"], }, }), basePath: "/api/copilotkit-ogui", mode: "single-route", }); return await copilotHandler(req); };

要点说明:

  • openGenerativeUI.agents数组用于声明哪些 Agent 允许产出开放生成式 UI;这里open-gen-uiopen-gen-ui-advanced两个 Agent 都映射到同一个 CrewAI 端点/conversational_flows/frontend-tools
  • mode: "single-route"让该路由独立承载所有运行时请求,避免与默认路由互相干扰;
  • 从源码结构可以推断,运行时中OpenGenerativeUIMiddleware负责把generateSandboxedUi工具调用流转换为open-generative-ui活动事件,供前端内置渲染器消费。

在 Python 侧,/frontend-tools端点由FrontendToolFlow提供,conversational_flows.py 中可以看到"frontend-tools": _conversational_type(FrontendToolFlow)的映射注册;Python 测试 test_specialized_flows.py 中也登记了"open-gen-ui-advanced": "/frontend-tools"的对应关系。

四、前端集成:Provider 上的openGenerativeUI与建议词

前端页面 page.tsx 通过CopilotKitProvider 的openGenerativeUI属性把沙箱函数数组交给内置渲染器:

"use client"; import { CopilotKit, CopilotChat, useConfigureSuggestions } from "@copilotkit/react-core/v2"; import { openGenUiSandboxFunctions } from "./sandbox-functions"; import { openGenUiSuggestions } from "./suggestions"; export default function OpenGenUiAdvancedDemo() { return ( <CopilotKit runtimeUrl="/api/copilotkit-ogui" agent="open-gen-ui-advanced" openGenerativeUI={{ sandboxFunctions: openGenUiSandboxFunctions }} > <div className="flex justify-center items-center h-screen w-full"> <div className="h-full w-full max-w-4xl"> <Chat /> </div> </div> </CopilotKit> ); } function Chat() { useConfigureSuggestions({ suggestions: openGenUiSuggestions, available: "always", }); return ( <div className="flex h-full w-full flex-col p-3"> <CopilotChat agentId="open-gen-ui-advanced" className="flex-1 rounded-2xl" /> </div> ); }

三个关键点:

  • runtimeUrl="/api/copilotkit-ogui"指向第三节专门隔离的运行时路由;
  • openGenerativeUI={{ sandboxFunctions: [...] }}将宿主端沙箱函数数组注入 Provider,内置的OpenGenerativeUIActivityRenderer会把这些函数作为可在 iframe 内调用的远端对象挂载进沙箱;
  • useConfigureSuggestions用于在聊天输入区常驻建议词(available: "always")。

建议词定义在 suggestions.ts,共三条:

export const openGenUiSuggestions = [ { title: "Calculator", message: "Calculator (calls evaluateExpression)" }, { title: "Ping the host", message: "Ping the host (calls notifyHost)" }, { title: "Inline expression evaluator", message: "Inline expression evaluator" }, ];

每条建议词的message字符串会原样作为 Agent 提示词,同时也作为 aimock 固定夹具(fixture)的键;在showcase/aimock/d5-all.json中存在与这些消息字符串逐字对齐的夹具条目,以保证每次点击建议词都会触发确定性的generateSandboxedUi工具调用,而不是被通用兜底夹具吸收。

五、宿主沙箱函数:定义、安全校验与调用协议

沙箱函数是"高级"模式的灵魂,定义在 sandbox-functions.ts。每个函数包含namedescriptionparameters(Zod Schema)和handler四个部分;名称、描述与由 Zod 推导出的 JSON Schema 会被注入 Agent 上下文,让 LLM 在生成 HTML/JS 时知道有哪些桥可用。每个 handler 运行在宿主页面上,其返回值会被 iframe 内的调用方 await。

5.1 evaluateExpression:宿主端安全求值

{ name: "evaluateExpression", description: "Safely evaluate a basic arithmetic expression on the host page and return the numeric result. " + "Supports +, -, *, /, parentheses, and decimal numbers. " + "Use this from inside a calculator or spreadsheet UI.", parameters: z.object({ expression: z.string().describe("An arithmetic expression, e.g. '12 * (3 + 4.5)'"), }), handler: async ({ expression }: { expression: string }) => { if (!/^[\d+\-*/().\s]+$/.test(expression)) { return { ok: false, error: "Unsupported characters in expression." }; } try { const value = Function(`"use strict"; return (${expression});`)(); if (typeof value !== "number" || !Number.isFinite(value)) { return { ok: false, error: "Not a finite number." }; } console.log("[open-gen-ui/advanced] evaluateExpression", expression, "=", value); return { ok: true, value }; } catch (err) { return { ok: false, error: err instanceof Error ? err.message : String(err) }; } }, }

安全实现要点:

  • 先用正则/^[\d+\-*/().\s]+$/白名单过滤,仅允许数字、四则运算符、括号、小数点和空白,任何含标识符或可疑字符的表达式直接返回ok: false绝不执行任意 JS
  • 通过Function("use strict"; return (...))求值,并对结果做typeof === "number"Number.isFinite双重校验;
  • 返回统一结构{ ok, value }{ ok: false, error },便于 iframe 内 UI 消费。

5.2 notifyHost:从沙箱向宿主发送通知

{ name: "notifyHost", description: "Send a short notification message from the sandboxed UI to the host page. " + "The host logs the message and returns a confirmation object.", parameters: z.object({ message: z.string().describe("A short status message."), }), handler: async ({ message }: { message: string }) => { console.log("[open-gen-ui/advanced] notifyHost:", message); return { ok: true, receivedAt: new Date().toISOString(), message }; }, }

该函数演示了"沙箱 → 宿主"的通知模式:宿主仅记录消息并返回receivedAt时间戳(ISO 字符串),iframe 内 UI 拿到这个确认对象后即可把时间戳渲染出来——这正好对应 QA 清单第 5 步"时间戳显示在沙箱中"。

5.3 调用协议

两个 handler 的函数头注释明确了调用方式:Agent 生成的 iframe 内脚本通过Websandbox.connection.remote.<name>(args)调用这些宿主函数(源码见 sandbox-functions.ts)。由于函数名、描述和 Schema 都被注入 Agent 上下文,LLM 生成 HTML 时会把按钮的点击事件正确绑定到对应远端调用上。从设计上看,这类函数的接口应保持"小而明确"——它们就是 Demo 的"应用侧工具"(app-side tools),供沙箱生成的 UI 调用。

六、E2E 验证:如何证明"iframe 内点击 → 宿主执行 → 结果回写"

仓库为高级开放生成式 UI 提供了完整的 Playwright 测试 open-gen-ui-advanced.spec.ts,这也是理解整条链路的最好"活文档"。测试分为两层断言:

6.1 冒烟层(SMOKE)

每个提示词(Calculator / Ping the host / Inline expression evaluator)都断言产生一个srcdoc非空的iframe[sandbox*="allow-scripts"],确认 open-gen-ui 管线成功挂载了内容。测试通过frameLocatorsandbox="allow-scripts"iframe 交互(依赖 CDP,不受 null origin 限制)。

6.2 往返层(ROUND-TRIP)

驱动 iframe 内控件,断言宿主端 handler 确实执行(通过宿主 console 中稳定的[open-gen-ui/advanced]前缀日志捕获)且 iframe 内输出元素被宿主返回值更新:

  • Ping 测试:点击 iframe 内#hi按钮 → 宿主notifyHost被触发 → 断言 iframe#out元素包含 "host replied at";
  • 内联表达式求值测试:向#in输入框pressSequentially("2+2")(注释说明:fill()allow-scripts沙箱 iframe 内可能静默失效,按键序列总是可达)→ 点击#go→ 断言宿主日志出现= 4#out显示 "= 4";
  • 计算器测试:依次点击数字2、运算符+、数字3按钮再点=#eq)→ 断言宿主日志出现= 5且显示区#d文本为 "5"。

测试头部注释还透露了两个工程细节:

  • 驱动采用"textarea 填充 + 回车"而非点击建议词 pill,因为页面存在 EmptyState 与 SuggestionBar 两套 chip 表面(依赖messages.length挂载),pill 点击存在时序抖动;两条路径最终都会触发同一个runAgent
  • d5-all.json 夹具在 feature-parity.json 之前加载,其 first-match-wins 顺序能命中精确的建议词消息,避免落入会返回通用问候语的userMessage: "hi"兜底夹具。

6.3 测试要点速查

测试项操作路径断言
页面加载访问/demos/open-gen-ui-advanced3 个建议词 pill 可见
冒烟:三条提示词填充输入框并回车iframe[sandbox*="allow-scripts"]可见且srcdoc/src非空
Ping 往返iframe 内点击#hi宿主日志含notifyHost#out含 "host replied at"
内联求值往返#in输入2+2,点击#go宿主日志= 4#out含 "= 4"
计算器往返依次点2+3#eq宿主日志= 5#d文本为 "5"

七、与基础版(open-gen-ui)的对照

基础版 open-gen-ui 页面 与高级版共用同一条/api/copilotkit-ogui运行时路由(两者都列在openGenerativeUI.agents中)。两者的本质区别在于:

  • 基础版:Agent 只生成静态 HTML/CSS/可视化内容,渲染进沙箱 iframe 即可,不涉及宿主回调;其 design-skill.ts 通过向 Agent 注入视觉设计技能(如"像教科书插图或可探索解释一样")来约束产出质量;
  • 高级版:在基础能力之上注册宿主沙箱函数,让 iframe 内 UI 拥有"对外部世界的副作用能力"(求值、通知等),把沙箱从"展示容器"升级为"可交互应用宿主"。

两者共同的约束是 iframe 安全模型:Agent 产出的 HTML 不包含<form>、不使用type='submit',事件一律通过addEventListener绑定(见 suggestions.ts 注释),这些约束会写进系统提示词中。

八、运行与验证方式

该 Demo 位于 CrewAI Conversational Flows 集成 showcase 中(manifest.yaml 登记的 slug 为crewai-conversational-flowscopilotkit_version: 2.0.0,后端语言 Python,Agent 端点为/conversational_flows/frontend-tools)。典型运行方式为:

  1. 启动 Python 后端(CrewAI Conversational Flows,默认监听http://localhost:8000,可用AGENT_URL环境变量覆盖);
  2. 启动 Next.js 前端,访问/demos/open-gen-ui-advanced
  3. 手工按 QA 清单验证,或运行tests/e2e/open-gen-ui-advanced.spec.ts进行自动化回归。

需要说明的适用前提:openGenerativeUI为运行时级全局标志,开启会影响 probe 响应,因此仓库将其隔离在独立路由/api/copilotkit-ogui上,避免干扰默认运行时中按 Demo 的工具注册——在自己项目中接入该能力时,建议沿用同样的路由隔离策略。

结语

open-gen-ui-advanced展示了生成式 UI 的一个关键进阶:Agent 自由生成界面,而宿主通过注册"沙箱函数"为其提供受控的能力边界。evaluateExpression的白名单正则与有限性校验、notifyHost的简单确认回执,加上 E2E 测试对"宿主日志 + iframe 输出"的双重断言,共同勾勒出一条清晰、可验证、可复用的"开放生成 + 沙箱桥接"实践路径。

【免费下载链接】CopilotKitThe Frontend Stack for Agents & Generative UI. React, Angular, Mobile, Slack, and more. Makers of the AG-UI Protocol项目地址: https://gitcode.com/GitHub_Trending/co/CopilotKit

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 13:38:25

多Agent系统可观测性实战:从埋点设计到故障排查的完整方法论

我们团队最近把三条业务线全部迁到了基于大模型的多Agent协作架构上&#xff0c;系统跑起来的那一瞬间&#xff0c;所有人都很兴奋。但等兴奋劲过了&#xff0c;真正让人头疼的事情才刚开始&#xff1a;系统在测试环境一切正常&#xff0c;一到生产环境就开始“抽风”&#xff…

作者头像 李华
网站建设 2026/9/13 13:37:46

基于1D CNN的锂电池故障诊断:从1×9特征输入到工程部署

简介&#xff1a;一份面向深度学习与电池健康管理研究者的CNN故障诊断示例资源&#xff0c;聚焦电池不一致性故障场景&#xff0c;展示如何用卷积神经网络对19维电池采集数据进行特征提取与状态分类&#xff0c;适合初学者对照学习卷积层、池化、Softmax等模块实现。包内共12个…

作者头像 李华
网站建设 2026/9/13 13:36:41

垃圾分选设备行业现状与技术发展趋势

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华