CopilotKit 集成 CrewAI Flows:用 Hashbrown 流式渲染声明式生成式 UI 的完整实践指南
2026/9/13 11:20:16 网站建设 项目流程

CopilotKit 集成 CrewAI Flows:用 Hashbrown 流式渲染声明式生成式 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 仓库中showcase/integrations/crewai-crews的 BYOC(Bring Your Own Components)Hashbrown 集成示例为蓝本,系统讲解如何让一个极简的 CrewAI 单 Agent Crew 输出符合 Hashbrown schema 的 JSON 信封(envelope),并借助@hashbrownai/react的前端解析器把流式 JSON 逐步组装成 MetricCard、饼图、柱状图、交易卡片与 Markdown 混排的销售仪表盘。读完本文,你将掌握该集成的端到端调用链(CrewAI Crew → FastAPI → CopilotRuntime → React 渲染器)、纯 JSON 输出的系统提示词约束技巧,以及完整的前端渲染与 E2E 验证方法。

背景:为什么需要 BYOC 与 Hashbrown

在大模型驱动的对话应用中,常规做法是让 LLM 输出自然语言文本,再由前端把文本展示给用户。但在销售分析、数据看板这类场景中,用户期望的不是一段话,而是结构化的可视化 UI——KPI 卡片、饼图、柱状图等。Hashbrown 正是为此设计的"组件目录 + 结构化输出"方案:LLM 不再输出自由文本,而是输出一份受 schema 约束的 JSON 信封,前端拿到后按组件目录逐块渲染。

本示例名为 BYOC(Bring Your Own Components),核心思想是:前端自己定义组件目录(catalog)并暴露给 LLM,LLM 只负责输出符合目录的 JSON 结构,渲染完全由前端组件完成。与之相对的是qa/declarative-json-render.md这类"通用 JSON 渲染"路径——两者区别在于:Hashbrown 方案有严格的组件 props schema 约束与流式逐块组装能力,而通用 JSON 渲染则更自由但缺少目录校验。

在 CopilotKit 生态中,这一集成落在 CrewAI 集成 showcase 内,对应的 QA 文档为 qa/declarative-hashbrown.md,本文即围绕该文档及其源码实现展开。

架构总览:四层调用链

