CopilotKit + LangGraph (FastAPI) 共享状态只读模式 QA 测试指南:让 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
本指南围绕 CopilotKit 与 LangGraph (FastAPI) 集成仓库中的shared-state-read(Shared State: Reading)演示场景,给出从环境准备、逐项功能验证到错误处理与验收标准的完整 QA 测试流程。读完本文,你将掌握如何在"前端发布状态、Agent 只读不写"的架构下,系统地验证 Agent 能否基于 UI 中的实时数据回答问题,以及如何用仓库中的源码与 Playwright 测试佐证每个测试断言。
测试对象与架构背景
shared-state-read是一个"配方编辑器 + AI 助手"演示:页面左侧是一个受控的表单(Recipe Card),右侧是默认展开的CopilotSidebar(标题为 "AI Recipe Assistant")。用户编辑表单、点击建议或 "Improve with AI" 按钮后,Agent 通过对话参与配方的生成与优化。
该场景的核心架构是单向共享状态(只读):
- 前端是单一数据源:页面通过
agent.setState({ recipe: ... })把整个配方对象发布进 Agent 状态,后续每次编辑都直接流向agent.setState,下一次渲染立即反映最新值; - Agent 只读不写:后端没有用于修改配方的工具,Agent 只负责在每一轮对话中读取这份状态(
agent.state.recipe)并基于它回答,因此后端无法反向污染表单数据; - 状态有类型约束:前端与后端共享同一份类型化状态结构(
RecipeAgentState),避免运行期字段错位。
上述机制对应的实现分别在 page.tsx(前端状态发布与订阅)、types.ts(状态与数据结构定义)以及 manifest.yaml 的shared-state-readdemo 条目中。
前置条件
开始执行 QA 前,请确认以下两项:
- Demo 已部署且可访问:
/demos/shared-state-read页面能正常打开; - Agent 后端健康:请求
/api/health(或对应健康检查路由)返回正常状态,LangGraph 后端可达。
后端连通性可以从 route.ts 中看到:GET请求会检查${AGENT_URL}/ok(默认http://localhost:8123,可通过环境变量AGENT_URL覆盖)并在 3 秒超时内返回agent_status: reachable / unreachable,这是定位"前端正常但 Agent 无响应"类问题时的首选入口。
一、基础功能验证
页面与表单加载
- 导航到 shared-state-read 演示页面;
- 验证配方卡片表单成功加载(
data-testid="recipe-card"); - 验证
CopilotSidebar默认展开,且标题为 "AI Recipe Assistant"; - 通过侧边栏发送一条消息;
- 验证 Agent 能正常回复。
这里的data-testid="recipe-card"定义在 recipe-card.tsx,侧边栏默认展开与标题设置来自 page.tsx 中<CopilotSidebar defaultOpen labels={{ modalHeaderTitle: "AI Recipe Assistant" }} />的配置。
Agent 路由注册
后端路由中,shared-state-read走的是"中性默认 Agent"通道:在 route.ts 的agentNames列表里注册,并通过createAgent()映射到sample_agent图。而sample_agent在 langgraph.json 中指向 agent.py 里由create_agent构建的图:它挂载了CopilotKitMiddleware,使用AgentState作为状态 schema,并带有 7 个后端工具。QA 阶段若消息无法得到回复,应优先检查该路由的注册日志(Registered N agent names)与GET /api/copilotkit返回的agent_status。
二、特性专项检查
1. 初始配方状态(Initial Recipe State)
- 验证配方标题输入框初始值为 "Make Your Recipe";
- 验证烹饪时间下拉框默认选中 "45 min";
- 验证技能等级下拉框默认选中 "Intermediate";
- 验证默认食材正确显示:
- Carrots(3 large, grated),带 🥕 胡萝卜 emoji;
- All-Purpose Flour(2 cups),带 🌾 小麦 emoji;
- 验证默认步骤正确显示:"Preheat oven to 350 F"。
以上默认值全部来自 types.ts 中导出的INITIAL_RECIPE常量。QA 时可以对照源码逐字段断言,尤其注意cooking_time默认值是通过cookingTimeValues索引映射到下拉框选项的(见 recipe-card.tsx),该映射逻辑正是"45 min"正确显示的保证。
2. 建议(Suggestions)
- 验证 "Create Italian recipe" 建议可见;
- 验证 "Make it healthier" 建议可见;
- 验证 "Suggest variations" 建议可见。
三条建议通过useConfigureSuggestions注册,available: "always"(page.tsx),点击建议会直接把对应的message作为用户消息发送给 Agent。
3. 配方编辑(本地状态,Local State)
- 编辑配方标题,验证输入即时更新;
- 修改技能等级下拉框,验证选项即时更新;
- 修改烹饪时间下拉框,验证选项即时更新;
- 切换饮食偏好复选按钮(如 "Vegetarian"),验证其变为选中态;
- 点击 "+ Add Ingredient"(
data-testid="add-ingredient-button"),验证新增一行空白食材行; - 编辑食材的名称与用量;
- 点击 "x" 按钮移除某个食材;
- 点击 "+ Add Step" 验证新增一条步骤输入行;
- 编辑步骤文本并验证保存生效;
- 点击 "x" 按钮移除某个步骤。
表单是一个纯受控组件:RecipeCard接收recipe作为 props,任何改动都通过update(partial)→onChange(next)合并出新对象,再经handleChange写入agent.setState({ recipe: next })(page.tsx)。因此"本地编辑"本质上是"写进 Agent 共享状态",而不是游离于状态之外的本地临时数据——这正是下一节 AI 更新能感知编辑结果的前提。QA 时可结合 recipe-card.tsx 中updateIngredient、updateInstruction的实现来设计边界用例(如连续快速编辑、清空后恢复)。
4. AI 驱动的配方更新(useAgent 结合共享状态)
- 点击 "Create Italian recipe" 建议;
- 验证 Agent 更新了配方标题、食材与步骤;
- 验证被修改的区块出现 ping(高亮)指示器;
- 验证 "Improve with AI" 按钮(
data-testid="improve-button")在加载期间文案变为 "Please Wait..."; - 点击 "Improve with AI",验证配方被增强。
这里涉及两条消息路径:
- 点击建议:消息进入对话,Agent 读取
agent.state.recipe后给出新配方;前端通过useAgent订阅OnStateChanged与OnRunStatusChanged(page.tsx),任何状态变化都会触发重渲染。 - 点击 "Improve with AI":
handleImprove先通过agent.addMessage注入一条用户消息 "Improve the recipe",再调用copilotkit.runAgent({ agent })显式触发一次 Agent 运行(page.tsx),并且运行期间isRunning为真,按钮被禁用并显示 Spinner + "Please Wait..."(recipe-card.tsx)。
由于本场景后端不写状态,所谓"AI 更新配方"是 Agent 在回复中给出新的配方内容后,由前端将这些内容重新写回agent.setState(或由用户在表单中采纳),这是与 read-write 模式的关键差异。
5. Agent 读取前端状态(Agent Reads Frontend State)
- 编辑配方(如修改标题、新增食材);
- 向 Agent 提问 "What recipe am I making?";
- 验证 Agent 的回复内容引用了当前配方状态。
这是整个只读模式最有代表性的验收点:Agent 无需前端把上下文塞进消息,而是直接读取共享的agent.state.recipe就能准确回答"当前在做什么菜"。对应的自动化断言已固化在 tests/e2e/shared-state-read.spec.ts 中:向输入框填入 "What recipe am I making?" 并回车后,断言助手消息(data-testid="copilot-assistant-message")在 30 秒内出现。QA 时可以在此基础上进一步断言回复文本包含刚编辑过的标题或食材名,以验证"引用的是当前状态"而非缓存的旧数据。
三、错误处理
- 发送空消息,验证能被优雅处理(不崩溃、不无限 loading);
- 正常使用过程中无 console 报错;
- 验证 "Improve with AI" 按钮在加载期间处于禁用态。
注意按钮禁用态已在 recipe-card 中由disabled={isLoading}保证(recipe-card.tsx),同时handleImprove内部还有if (agent.isRunning) return;的防御性检查(page.tsx),防止重复触发并发运行。QA 时应重点验证"快速连点"与"运行中点建议"这两种竞态场景均不会产生重复请求或状态错乱。
四、预期结果(验收标准)
| 验收项 | 标准 |
|---|---|
| 加载性能 | 配方卡片与侧边栏在 3 秒内加载完成 |
| 响应性能 | Agent 在 10 秒内完成回复 |
| 状态一致性 | 配方状态在 UI 与 Agent 之间双向同步(UI → Agent 发布,Agent 读取后回显到对话) |
| 变更可视化 | ping 指示器正确高亮被 Agent 修改过的区块 |
| UI 稳定性 | 无 UI 错误、无布局破损 |
需要说明的是,"双向同步"在此场景中是指"UI 写入 → Agent 读取 → Agent 回复反映该状态"的完整闭环,与 write 模式下"Agent 主动回写状态"的含义不同;两者的对比可以参考同目录的 shared-state-read-write.md(UI 写偏好、Agent 用工具写笔记)与 shared-state-streaming.md(Agent 逐 token 向 UI 流式推送状态增量)。
五、源码级测试与复现路径
- 端到端测试:tests/e2e/shared-state-read.spec.ts 覆盖了四条核心链路——配方卡片与侧边栏加载、三条建议渲染、"+ Add Ingredient" 追加行、侧边栏消息得到助手回复,可作为手工 QA 的自动化兜底。
- 前端实现:page.tsx 与 recipe-card.tsx 提供了所有
data-testid钩子(recipe-card、add-ingredient-button、ingredients-container、ingredient-card、instructions-container、improve-button),QA 脚本可以直接复用。 - 状态与数据契约:types.ts 中的
SkillLevel、CookingTime、SpecialPreferences、Ingredient、RecipeData、RecipeAgentState与INITIAL_RECIPE是全部断言的事实基准。 - 后端接线:route.ts 中
shared-state-read → sample_agent的映射、langgraph.json 的图注册,以及 agent.py 中挂载CopilotKitMiddleware的create_agent调用,共同构成了"前端状态能到达 Agent 上下文"的运行时链路。
结语
Shared State(只读)模式适合所有"Agent 需要感知当前 UI 状态但不应篡改业务数据"的场景,例如文档编辑器、表单填写、配置面板与任务清单。以本文的 QA 清单为骨架,配合仓库中的源码与 Playwright 用例,你可以在 30 分钟内完成对shared-state-read场景的完整验收,并将同样的验证思路迁移到 shared-state-read-write 等相邻 demo,逐步建立起覆盖读写双向、流式推送等变体的共享状态测试体系。
【免费下载链接】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),仅供参考