CopilotKit 实战:基于 CrewAI Conversational Flows 构建流式任务进度追踪的 Agentic Generative 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集成的gen-ui-agent演示展开:当 Agent 处理长时间运行的任务时,如何把「规划步骤 + 实时状态流」渲染成聊天窗口里动态更新的进度卡片。文章以官方 QA 验收文档为主线,结合前后端源码、端到端测试与路由配置,完整讲解该功能的架构原理、验收要点、实现细节与排查方法。读完本文,你将掌握 Agentic Generative UI 的完整技术链路:后端通过set_steps工具发布状态、AG-UI 桥接层把每次状态变更流式推送到前端、前端用useAgent订阅状态并原位渲染单一卡片。
一、文档定位与前置条件
本文对应的官方文档为 showcase/integrations/crewai-conversational-flows/qa/gen-ui-agent.md,它是该演示的 QA 验收清单(Test Steps)与预期结果(Expected Results)的权威来源。文档要求在执行测试前满足两项前置条件:
- 演示已部署且可访问:
gen-ui-agent演示页面已上线,可通过其路由/demos/gen-ui-agent访问(路由注册见 manifest.yaml); - Agent 后端健康:通过
/api/health检查后端服务状态。
在仓库中,健康检查接口的实现位于 src/app/api/health/route.ts,返回status: "ok"与集成名称。另在 src/app/api/copilotkit/route.ts 中,GET /api/copilotkit会额外探测后端 Agent 进程(默认http://localhost:8000,可用AGENT_URL环境变量覆盖)的可达性,并回显OPENAI_API_KEY是否已设置——这是定位「页面能开、Agent 不回答」类问题的第一排查点。
二、功能全景:Agent 如何「边干活边更新界面」
QA 文档把测试拆成三大块:基本功能、特性专项检查、错误处理。在深入每步验收标准之前,先理解这个演示的完整数据流,这对后续核对每个断言至关重要。
从 src/app/demos/gen-ui-agent/page.tsx 的注释可以看出,该演示的核心模式是:
- 后端是一个独立的 CrewAI Flow(
GenUiAgentFlow),它自定义了自己的状态 schema(steps: list[Step]),并暴露一个名为set_steps的自定义工具,由大模型调用以变更状态; - 每次
set_steps调用都会把更新后的steps流式推送到客户端(通过copilotkit_emit_state触发 AG-UI 的STATE_SNAPSHOT事件); - 前端通过
useAgent(v2)订阅实时状态,再通过messageView.children在聊天记录中渲染一个InlineAgentStateCard卡片; - 卡片在状态到达时就地重新渲染——不会为每条消息生成新卡片,也不会出现重复卡片。
这一模式取代了旧版useCoAgentStateRender(该方案会为每条状态变更消息各渲染一张卡片,导致堆积)。它在所有集成的gen-ui-agent演示中保持一致(mastra、strands、ag2、agno、langgraph-typescript、pydantic-ai 等)。
前后端通信链路
前端路由的接线位于 src/app/api/copilotkit/route.ts:gen-ui-agent这个前端别名被显式映射到独立的 Agent 端点/gen-ui-agent,而不是通用的 chat Flow——注释特别强调,每个别名都必须显式配置,否则 UI 看似连上了、但丢失了该演示赖以存在的专用 AG-UI 事件:
// gen-ui-agent routes to a dedicated CrewAI Flow backend that owns the // `set_steps` tool + per-call STATE_SNAPSHOT emit (see // src/agents/gen_ui_agent.py). agents["gen-ui-agent"] = createAgent("/gen-ui-agent");后端 Flow 挂载在conversational_flows/gen-ui-agent路径下(HttpAgent的 url 拼接规则),而 AG-UI 桥接层通过add_crewai_flow_fastapi_endpoint把该 Flow 暴露为 FastAPI 端点。
三、逐项验收:基本功能测试
QA 文档的第一组测试聚焦于页面基础能力,完整清单如下:
- 导航到
gen-ui-agent演示页面; - 验证聊天界面以居中、全高布局加载;
- 验证聊天输入框的占位符"Type a message"可见;
- 发送一条基础消息;
- 验证 Agent 正常回复。
其中「居中全高布局」的实现在 page.tsx:外层使用flex justify-center items-center h-screen w-full,内层用h-full w-full max-w-4xl限制最大宽度。聊天组件CopilotChat本身设置className="h-full rounded-2xl",保证卡片式圆角外观占满可用高度。
「输入框占位符」由CopilotChat组件内置提供,对应的端到端断言可在 tests/e2e/gen-ui-agent.spec.ts 中找到:
test("page loads with chat input", async ({ page }) => { await expect(page.getByPlaceholder("Type a message")).toBeVisible(); });「发送消息并得到回复」同样有自动化覆盖:填入Hello后按回车,断言首个data-testid="copilot-assistant-message"在 30 秒内可见(见同文件 L12-L22)。同时该测试还验证了消息列表容器copilot-message-list只有在首条消息发送后才渲染——因为CopilotChatv2 在无消息时展示欢迎屏,messageView.children回调(负责渲染该容器)只在首条消息之后被调用。
四、特性专项检查:Suggestion 按钮与任务进度追踪器
4.1 Suggestion 按钮
QA 文档要求验证两个建议按钮可见:
- "Simple plan":规划 5 步前往火星;
- "Complex plan":规划 10 步制作披萨。
注意:当前仓库的 suggestions.ts 已更新为三个建议(产品发布规划、团队 offsite 组织、竞品调研),说明建议文案会随演示迭代而变化。QA 文档中的 "Simple plan" / "Complex plan" 属于该文档编写时期的版本,实际验收时应以当前页面渲染的建议按钮为准,但验证目标一致:每个建议按钮都应能一键把预设提示词填入聊天。这也体现了 QA 文档的"意图校验"思想——核对的是行为契约而非固定文案。
建议按钮的实现依赖useConfigureSuggestions(来自@copilotkit/react-core/v2),配置项包含按钮标题与点击后发送的消息,available: "always"表示建议始终可用:
useConfigureSuggestions({ suggestions: [ { title: "Plan a product launch", message: "Plan a product launch for a new mobile app.", }, // ... ], available: "always", });4.2 任务进度追踪器(useAgent + 状态流)
这是整个演示的核心。QA 文档给出了非常细致的渲染验收标准,逐条对照源码如下:
卡片渲染
- 点击 "Simple plan" 建议或输入 "Build a plan to go to Mars in 5 steps";
- 验证
TaskProgress组件渲染(data-testid="task-progress"); - 验证进度条出现且带有渐变填充;
- 验证步骤项出现并带描述(
data-testid="task-step-text"); - 验证 "N/N Complete" 计数器随步骤完成而更新。
当前实现中,卡片根节点使用data-testid="agent-state-card",每个步骤项使用data-testid="agent-step"并通过data-status属性暴露状态值(pending/in_progress/completed)——测试正是靠这两个属性定位元素。头部文案(headline)由 InlineAgentStateCard.tsx 计算:全部完成时显示All N steps complete,进行中显示Step X of N,尚无步骤时显示Planning…,这个动态文案即承担了 "N/N Complete" 计数器的职责。
三种步骤状态的视觉规范
QA 文档要求已完成、进行中、未来待办三种状态呈现不同的视觉样式。源码 InlineAgentStateCard.tsx 中的StepMarker组件实现了完整对应:
| 状态 | 视觉规范(QA 文档) | 源码实现 |
|---|---|---|
| 已完成 | 绿色背景渐变 + 对勾图标 + 绿色文字 | 圆形徽章bg-[#85ECCE],内嵌 SVG 对勾(M5 13l4 4L19 7),标题文字加删除线与灰绿弱化色text-[#838389] |
| 进行中 | 蓝/紫背景渐变 + 旋转图标 + "Processing..." 文字 + 脉冲动画 | 圆形徽章bg-[#BEC2FF],内嵌animate-spin旋转加载 SVG(SpinnerIcon),当前步骤标题为深色加粗text-[#010507] font-medium |
| 未来待办 | 灰色背景 + 时钟图标 + 弱化文字 | 白色圆形徽章带border-[#DBDBE5]边框与序号数字index + 1,标题为中性灰text-[#57575B] |
卡片顶部还有一个整体状态图标与文案联动:status === "inProgress" && done < total时显示旋转 Spinner,否则显示绿色对勾,与 QA 文档"完成时变绿勾、进行中旋转"的预期一致。
复杂计划(Complex Plan)
- 输入 "Plan to make pizza in 10 steps";
- 验证进度追踪器中出现 10 个步骤;
- 验证进度条宽度随步骤完成而增加。
这里体现的是步骤数量与计划内容解耦的能力——后端系统提示词要求"恰好规划 3 个步骤"(详见 gen_ui_agent.py),但 QA 文档描述的 5 步/10 步属于该演示演化过程中的早期行为。验证时更关键的判据是:无论步骤数量多少,步骤条目、状态迁移与进度展示都必须正确联动。
五、前端实现剖析:单卡片原位更新
5.1 状态订阅与数据流
page.tsx 中的Chat组件演示了 v2 的标准用法:
const { agent } = useAgent({ agentId: "gen-ui-agent", updates: [UseAgentUpdate.OnStateChanged], }); const steps = (agent.state as AgentState | undefined)?.steps ?? []; const status = agent.isRunning ? "inProgress" : "complete";要点:
useAgent订阅OnStateChanged更新,后端每次STATE_SNAPSHOT事件都会触发回调;agent.state.steps是后端AgentState中steps字段的直接映射(状态 schema 定义见下文 6.2 节);agent.isRunning作为卡片整体状态(进行中/完成)的判定依据。
5.2 单卡片渲染的关键技巧
messageView.children把消息列表、状态卡片、中断元素组合进MessageListWithState(见 message-list-with-state.tsx):
<div>export type Step = { id: string; title: string; status: "pending" | "in_progress" | "completed"; };注释明确指出:该结构必须与 Python 后端set_steps工具发出的StepTypedDict 一一对应,状态流转为pending → in_progress → completed。React 列表以step.id作为稳定 key(兜底使用索引),保证步骤跨状态迁移时组件实例稳定、动画不闪烁。
六、后端实现剖析:CrewAI Flow 驱动状态机
6.1 为什么用 Flow 而不是 Crew
后端文件 src/agents/gen_ui_agent.py 的文件头注释给出了明确的架构决策:该演示必须用crewai.flow.Flow实现,不能托管在 Crew 端点——因为ChatWithCrewFlow不会把每个工具的状态变更暴露给 AG-UI 桥接层,其唯一的共享状态变更只是把result.raw追加到state["outputs"]。而本演示需要「每个工具调用后都广播状态快照」,这只有自定义 Flow 能提供。后端策略与shared_state_read_write.py、subagents.py一致:每个 Flow 通过add_crewai_flow_fastapi_endpoint挂载到独立路径,并在每次工具执行后调用copilotkit_emit_state(self.state),让 AG-UI 桥接层发出STATE_SNAPSHOT事件,前端useAgent({updates: [OnStateChanged]})订阅消费。
6.2 状态 Schema:带类型的 steps 列表
class Step(BaseModel): id: str = "" title: str = "" status: Literal["pending", "in_progress", "completed"] = "pending" class AgentState(CopilotKitState): steps: List[Step] = Field(default_factory=list)Step的id被设计为稳定且不透明的句柄,让前端能在状态迁移期间保持 React key 稳定。该设计同时镜像了 LangGraph 参考实现(GenUiAgentState.steps[i]typed dict)与 MAF(STATE_SCHEMA.steps.items),体现了仓库中多集成之间刻意保持的 parity(对等性)。
6.3 set_steps 工具:状态发布的唯一通道
工具采用纯 OpenAI 兼容的 JSON Schema 定义(而非 CrewAIBaseTool),因为监督 LLM 调用走litellm.acompletion直连,JSON Schema 才是正确的原语(与shared_state_read_write.SET_NOTES_TOOL保持一致)。工具描述中强调了两个关键约定:
- 每次调用都必须携带完整的步骤列表——这是唯一的真相来源,不是增量 diff;
- 串行执行——禁止并行调用
set_steps,每次必须等待上一次返回。
参数 schema 中每个步骤对象必填id、title、status,且status限制枚举为pending/in_progress/completed。
6.4 ReAct 循环与状态快照
GenUiAgentFlow.chat是一个受_MAX_ITERATIONS = 20限制的循环(gen_ui_agent.py),每次迭代:
- 用「系统提示词 + 当前用户回合消息」组装请求,调用
litellm.acompletion(模型openai/gpt-5.4,parallel_tool_calls=False强制串行,stream=True经copilotkit_stream包装); - 若响应没有工具调用,说明 LLM 给出了最终文本回复,本轮结束;
- 若响应包含工具调用,则遍历全部调用(不取
[0],防御某些提供方违规下发多个工具调用导致消息线程不完整):set_steps:解析参数 →_coerce_steps做防御性清洗(丢弃非字典、缺 id/title、状态非法的条目,单条坏数据不拖垮整个 Flow)→整体替换state.steps(last-write-wins 归约,与 LangGraph 的_last_stepsreducer 及 MAF 的state_update语义一致)→ 追加 tool 结果消息 →copilotkit_emit_tool_result;- 前端注册的其他 action:仅追加占位 tool 结果(
"frontend tool — handled client-side"),保持消息线程合法,实际往返由 AG-UI 客户端负责;
- 若本轮确实改变了 steps,调用
copilotkit_emit_state(self.state)发出状态快照,前端随即重渲染——无需等待下一轮 LLM 响应。
关于状态替换语义(swap 而非 accumulate),测试在 tests/python/test_specialized_flows.py 与 d5 probe(d5-gen-ui-agent.ts)中都有断言。
6.5 系统提示词:把状态机写进约束
SYSTEM_PROMPT 把整个演示的行为契约硬编码进了提示词:
- 每个请求恰好规划 3 个具体步骤,先一次性以
pending状态发布全部 3 步; - 随后按步骤逐个走
in_progress → completed(每步两次set_steps调用); - 全部完成后发送一条最终总结消息并终止,不再调用任何工具;
- 每步的
id必须在计划生命周期内保持稳定; - 步骤标题必须保留用户场景的关键词(产品发布计划须含
launch与marketing,offsite 须含venue与agenda,竞品调研须含competitor与weakness)。
这里有一个值得注意的细节:按 3 步骤的正常流程,一次用户回合需要 1 次枚举 + 3×2 次迁移 + 1 条最终文本 = 8 次 LLM 往返,_MAX_ITERATIONS = 20提供了约 2.5 倍余量供模型重试工具调用格式,与 LangGraph 参考实现的recursion_limit=50启发式(约 3 倍余量)对等。
6.6 模块级单例
文件末尾的gen_ui_agent_flow = GenUiAgentFlow()是模块级单例:add_crewai_flow_fastapi_endpoint会按请求深度拷贝(deepcopy)它,因此初始化成本只在导入时支付一次——这是高并发下性能与正确性兼顾的工程细节。
七、错误处理与预期结果
7.1 错误处理验收
QA 文档要求验证两项:
- 发送空消息应被优雅处理(不崩溃、不报错);
- 正常使用过程中无控制台错误。
空消息的优雅处理由CopilotChat组件层承担(不发送无内容消息);而后端_coerce_steps的防御性清洗(gen_ui_agent.py)从数据层面保证了:即使模型返回畸形步骤数据,坏行会被丢弃、好行继续驱动 UI,整个 Flow 不会中断。
7.2 预期结果(QA 文档原文)
- 聊天在3 秒内加载完成;
- Agent 在10 秒内给出响应;
- 任务进度追踪器展示实时的步骤完成状态;
- 进度条平滑动画;
- 无 UI 错误或布局损坏。
需要说明:这些数字属于该 QA 文档的验收基准,实际表现取决于部署环境与 LLM 提供方的响应速度。自动化的近似验证可参考 e2e 测试的超时设置——例如首条助手消息 30 秒、步骤完成 60 秒、整个运行 120 秒的宽限设计(见 gen-ui-agent.spec.ts),生产环境建议结合GET /api/copilotkit返回的agent_status先确认后端链路健康,再判断响应延迟归属。
八、验收实践清单(可直接复用)
将 QA 文档与仓库源码结合,整理出一份可复用的验收清单:
- 健康前置:访问
GET /api/health与GET /api/copilotkit,确认后端可达、agent_status: reachable; - 布局与入口:
/demos/gen-ui-agent页面全高居中加载,输入框占位符 "Type a message" 可见,欢迎屏正常; - 建议按钮:页面建议按钮(标题 + 预设消息)可见且可点击,点击后正确填充提示词;
- 状态卡片:发送计划类消息后,
agent-state-card出现且始终只有 1 张;agent-step条目数量与计划步骤一致; - 三种状态样式:核对已完成(绿色勾)、进行中(旋转图标 + 深色加粗)、待办(序号灰色徽章)三态视觉是否符合 4.2 节表格;
- 状态流转:等待运行结束,卡片头部变为 "All N steps complete" 绿色勾,所有步骤
data-status="completed",全程无新增卡片; - 异常路径:发送空消息不报错,控制台无错误日志;
- 时间基准:参考 7.2 节预期结果核对加载与响应速度。
九、结语
gen-ui-agent演示是理解 CopilotKit Agentic Generative UI 的绝佳样本:它把「Agent 如何透明地向用户展示长任务进展」这个产品问题,拆解成了后端状态 schema、set_steps工具、AG-UI 状态快照、前端useAgent订阅与单卡片原位渲染五层清晰的技术答案,并且用 QA 文档 + e2e 测试把每一层的行为契约固定下来。若你想深入源码,建议按此顺序阅读:QA 文档 → 前端 page.tsx → 状态卡片组件 → 后端 Flow → 路由接线 → 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),仅供参考