从整体上看,这个演示由四层组成,它们协同完成"用户点击 → 流式 JSON → 逐步渲染 UI"的完整链路:

  1. CrewAI 后端src/agents/byoc_hashbrown_agent.py定义一个专门输出 Hashbrown JSON 信封的 Crew,通过 agent_server.py 挂载到 FastAPI 端点/byoc-hashbrown
  2. CopilotRuntime 代理层src/app/api/copilotkit-byoc-hashbrown/route.ts在 Next.js 侧创建一个CopilotRuntime,把请求代理到后端的AGENT_URL(默认http://localhost:8000)。
  3. React 前端src/app/demos/declarative-hashbrown/page.tsx渲染CopilotKit包装的聊天界面;hashbrown-renderer.tsx注册组件目录并用useJsonParser解析流式 JSON。
  4. E2E 测试tests/e2e/declarative-hashbrown.spec.ts用 Playwright 验证三类建议词触发的渲染结果。

调用链对应的关键文件与行号如下(后文逐一展开):

文件职责
Crew 定义byoc_hashbrown_agent.py纯 JSON 输出的系统提示词 + 单 Agent Crew
后端挂载agent_server.pyadd_crewai_crew_fastapi_endpoint(app, ByocHashbrown(), "/byoc-hashbrown")
Runtime 代理route.tsHttpAgent指向${AGENT_URL}/byoc-hashbrown
前端页面page.tsxCopilotKit+HashBrownDashboard布局
渲染器hashbrown-renderer.tsxuseUiKit定义组件目录、useJsonParser流式解析
聊天组件chat.tsxCopilotChat接入自定义 assistant 消息槽

一、CrewAI 后端:让 Crew 只输出 JSON

1.1 系统提示词:Hashbrown JSON 信封规范

后端核心是 byoc_hashbrown_agent.py 中的BYOC_HASHBROWN_SYSTEM_PROMPT。它告诉 LLM:每次回复必须是一个单一的 JSON 对象,且形如{"ui": [...]}信封,其中ui数组中的每个元素是组件名到{ props: {...} }的映射:

{ "ui": [ { "metric": { "props": { "label": "Total Revenue", "value": "$1.2M" } } }, { "pieChart": { "props": { "title": "...", "data": "[{...}]" } } }, { "barChart": { "props": { ... } } }, { "dealCard": { "props": { ... } } }, { "Markdown": { "props": { "children": "..." } } } ] }

这里有一个关键设计(源码注释中特别强调):CrewAI 驱动的 LLM 必须直接输出原始 schema 形状,而不是 Hashbrown 官方的 XML<ui>...</ui>DSL。那个 XML DSL 是当 Hashbrown 自己驱动 LLM 时,由它把 DSL 编译成 schema 文档使用的;本示例通过 CrewAI 驱动,所以必须直接输出 JSON schema 本身。

各组件 props 规范如下(均出自系统提示词原文):

组件props说明
metriclabel: string,value: stringKPI 卡片,value为预格式化字符串,如"$1.2M""248"
pieCharttitle: string,data: string环形图,dataJSON 编码的字符串(内嵌 JSON),为至少 3 段的{label, value}数组
barCharttitle: string,data: string垂直柱状图,data同上,至少 3 根柱,通常按时间排序
dealCardtitle: string,stage: string,value: number单笔销售交易;stage必须属于六个枚举值之一;value为裸数字(不含货币符号与逗号)
Markdownchildren: string简短说明文字,用于小标题与过渡句,支持标准 Markdown

提示词还规定了硬性约束:不包代码围栏、不输出 JSON 对象之外的任何前言或解释、不调用任何工具、不询问澄清性输入;图表数据优先给出 3~6 行合理样本数据,标签保持简短;data必须是 JSON 字符串,内层引号需要转义。提示词末尾还附了一个完整的销售仪表盘示例响应,供 LLM few-shot 参考。

1.2 为何要"绕过"CrewAI 的默认系统提示词

CrewAI 有一个让开发者头疼的默认行为:ChatWithCrewFlow.build_system_message会用固定的 "CrewAI platform" 样板包装 Crew 描述,这段样板会主动怂恿 LLM 自我介绍、询问澄清输入——这恰好与"只输出一个 JSON 对象"的需求直接冲突。

源码 byoc_hashbrown_agent.py 的模块 docstring 明确指出了这一点,并给出了两条解决路径:

  1. 预播种系统提示词preseed_system_prompt):把我们的提示词注册为crew_description,同时跳过启动时的二次 AI 调用(CrewAI 默认会用 LLM 探测来生成 crew 描述),让 Crew 构建保持同步、廉价、快速。
  2. 安装硬覆盖install_custom_system_message):monkey-patchChatWithCrewFlow.__init__,在实例构造完成后立刻把自定义系统消息写回self.system_message,覆盖掉上游代码组装的那一份。

这两者的实现都在 _chat_flow_helpers.py 中,且都通过crew_name作为 key 注册,未注册的 Crew 会回落到默认行为,互不干扰。

1.3 最小 Crew 的搭建与端点挂载

由于聊天行为完全由自定义系统消息驱动,Crew 本身只需要一个"躯壳"即可——源码注释把它描述为"The agent body is only here becauseChatWithCrewFlowrequires a crew with at least one agent + task"。实际创建如下(见 byoc_hashbrown_agent.py):

agent = Agent( llm="gpt-5.4", role="Hashbrown JSON Emitter", goal="Emit hashbrown-shaped JSON responses.", backstory=BYOC_HASHBROWN_SYSTEM_PROMPT, verbose=False, tools=[], ) task = Task( description="Respond with a single hashbrown-shaped JSON object.", expected_output="A JSON object matching the hashbrown schema.", agent=agent, ) return Crew( name=CREW_NAME, agents=[agent], tasks=[task], process=Process.sequential, verbose=False, chat_llm="gpt-5.4", )

随后在 agent_server.py 中将其挂载为 FastAPI 端点:

add_crewai_crew_fastapi_endpoint(app, ByocHashbrown(), "/byoc-hashbrown")

其中ByocHashbrown是实现了name属性与crew()方法的适配器类,crew()使用模块级缓存_cached_crew保证只构建一次 Crew(见 byoc_hashbrown_agent.py)。

二、Runtime 代理层:把前端请求转发给 CrewAI

