news 2026/9/14 1:55:48

CopilotKit Tool-Based Generative UI:让 Agent 工具返回的结构化数据直接渲染为 React 自定义组件

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CopilotKit Tool-Based Generative UI:让 Agent 工具返回的结构化数据直接渲染为 React 自定义组件

CopilotKit Tool-Based Generative UI:让 Agent 工具返回的结构化数据直接渲染为 React 自定义组件

【免费下载链接】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

导读:Tool-Based Generative UI 是 CopilotKit Generative UI 能力的一种实现路径——Agent 不直接输出 UI 描述,而是调用后端工具获取结构化数据,前端再把该工具的执行结果渲染为定制 React 组件(图表、卡片等),并天然支持加载态与完成态。本文以仓库内 Langroid 集成展示区的gen-ui-tool-basedDemo 为完整案例,结合 demo 页面源码、后端 Agent 实现 与 E2E 测试,带你掌握注册渲染器、定义组件参数 Schema、配置建议提示词(Suggestion)以及前后端联调的完整套路。

一、核心机制:工具调用结果 ≠ 纯文本

在普通 Chat 中,Agent 调用后端工具后,返回值通常会被 LLM 再加工成一段文本回复。Tool-Based Generative UI 改变了这一链条:Agent 调用后端工具 → 工具返回结构化数据(JSON)→ 前端将该结果映射为一个自定义 React 组件直接渲染在对话流中

正如 Demo 的 README 所概括的:

The agent calls a backend tool that returns structured data; the frontend renders that tool result as a custom React component instead of plain text.

也就是说,对话气泡里出现的不是一段"数据说明文字",而是一张真正的柱状图、饼图或业务卡片——数据可视化、富交互都由前端组件完成,LLM 只需负责"决定调用哪个工具、生成什么参数"。

useRenderTool:工具名到渲染器的映射表

README 指出,前端通过useRenderTool把每个工具名(tool name)映射到一个渲染器(renderer),渲染器会收到三样东西:

  • args—— Agent 调用工具时生成的参数;
  • result—— 后端工具返回的结构化结果;
  • status—— 工具调用的生命周期状态。

因为有status的参与,UI 才能区分"正在执行(loading)"与"已完成(complete)"两种呈现,避免用户在等待期间看到空白或过时数据。

实现说明:在当前仓库的 Langroid 集成里,该能力由@copilotkit/react-core/v2useComponent钩子落地(详见下文源码分析),README 中的useRenderTool是对这一机制的上层描述。两者本质一致:注册"工具/组件名 → 渲染组件 + 参数 Schema"。

二、Demo 源码拆解:一条消息如何变成一张图表

1. 页面骨架:CopilotKit+CopilotChat

Demo 页面(page.tsx)结构非常简洁——整个页面就是居中的聊天界面,没有多余框架:

"use client"; import React from "react"; import { CopilotChat, CopilotKit, useComponent, } from "@copilotkit/react-core/v2"; import { BarChart, barChartPropsSchema } from "./bar-chart"; import { PieChart, pieChartPropsSchema } from "./pie-chart"; import { useSuggestions } from "./suggestions"; function Chat() { useComponent({ name: "render_bar_chart", description: "Display a bar chart with labeled numeric values.", parameters: barChartPropsSchema, render: BarChart, }); useComponent({ name: "render_pie_chart", description: "Display a pie chart with labeled numeric values.", parameters: pieChartPropsSchema, render: PieChart, }); useSuggestions(); return ( <div className="flex justify-center items-center h-screen w-full"> <div className="h-full w-full max-w-4xl"> <CopilotChat agentId="gen-ui-tool-based" className="h-full rounded-2xl" /> </div> </div> ); } export default function ControlledGenUiDemo() { return ( <CopilotKit runtimeUrl="/api/copilotkit" agent="gen-ui-tool-based"> <Chat /> </CopilotKit> ); }

