CrewAI Conversational Flows 中的 BYOC json-render:在 CopilotKit 中用{root, elements}扁平规范渲染声明式生成 UI
【免费下载链接】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集成包的BYOC json-render声明式 UI 方案展开:它以 CrewAI 的ChatWithCrewFlow为后端承载一个"专用 Crew"(ByocJsonRender),让 Agent 输出一份严格符合@json-render/react扁平元素规范({ root, elements })的 JSON 对象,前端再通过<JSONUIProvider>与<Renderer />将流式 JSON 实时转换为 MetricCard、BarChart、PieChart 三种目录组件。读完本文,你将掌握该 demo 的完整链路(Prompt → Crew → FastAPI 挂载 → Next.js Runtime Route → 前端渲染器)、QA 验证步骤与已知集成断裂点,能够复现并独立排查同类"声明式 JSON 渲染"集成。
一、背景:什么是 BYOC json-render,以及它解决的问题
在 Agent 驱动的对话式 UI 中,一个常见痛点是:如何让模型输出的结构化数据稳定地映射为真实的 UI 组件。直接让模型输出 JSX 或组件代码既不可控也不安全;让模型输出自由文本再由前端正则解析则脆弱不堪。crewai-conversational-flows的 json-render 方案给出的答案是:
- 后端 Agent 被约束为只输出一个 JSON 对象,且对象形态固定为
@json-render/react消费的扁平元素映射(flat element map)——{ root, elements }; - 前端把这份 JSON 直接喂给
<Renderer />,由注册表(registry)中三个受 Zod 校验的组件(MetricCard / BarChart / PieChart)完成渲染; - 在 JSON 尚未合法、或模型偶尔输出纯文本时,聊天界面自动回退到默认气泡,保证"永不白屏、永不卡死"。
该方案在仓库中的完整载体包括:
- QA 测试文档:qa/declarative-json-render.md(本文骨架来源);
- 后端专用 Crew:src/agents/byoc_json_render_agent.py;
- 前端渲染器:src/app/demos/declarative-json-render/json-render-renderer.tsx;
- 运行时路由:src/app/api/copilotkit-byoc-json-render/route.ts。
值得说明的是:虽然 Demo 页面与 QA 文档中的路由名称为declarative-json-render,但后端模块刻意保留了byoc_前缀(模块注释明确写道"byoc_prefix on the backend module is deliberate and stays"),因为它是与主多 Agent 运行时隔离的"自带后端"(Bring Your Own Crew)专用入口。
二、前置条件与依赖环境
根据 QA 文档,运行本 demo 需要满足以下前置条件:
- Demo 页面可访问:位于
/demos/declarative-json-render; - 后端健康:
agent_server.py正在运行且健康,它把该 Crew 挂载在/conversational_flows/byoc-json-render; - Next.js 运行时路由:页面挂载
/api/copilotkit-declarative-json-render作为runtimeUrl。但需要特别留意:当前包内只存在 copilotkit-byoc-json-render/route.ts,因此按现有代码,demo 页在其运行时 URL 上会 404(该问题详见下文"集成注意事项",属于已知遗留问题而非 QA 回归); OPENAI_API_KEY:Agent 后端(GPT 模型)需要该环境变量;- 依赖包:
package.json中需存在@json-render/core与@json-render/react,当前锁定版本为0.18.0(见 package.json)。
2.1 依赖版本快照
从 package.json 可以确认与本方案直接相关的依赖:
| 依赖 | 版本 | 作用 |
|---|---|---|
@json-render/core | 0.18.0 | 声明式 JSON 规范的核心库 |
@json-render/react | 0.18.0 | JSONUIProvider/Renderer等 React 渲染组件 |
@copilotkit/react-core | 1.68.2 | CopilotKit根组件与聊天消息组件 |
@copilotkit/runtime | 1.68.2 | CopilotRuntime与运行时处理器 |
@ag-ui/client | 0.0.57 | HttpAgent,连接 FastAPI 挂载的 Crew |
recharts | ^2.15.0 | 图表组件(BarChart/PieChart)底层实现 |
QA 文档特别指出,由于 JSON{ root, elements }规范比 hashbrown 的 token 流更冗长,渲染预算会略高——60 秒内完成渲染是该 QA 对页面加载渲染的既定预算。
三、后端实现:CrewAI 专用 Crew 与 JSON 规范约束
3.1 Crew 的整体设计
byoc_json_render_agent.py 定义了一个名为ByocJsonRender的专用 Crew,其核心设计决策包括:
- 单 Agent + 单 Task:一个"JSON-Render Spec Emitter"角色 Agent,任务描述为"Respond with a single JSON object matching the @json-render/react flat-element spec",预期输出为
{ root, elements }JSON 对象; - 顺序流程:
Process.sequential,LLM 配置为llm="gpt-5.4",同时设置chat_llm="gpt-5.4"(chat_llm用于 CrewAI 对话流程中的闲聊/补充生成); verbose=False与空工具列表:避免工具调用干扰纯 JSON 输出;- Crew 缓存:通过模块级
_cached_crew全局变量复用 Crew 实例,避免每次请求重建; - 适配器形态:类暴露
name与crew()方法,注释明确说明该形态是为了匹配add_crewai_crew_fastapi_endpoint的调用约定。
3.2 CrewAI 的关键坑:覆盖平台系统提示词
模块 docstring 记录了一个重要的 CrewAI 经验:
ChatWithCrewFlow 会在每一轮对话外层包裹一套 CrewAI "platform" 系统提示词,这会与"仅输出 JSON"的要求冲突。因此这里通过
install_custom_system_message安装硬覆盖,用我们自己的 json-render schema prompt 替换组合后的系统消息。
具体代码为:
preseed_system_prompt(CREW_NAME, BYOC_JSON_RENDER_SYSTEM_PROMPT) install_custom_system_message(CREW_NAME, BYOC_JSON_RENDER_SYSTEM_PROMPT)两个工具函数均来自agents._chat_flow_helpers。这一对调用分别负责"预置"与"安装覆盖",是保证 CrewAI 平台提示词不污染 JSON 输出的关键防线。任何基于 CrewAI Conversational Flows 做"纯结构化输出"的集成,都值得借鉴这一模式。
3.3 输出规范:扁平元素映射{ root, elements }
系统提示词(BYOC_JSON_RENDER_SYSTEM_PROMPT)要求 Agent 在收到 UI 请求时,只返回一个 JSON 对象,不得夹杂任何散文、markdown 代码围栏、前置解释、工具调用或澄清问题。对象必须匹配如下 schema:
{ "root": "<id of the root element>", "elements": { "<id>": { "type": "<component name>", "props": { "... component-specific props ..." }, "children": [ "<id>", ... ] } } }其中:
root是根元素 id 的字符串;elements是一个 id → 元素描述的映射;- 每个元素包含
type(组件名,必须是注册表中的名称)、props(组件专属属性)、可选的children(子元素 id 数组)。
3.4 允许的组件目录与 props 契约
系统提示词中完整给出了三个组件的 props 契约,前端 Zod 校验(ALLOWED_TYPES)与之一一对应:
MetricCard
| 字段 | 类型 | 说明 |
|---|---|---|
label | string | 指标名称 |
value | string | 指标值(如"$1.24M") |
trend | string | null | 趋势描述,示例:"+12% vs last quarter"、"-3% vs last month"、null |
BarChart
| 字段 | 类型 | 说明 |
|---|---|---|
title | string | 图表标题 |
description | string | null | 描述 |
data | { label: string, value: number }[] | 数据点数组 |
PieChart
| 字段 | 类型 | 说明 |
|---|---|---|
title | string | 图表标题 |
description | string | null | 描述 |
data | { label: string, value: number }[] | 数据点数组 |
3.5 六条硬性规则
提示词用编号规则约束模型行为,QA 的健壮性断言(无孤儿元素、无陌生组件类型、无 markdown 围栏)全部来源于此:
- 只输出合法 JSON:禁止 markdown 代码围栏、禁止对象之外的任何文本;
- 引用完整性:
root与任何children数组引用的 id,必须出现在elements的键中; - 多组件仪表盘结构:用 root MetricCard 承载图表子节点,或任选一个元素作 root、其余作为其 children;禁止产生孤儿元素;
- 销售域真实数据:使用收入、管道、转化率、类别、月份等真实感的销售域数值;
children可选:一旦出现必须是字符串数组;- 组件类型封闭:不得发明上述三个之外的组件类型。
3.6 三个内置 Worked Example
提示词内置了三个完整示例,QA 中的三个建议 pill(Sales dashboard / Revenue by category / Expense trend)正是对这三个示例的复现:
示例一:销售仪表盘(MetricCard + BarChart 嵌套)
{ "root": "revenue-metric", "elements": { "revenue-metric": { "type": "MetricCard", "props": { "label": "Revenue (Q3)", "value": "$1.24M", "trend": "+18% vs Q2" }, "children": ["revenue-bar"] }, "revenue-bar": { "type": "BarChart", "props": { "title": "Monthly revenue", "description": "Revenue by month across Q3", "data": [ { "label": "Jul", "value": 380000 }, { "label": "Aug", "value": 410000 }, { "label": "Sep", "value": 450000 } ] } } } }示例二:按类别拆分收入的饼图
{ "root": "category-pie", "elements": { "category-pie": { "type": "PieChart", "props": { "title": "Revenue by category", "description": "Share of total revenue by product category", "data": [ { "label": "Enterprise", "value": 540000 }, { "label": "SMB", "value": 310000 }, { "label": "Self-serve", "value": 220000 }, { "label": "Partner", "value": 170000 } ] } } } }示例三:月度支出的柱状图
{ "root": "expense-bar", "elements": { "expense-bar": { "type": "BarChart", "props": { "title": "Monthly expenses", "description": "Operating expenses by month", "data": [ { "label": "Jul", "value": 210000 }, { "label": "Aug", "value": 225000 }, { "label": "Sep", "value": 240000 } ] } } } }三个示例覆盖了{root, elements}规范的核心能力:单组件渲染、嵌套渲染(MetricCard 包裹 BarChart)、以及根节点直接是叶子图表的三种拓扑。
四、运行时链路:从 Next.js Route 到 FastAPI 挂载点
4.1 专用 Runtime Route
route.ts 是一个独立运行时入口,将byoc_json_renderCrew 与默认多 Agent 的/api/copilotkit运行时隔离:
- 通过
AGENT_URL环境变量(默认http://localhost:8000)构造HttpAgent,目标是{AGENT_URL}/conversational_flows/byoc-json-render; agents注册表中byoc_json_render与default均指向同一个 agent;- 用
createCopilotRuntimeHandler以single-route模式处理 POST,basePath为/api/copilotkit-byoc-json-render; - 捕获异常并返回
{ error, stack }的 JSON 响应(500)。
前端 Demo 页 page.tsx 通过<CopilotKit runtimeUrl=... agent=...>指向运行时,并将聊天表面限制在max-w-4xl居中容器内。从源码结构看,该页面的runtimeUrl写为/api/copilotkit-declarative-json-render,与后端实际存在的copilotkit-byoc-json-render路由不一致——这正是 QA 文档标注的已知断裂点。
4.2 后端的挂载方式
agent_server.py(位于 src/agent_server.py)使用 FastAPI 承载 Crew。根据 QA 文档与route.ts的注释,Crew 被挂载在/conversational_flows/byoc-json-render路径下,前端HttpAgent正是向该路径发起请求。package.json的dev脚本也印证了双进程启动方式:
concurrently "next dev --turbopack" "PYTHONPATH=. python -m uvicorn agent_server:app --host 0.0.0.0 --port 8000 --reload"即前端 Next.js(Turbopack)与后端 uvicorn(8000 端口、热重载)并行启动,AGENT_URL默认为http://localhost:8000。
五、前端渲染:流式 JSON 到组件的转换
前端渲染逻辑集中在 json-render-renderer.tsx 中,它替换默认的助手消息气泡(CopilotChatAssistantMessage),实现了"流式回退 + 组件替换"的核心机制:
5.1 渲染决策树
const content = typeof props.message.content === "string" ? props.message.content : ""; const spec = useMemo(() => parseSpec(content), [content]); // Stream not yet a valid spec (or plain prose) — render the default bubble. if (!spec) return <CopilotChatAssistantMessage {...props} />; return ( <div>导航到/demos/declarative-json-render;聊天输入框(composer)可见; 三个建议 pill 出现,标题分别为 "Sales dashboard"、"Revenue by category"、"Expense trend"; 控制台无报错。 6.2 Sales dashboard 建议
- 点击 "Sales dashboard" 建议;
- 60 秒内,助手气泡中出现
data-testid="json-render-root"包裹容器; - 容器内渲染出
data-testid="metric-card"; - 容器内渲染出图表(
data-testid="bar-chart"或data-testid="pie-chart"); - MetricCard 的嵌套子节点(Sales Dashboard 示例中的 BarChart)正常渲染——不得被静默丢弃;
- 渲染完成后不显示任何原始 JSON 文本——流式 JSON 已被组件替换。
6.3 Revenue by category
- 点击 "Revenue by category" 建议;
- 60 秒内渲染出
data-testid="pie-chart",包含多个类别切片与图例; - 控制台不得出现
useVisibility must be used within a VisibilityProvider错误(这验证了<JSONUIProvider>正确包裹了<Renderer>)。
6.4 Expense trend
- 点击 "Expense trend" 建议;
- 60 秒内渲染出
data-testid="bar-chart",包含月份标签。
6.5 自由文本提示
- 输入 "Show me a metric for quarterly revenue" 并发送;
- 至少渲染出一个
metric-card,控制台无报错。
6.6 多轮对话
- 上一渲染可见后,发送追问(如 "Now break that down by region");
- 新的助手消息出现新的 json-render 渲染,且先前的渲染保留在对话记录中。
6.7 异常输出处理
- 若 Agent 偶尔回复非 JSON 文本(可通过问 "tell me a joke" 强制触发),聊天应回退为默认助手气泡渲染该原始文本;无崩溃、无卡死的 loading 状态。
七、预期结果与验收口径
- 建议渲染在60 秒内落地;预算略高于 hashbrown demo,因为 JSON
{ root, elements }规范比 hashbrown 的 token 流更冗长; - 控制台无未捕获错误;
- 流式输出在 JSON 合法之前回退为纯文本,解析成功后无缝切换为渲染组件(QA 文档原话:"Streaming falls back to plain text until the JSON parses, then swaps to rendered components")。
八、集成注意事项与已知问题
byoc_前缀是刻意保留的:扁平 spec prompt 位于src/agents/byoc_json_render_agent.py,模块命名不得随意改动;- 已知断裂(既有问题,非本次 QA 回归):Demo 页(north-star 副本)挂载
runtimeUrl="/api/copilotkit-declarative-json-render",但本包只提供src/app/api/copilotkit-byoc-json-render/route.ts。在 API 路由改名(或增加别名)之前,所有测试步骤都会在网络层失败——QA 文档明确要求将该问题作为独立修复项登记,而不是当作 QA 缺陷; - 测试标识约定:没有
byoc-json-render-roottest id,也不存在/demos/byoc-json-render路由——只能在规范路由上断言data-testid="json-render-root"。
九、总结:一套可复用的"声明式 JSON 渲染"模式
从本 demo 可以提炼出四条可复用于其他 Agent 框架(LangGraph、LlamaIndex、Pydantic AI 等)的集成经验——仓库中claude-sdk-python/qa/byoc-json-render.md等平行 QA 文档也印证了该模式是 CopilotKit 全集成矩阵的统一范式:
- 用强约束提示词锁定输出形状:schema + 组件契约 + 规则 + worked examples 四件套,是让模型稳定输出
{root, elements}的关键; - 压制框架级系统提示词的干扰:CrewAI 场景必须通过
install_custom_system_message覆盖平台提示词(LangGraph 等其他框架则依赖 prompt 自身的强约束); - 前端做容错渲染:流式回退默认气泡、代码围栏剥离、平衡花括号提取、白名单校验,四层防御保证永不白屏;
- 为测试预留稳定的 DOM 契约:
data-testid="json-render-root"、metric-card、bar-chart、pie-chart等测试标识,让 QA 与 E2E 断言长期稳定。
如需继续深入,可对比阅读 claude-sdk-python/qa/byoc-json-render.md 与其他集成目录下的byoc_json_render_agent.py,观察同一套{root, elements}规范在不同 Agent 框架下的提示词差异与共性。
【免费下载链接】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),仅供参考