在 Next.js 一侧,route.ts 创建了一个专用的 CopilotRuntime:

const AGENT_URL = process.env.AGENT_URL || "http://localhost:8000"; function createAgent() { return new HttpAgent({ url: `${AGENT_URL}/byoc-hashbrown` }); } const agents: Record<string, AbstractAgent> = { "byoc-hashbrown-demo": createAgent(), default: createAgent(), }; const runtime = new CopilotRuntime({ agents });

关键点:

  • AGENT_URL环境变量:默认指向http://localhost:8000(即本地 agent_server 的监听地址),可通过环境变量覆盖指向远程部署的后端。
  • 两个 agent 别名byoc-hashbrown-demodefault指向同一个HttpAgent,前端页面通过agent="byoc-hashbrown-demo"显式选中该 agent。
  • 导出 POST 处理器:使用createCopilotRuntimeHandler并以mode: "single-route"basePath: "/api/copilotkit-byoc-hashbrown"挂载,捕获异常后返回结构化{ error, stack }JSON(500 状态码)。

三、React 前端:组件目录注册与流式 JSON 渲染

3.1 页面组装

page.tsx 使用CopilotKit组件包裹整个页面,指定runtimeUrl="/api/copilotkit-byoc-hashbrown"agent="byoc-hashbrown-demo",内部再套一层自定义的HashBrownDashboardprovider:

<CopilotKit runtimeUrl="/api/copilotkit-byoc-hashbrown" agent="byoc-hashbrown-demo" > <HashBrownDashboard> {/* 页面布局与 <Chat /> */} </HashBrownDashboard> </CopilotKit>

页面头部标题为 "Declarative UI: Hashbrown",副标题说明这是通过@hashbrownai/react实现的流式结构化输出。聊天区由 chat.tsx 渲染CopilotChat,并把默认的CopilotChatAssistantMessage槽替换为自定义的HashBrownRenderMessage(需要类型断言以满足 slot 签名)。

3.2 组件目录:useUiKit + exposeComponent

hashbrown-renderer.tsx 是本方案的核心。它调用@hashbrownai/reactuseUiKit声明组件目录:

function useSalesDashboardKit() { return useUiKit({ examples: prompt`...`, components: [ exposeMarkdown(), exposeComponent(MetricCard, { name: "metric", description: "A KPI metric card with label, value, and optional trend", props: { label: s.string("The metric label/name"), value: s.string("The metric value (formatted)"), }, }), exposeComponent(PieChartWithStringData, { name: "pieChart", description: "A donut/pie chart. `data` is a JSON-encoded string ...", props: { title: s.string("Chart title"), data: s.string("JSON array of {label, value} segments"), }, }), // barChart、dealCard 同理 ... ], }); }

值得注意的工程细节:

  • examples中使用 Hashbrown 的prompt模板字面量写一段<ui>...</ui>的示例,混合展示Markdown标题、metricpieChartbarChartdealCard,并附 hint 提示图表必须包含titledatadata是 JSON 编码的{label, value}数组字符串——这与后端系统提示词的约定完全一致。
  • dealCardstages.enumeration声明,枚举值正好是后端提示词中规定的六个管道阶段(prospect/qualified/proposal/negotiation/closed-won/closed-lost),前后端 schema 严格对齐。

3.3 图表 data 字符串化:流式解析下的稳定性设计

前端组件PieChartWithStringDataBarChartWithStringData都接收data: string,内部用parseChartDataJSON.parse,解析失败(流式中途的截断 JSON)则渲染null,成功才把真实数组交给图表组件。源码注释解释了这样做的原因:"The LLM streams JSON as text anyway, so we modeldataas a string and parse inside the wrapper"。这保证了 schema 在部分流式状态下保持稳定,避免中途的非法 props 触发渲染崩溃——这是 Hashbrown 流式方案的基石

HashBrownDashboard通过useUiKit拿到kit(内含schemarender)后放入 Context,供消息渲染器使用。若在 provider 之外使用会抛出HashBrownRenderMessage must be used within HashBrownDashboard错误。

3.4 消息槽渲染:useJsonParser 渐进式组装

AssistantMessage组件对每条 assistant 消息调用useJsonParser(content, kit.schema)

const { value } = useJsonParser(content, kit.schema); if (!value) return null; return <div contenteditable="false">【免费下载链接】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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询