关键点:

  • useComponent注册渲染器:每个渲染器都有一个唯一name。Agent 在对话中决定渲染某种 UI 时,就是按这个名字找到对应组件。description帮助 LLM 理解该组件"何时该用",parameters规定组件所需的入参结构。
  • CopilotKitProvider 指定 Runtime 与 AgentruntimeUrl="/api/copilotkit"指向 Next.js 侧的运行时路由;agent="gen-ui-tool-based"是 manifest 与运行时路由中登记的 agent 名。
  • CopilotChat指定agentId:与 Provider 上的agent保持一致,聊天会路由到同一 Agent 后端。

2. 参数 Schema:用 Zod 约束组件入参

渲染组件与普通 React 组件最大的区别是:它的 props 由 LLM 动态生成,必须有一个机器可读、可校验的 Schema。Demo 用 Zod 定义(bar-chart.tsx):

export const barChartPropsSchema = z.object({ title: z.string().describe("Chart title"), description: z.string().describe("Brief description or subtitle"), data: z.array( z.object({ label: z.string(), value: z.number(), }), ), }); export type BarChartProps = z.infer<typeof barChartPropsSchema>;

饼图(pie-chart.tsx)采用了完全相同的title / description / data[{label, value}]结构。这样做的好处是:

  • Schema 即契约:LLM 生成参数时必须符合 Schema,前端拿到后可以直接z.infer出类型,无需手写重复的 TS 接口;
  • 描述即提示z.string().describe(...)中的描述文本会进入 Agent 的上下文,引导 LLM 生成语义正确的字段值(例如title应填图表标题而非任意文本)。

3. 渲染组件:把结构化数据画出来

BarChart为例,它是一个基于 recharts 的纯展示组件:ResponsiveContainer自适应宽度、CartesianGrid网格线、XAxis/YAxis坐标轴、Tooltip悬浮提示,并用一组预置色板给每根柱子着色。值得一提的两个细节:

  • 空数据兜底:当data缺失或为空数组时,组件渲染一个带"No data available"的空状态卡片,而不是抛出异常或白屏;
  • 增量动画:通过useRef记录已渲染过的柱子索引,仅对"新到达"的柱子播放barSlideIn入场动画,避免数据流式更新时整张图反复闪烁。

PieChart则是一个纯 SVG 实现的环形图:按value占比计算每段弧长,用strokeDasharraystrokeDashoffset绘制圆环切片,下方配合图例展示数值与百分比(val.toLocaleString()格式化千分位)。

4. 建议提示词:引导用户快速触发图表

suggestions.ts 通过useConfigureSuggestions注册了三条建议提示词:

useConfigureSuggestions({ suggestions: [ { title: "Sales bar chart", message: "Show me a bar chart of quarterly sales for Q1, Q2, Q3, Q4.", }, { title: "Traffic pie chart", message: "Show me a pie chart of website traffic by source.", }, { title: "Market share", message: "Show a pie chart of smartphone market share by brand.", }, ], available: "always", });

available: "always"表示这些建议在任何状态下都可用。它们以可点击的提示胶囊(pill)形式展示在聊天界面,用户点一下即以message作为输入发出,从而降低"空想一句触发图表的话"的试错成本——E2E 测试里也把这三条建议的可见性作为页面加载成功的断言。

三、后端与运行时:工具从哪来、结果如何到达前端

1. 后端工具定义:ToolMessage+ AG-UI 适配

Langroid 集成中,所有工具都是langroid.agent.tool_message.ToolMessage的子类(agent.py)。与本 Demo 直接相关的核心工具是QueryDataTool

class QueryDataTool(ToolMessage): request: str = "query_data" purpose: str = "Query the database. Always call before showing a chart or graph." query: str def handle(self) -> str: try: result = query_data_impl(self.query) return _json_dumps(result) except Exception as exc: return _tool_error( error=_ToolErrorKind.QUERY_DATA_FAILED, message=f"{exc.__class__.__name__}: {str(exc)[:200]}", )

purpose字段是给 LLM 看的工具说明——"展示图表之前必须先调用本工具",这保证了 Agent 在用户要求"画个图"时不会跳过数据查询直接编造。handle()执行真实数据逻辑并返回 JSON 字符串;出错时返回统一结构的错误 JSON(_ToolErrorKind枚举限定错误码,防止字符串写错),便于外层 LLM 一致地识别可恢复的工具失败。

从源码注释可以确认这套机制的关键设计(agent.py):Langroid 没有原生的 AG-UI 适配器,集成层手工实现了 AG-UI 协议的 SSE 事件流;工具分为BACKEND_TOOLS(服务端执行)与FRONTEND_TOOLS(客户端执行)两组,并通过 import 时的金丝雀断言(_EXPECTED_FRONTEND_TOOL_NAMES)防止前后端工具名漂移。

2. 运行时路由:CopilotKit Runtime 代理到 Python Agent

前端runtimeUrl指向的 route.ts 是 Next.js 侧的运行时入口:它用HttpAgent@ag-ui/client)把每个 agent 名代理到运行在AGENT_URL(默认http://localhost:8000)的 Python 后端,并用createCopilotRuntimeHandlersingle-route模式统一处理 POST 请求。gen-ui-tool-based与其它 demo 共享同一个统一 Agent 端点/,前端仅通过 UI 组合(本 Demo 即CopilotChat+ 两个useComponent渲染器)区分行为。

GET分支则提供健康探针:返回agent_urlagent_statusOPENAI_API_KEY是否设置等诊断信息,方便在部署后快速确认后端连通性。

3. 请求-响应链路总览

将以上代码串联起来,一次"画饼图"请求的完整链路是:

  1. 用户在CopilotChat中输入"Show me a pie chart of revenue by category";
  2. 前端通过/api/copilotkit把消息转发给运行时,运行时经 AG-UI 协议转发给 Python 侧的 Langroid Agent;
  3. Agent 的 LLM 依据QueryDataTool.purpose决定先调用query_data获取结构化数据,再根据上下文调用前端注册的组件渲染工具;
  4. 结构化结果随 AG-UI 事件流回到前端;
  5. 前端按工具名命中useComponent注册的PieChart渲染器,用args填充 props,将 SVG 图表渲染进助手消息气泡中。

四、展示区定位与验证:manifest 与 E2E 测试

1. 在展示清单中的定位

manifest.yaml 将gen-ui-tool-based登记为名为"Tool-Based Generative UI"的 demo(route: /demos/gen-ui-tool-based),归入generative-ui标签,并高亮了三个关键文件:src/agents/agent.py(工具与 Agent 逻辑)、src/app/demos/gen-ui-tool-based/page.tsx(渲染器注册)与src/app/api/copilotkit/route.ts(运行时桥接)——与本 Demo 的前、中、后端三层完全对应。README 中所说 "The canonical description lives in the showcase manifest" 即指此处。

2. E2E 测试:可验证的行为契约

gen-ui-tool-based.spec.ts(Playwright)从用户视角锁定了该 Demo 的行为:

  • 页面加载:聊天输入框与三条建议胶囊("Sales bar chart" / "Traffic pie chart" / "Market share")在 10~15 秒内可见;
  • 饼图渲染:输入"Show me a pie chart of revenue by category"后,首个助手消息气泡内应出现 SVG 可视化(60 秒超时);
  • 柱状图渲染:输入"Show me a bar chart of monthly expenses"后同样断言 SVG 可见;
  • 基础对话:发送"Hello"能收到助手回复。

其中对[data-testid="copilot-assistant-message"]svg的断言,正是"工具结果以自定义组件而非纯文本渲染"这一核心特性的自动化验证——图表必须真的渲染在消息流里,而不是仅仅输出一段文字描述。

五、在 CopilotKit 中实践 Tool-Based Generative UI 的要点

结合本 Demo,可以提炼出一份可直接套用的实现清单:

  1. 前端注册渲染器:用useComponent为每个 UI 类型注册唯一name、面向 LLM 的descriptionparametersSchema 与render组件;
  2. Schema 双写合一:用 Zod 的describe()把字段语义喂给 LLM,同时用z.infer导出 TS 类型供组件消费,避免"提示词"与"类型"两套定义失同步;
  3. 组件做好空态与流式适配data为空要渲染占位卡片,增量数据更新要避免整体重渲染闪烁(Demo 中的isNew动画即为一例);
  4. 后端工具职责单一:数据获取与 UI 渲染解耦——后端只负责"查数据并返回结构化 JSON",purpose中写明使用前提(如"画图前先查询")引导 LLM 正确编排工具调用;
  5. 运行时桥接:确认前端runtimeUrl、Provider 的agentCopilotChatagentId与运行时路由(route.ts)中登记的 agent 名三者一致;
  6. 建议提示词降低门槛:用useConfigureSuggestions提供 2~3 个直击场景的示例输入,同时方便 E2E 稳定断言。

六、与其它 Generative UI 路径的区分

在 Langroid 集成的 manifest 中,Generative UI 是一个完整的演示族系(generative_ui字段与features列表),Tool-Based Generative UI 只是其中一条路径:

  • Tool Renderingtool-rendering):对 Agent 的工具调用过程做自定义展示(如天气卡片、航班卡片);
  • Agentic Generative UIgen-ui-agent):面向长时任务,Agent 驱动pending → in_progress → completed状态机并配合状态快照事件;
  • Declarative Generative UI / A2UIa2ui-fixed-schemadeclarative-gen-ui):Agent 依据前端声明的组件目录(catalog)动态生成 UI 操作指令,属于 A2UI(AG-UI)协议的 schema 驱动路线。

Tool-Based Generative UI 的独特之处在于:UI 形态由后端工具的返回结果决定,前端渲染器是"按图索骥"的静态注册表——不需要 LLM 去设计 UI 结构,而是让 LLM 专注于"选对工具、给对参数",可视化质量完全由前端组件保证。对于图表、报表、业务卡片这类"数据结构稳定、渲染要求高"的场景,这是比完全开放式 UI 生成更可控、更稳的方案。

参考文件:本 Demo 全部源码见 demo 目录(page.tsxbar-chart.tsxpie-chart.tsxsuggestions.ts),后端工具与 Agent 见 agent.py,运行时桥接见 route.ts,行为契约见 E2E 测试。

【免费下载链接】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/14 1:54:41

鲸鱼迁徙算法求解大规模物流路径规划问题

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

作者头像 李华
网站建设 2026/9/14 1:51:18

Ollama本地模型前端接入指南:从API调用到流式对话实战

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

作者头像 李华
网站建设 2026/9/14 1:49:39

PP-MattingV2 ONNX Runtime WinForms部署:实现高性能抠图

简介&#xff1a;面向 C# 桌面应用开发者&#xff0c;提供在 WinForm 中部署 PP-MattingV2 人像抠图 ONNX 模型的完整源码工程。项目基于 VS2019、.NET Framework 4.7.2&#xff0c;结合 OpenCvSharp4.8.0 与 ONNX Runtime 1.16.3 完成模型推理与图像处理&#xff0c;适合需要快…

作者头像 李华
网站建设 2026/9/14 1:47:53

BDS/GPS双模单点定位C语言实现与RINEX解析

简介&#xff1a;本资源是一套基于Visual Studio 2010开发的北斗卫星导航系统&#xff08;BDS&#xff09;单点定位C语言实现程序&#xff0c;面向GNSS导航算法学习者、测绘与地理信息专业学生及嵌入式定位开发初学者&#xff0c;用于理解伪距观测、坐标解算、误差修正等单点定…

作者头像 李华