ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

CopilotKit 实战:用 Playwright 为 Oracle Agent Spec × AG-UI 智能体搭建端到端测试体系

CopilotKit 实战:用 Playwright 为 Oracle Agent Spec × AG-UI 智能体搭建端到端测试体系 CopilotKit 实战用 Playwright 为 Oracle Agent Spec × AG-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本文以 oracle-agent-memory 展示项目 的前端 E2E 测试为实例讲解如何用 Playwright 驱动真实的 CopilotKit V2 聊天界面对抗运行中的 Agent SpecLangGraph over AG-UI智能体与 Oracle AI 数据库完成跨会话记忆召回、单轮工具调用、HITL 预订等关键链路的端到端验证并自动录制视频。读完本文你将掌握一套可复制的真实前端 真实后端 真实数据库E2E 测试模式以及应对非确定性、异步索引和上游适配器 Bug 的实战经验。背景这套 E2E 测试验证什么关联文档 所描述的测试位于examples/showcases/oracle-agent-memory/frontend/e2e/目录其定位是不 mock、不 stub的端到端验证测试直接驱动 CopilotKit V2 的真实聊天 UI向运行中的 Agent Spec 智能体concierge一个通过 AG-UI 协议暴露的 LangGraph agent和 Oracle AI Database 发起真实请求并把每一次运行录制为视频。展示项目的技术栈见 README决定了测试的验证目标CopilotKitV2 prerelease作为前端聊天层消费 AG-UI 协议Agent Spec将智能体定义为一次性的可移植 JSON运行在 LangGraph 之上agent/concierge/agent.pyOracle AI Database / Agent Memory提供持久的跨会话语义记忆oracleagentmemoryAI Vector Search 支撑。因此这套 E2E 覆盖了三条核心链路跨会话记忆召回、单轮内服务器工具recall_memorysearch_flights的完整执行以及基于前端 ClientTool 的人工确认HITL预订。测试覆盖范围与断言策略E2E 套件包含两个 spec 文件。主文件 concierge.spec.ts 中的三个用例加上 thread-title.spec.ts 的回归用例构成了如下覆盖矩阵Spec验证点实现要点concierge.spec.ts› recalls a preference in a brand-new session一个线程中教导的唯一事实FlyHigh-ts项目 →ZEPHYR-ts号码点击 New thread后同一浏览器会话、全新对话仍能被召回——即 Agent Spec 栈中基于用户作用域的持久记忆经由 Oracle。该用例第一个运行以见到刚重置过的存储先写入事实轮询wait-until-searchable.py确认已可检索再开新线程用唯一 token 询问concierge.spec.ts› finds a flight in a single turn第一轮对话即端到端驱动 Agent Spec 服务器工具recall_memorysearch_flights断言检查的是规范航班$740/KLM/AMS-001、直飞的细节这些细节只可能出现在助手回复中askUntilReply匹配/740\|KLM\|AMS-001\|nonstop/i确保工具确实执行且模型呈现了结果concierge.spec.ts› confirms before booking (HITL, single-run)book_flight是前端ClientTool确认卡片 → 登机牌在单次agent run 内完成无需第二轮对话下方提到的适配器 Bug 因此永远不会被触发断言渲染出的确认卡片按钮点击后断言登机牌徽章CONFIRMED ✓concierge.spec.ts› books inline from the flight card纯客户端的选择 → 确认 → 预订路径无 agent 往返回归守卫select 无反应/确认卡片滚出屏幕Bug断言不会注入 Book me flight … 用户消息全程在FlightOptions组件内完成断言/book me flight/i计数为 0thread-title.spec.ts› a new thread is NOT named after the previous conversation回归测试新线程标题不得沿用上一个会话的名称旧的ThreadTitler在切换线程时读取了共享的agent.messages导致此 Bug通过agent.subscribe订阅事件驱动标题断言新线程默认标题为 New conversation三个concierge.spec.ts用例都以assertNoAgentError(page)收尾用正则/RUN_ERROR|agent_run_error|fetch failed|must be a response to/i快速失败——其中must be a response to正是下方已知 Bug 的 OpenAI 400 错误特征串。唯一 token 机制跨运行确定性的关键跨会话记忆用例使用Date.now()生成每次测试运行唯一的事实见 concierge.spec.tsconst RUN ${Date.now()}; const PROGRAM FlyHigh-${RUN}; const FF_NUMBER ZEPHYR-${RUN};由于所有运行共享demo-user且记忆会跨运行、跨 cookbook 项目累积这个唯一 key 让语义召回能精确锁定本次运行写入的记忆避免把旧运行的事实误当成验证结果。确定性设计内存重置与异步索引等待E2E 有两个主要的非确定性来源套件分别给出了工程化解法。global-setup 清空用户记忆global-setup.ts 在套件启动前通过e2e/reset-memory.py清空 demo-user 的持久记忆。重置脚本 reset-memory.py 的核心逻辑oracleagentmemory按USER_ID键控记录因此一个作用域化的 DELETE 即可完成干净的清理按子表优先顺序删除RECORD_CHUNKS、MEMORY、MESSAGE、THREAD四张表RECORD_CHUNKS上的VECTOR$索引会在 DML 时自动维护通过uv run --directory agent python e2e/reset-memory.py在 agent 的 venv 中执行并显式加载agent/.env脚本不在.env的祖先目录中失败非致命DB 未启动或未配置时打印提示并以 0 退出让真实问题在测试本身中暴露。原文档特别提醒重置会在每次运行时抹掉demo-user已存储的全部记忆。为什么只能召回一次模型驱动记忆的副作用concierge 通过模型驱动的recall_memory工具召回且每一轮对话包括一次失败的召回都会被持久化。这意味着如果重试召回一条我不记得的回复也会被写入记忆从而污染下一次尝试。因此跨会话用例只做一次干净的召回且在确认 post-run 的记忆写入已提交之后进行。这是 helpers 中askUntilReply默认attempts 1的直接原因详见下文重试为何危险。等待异步索引wait-until-searchable.pyAgent Spec 的记忆管线是异步的一轮对话被持久化后Oracle Agent Memory 需要提取 → 向量化 → 建索引才能被检索到。刚教导的事实并非立即可召回固定 sleep 会与异步索引管线竞争产生看起来像产品 Bug 实为延迟不足的偶发失败。因此 concierge.spec.ts 在开新线程前用 wait-until-searchable.py 轮询recall_memory实际使用的同一检索路径memory.searchSearchScope(user_idUSER)直到唯一 token 出现在结果中。将 token 放进查询词frequent flyer number token使其成为可靠的是否已索引探针token 只在事实真正入库后才可能出现在结果里不存在假阳性。脚本默认 120 秒超时、3 秒轮询间隔spec 中通过execFileSync调用并设 150 秒上限索引未就绪时抛错并附带stderr让失败信息可直接定位。Playwright 配置两个服务、一个前端、全部复用playwright.config.ts 是整套测试的编排核心其设计思路是Playwright 自己拉起并复用后端依赖端口规划concierge agent 运行在:8001——非默认端口避免与手动npm run dev启动在:8000的 agent 冲突agent 默认:8000frontend 运行在专用测试端口:3200配置通过环境变量把前端的AGENT_URL指向http://127.0.0.1:8001/run。webServer 双实例webServer: [ { command: uv run python -m uvicorn concierge.server:app --host 127.0.0.1 --port ${AGENT_PORT}, cwd: agentDir, url: http://127.0.0.1:${AGENT_PORT}/health, reuseExistingServer: !process.env.CI, timeout: 180_000, }, { command: npm run dev -- --port ${FRONTEND_PORT}, url: http://localhost:${FRONTEND_PORT}, reuseExistingServer: !process.env.CI, timeout: 120_000, env: { AGENT_URL }, }, ],其中reuseExistingServer: !process.env.CI意味着本地已运行的服务会被直接复用Playwright 检查健康端点只有 CI 中才强制全新启动。globalSetup指向 global-setup.ts在套件前执行内存清理。并发与超时策略fullyParallel: false, workers: 1, retries: 0, timeout: 300_000, expect: { timeout: 120_000 },测试共享同一个服务端记忆存储demo-user所以必须串行、单 worker、零重试。Agent Spec 的每一轮召回 工具调用 LLM都很慢跨会话用例更是连续两轮因此给出 300 秒的宽松用例超时和 120 秒的断言超时。产物录制use: { baseURL: http://localhost:${FRONTEND_PORT}, viewport: { width: 1280, height: 720 }, video: { mode: on, size: { width: 1280, height: 720 } }, trace: retain-on-failure, actionTimeout: 30_000, },每次测试都录制.webm视频test-results/test/下已被 gitignore失败时额外保留 trace。HTML 报告内嵌视频失败时附 trace可回放定位。测试辅助函数helpers.ts 的工程智慧e2e/helpers.ts 封装了所有交互原语其中的细节都源于真实调试经验稳定 test-id 与发送时机CopilotKit V2 在 composer 和发送按钮上暴露了稳定的 test idcopilot-chat-textarea、copilot-send-button。sendMessage的关键技巧是等待发送按钮 enabled而不是直接按 Enter——因为 hydration 完成前按 Enter 会 no-op产生第一次交互丢失的竞态。发送后断言输入框被清空toHaveValue()作为消息已提交的可靠信号。sendAndAwaitRun等待流关闭不等于记忆已写入const streamClosed page .waitForResponse( (r) r.url().includes(/api/copilotkit) r.request().method() POST, { timeout: 150_000 }, ) .then((r) r.finished());注意 helpers 中的醒目说明持久化已不再与流关闭耦合。concierge 在 SSE 流以RUN_FINISHED关闭之后的后台任务中写记忆所以回复完成≠记忆已写入。需要记忆可被召回的调用方必须单独轮询即跨会话用例中的wait-until-searchable.pysendAndAwaitRun只负责等待 run 结束。askUntilReply 与重试为何危险export async function askUntilReply(page, question, patterns, { attempts 1, perAttemptMs 90_000, gapMs 3_000, } {})默认attempts 1不是保守而是安全要求每次重试都会把问题作为同一线程中的新一轮重新发送。在服务器工具调用之后这个第二轮会触发上游多轮tool_call_id关联 Bug永远不可能成功——重试只保证失败。对工具驱动的提示词需要更长等待时应该提高perAttemptMs而不是attemptsattempts 1仅对纯对话非工具驱动提示词安全。newThread 与 assertNoAgentErrornewThread点击侧边栏 New thread等待 composer 可见且为空。每次新线程都会挂载一个带全新threadId的 CopilotChat 实例隔离之前的所有服务器工具状态——只需要干净对话时用它而不是开新浏览器 context。assertNoAgentError断言运行时错误文本含已知多轮 Bug 的特征串数量为 0。已知问题多轮 tool_call_id 关联 Bug 及其规避E2E 的编排深度受一个上游 Bug 影响细节记录在 agentspec-multiturn-toolcall-correlation.md现象Agent Spec 智能体在 LangGraph 运行时使用服务器工具时第一轮正常但日志出现AG-UI tool-call correlation miss警告任何使用过工具后的第二轮对话LangGraph 的 model 节点报 OpenAI 400messages with role tool must be a response to a preceeding message with tool_calls根因服务器工具调用未被记录为ToolExecutionRequest适配器用原始request_id作为代理tool_call_id发出工具结果而前端从未把该 id 与助手的tool_calls条目关联。前端下一轮重放的历史因此包含一条没有前置assistant(tool_calls)的role: tool消息被 OpenAI 拒绝。第一轮的 correlation miss 警告与第二轮的 400 是同一缺陷的两处表现。展示项目的规避策略E2E 测试正是围绕这些规避策略设计的HITL 预订做成单轮book_flight是前端 ClientTooluseHumanInTheLoop确认卡片 → 登机牌在同一次agent run 内解决无需第二轮对话跨会话召回开新线程新线程获得全新threadId属于第一轮而非跟进轮服务器端 workaroundagent/concierge/server.pymonkey-patchfilter_only_new_messages用客户端完整历史全量替换适配器的增量合并RemoveMessage(REMOVE_ALL_MESSAGES) 原样返回恢复了真正的多轮对话同时_repair_dangling_tool_calls修补被放弃的 HITL 预订留下的悬挂tool_calls。原文档明确指出这是 workaround 而非修复一旦上游适配器正确记录ToolExecutionRequest即可移除并要求锁定适配器 commit 并重新测试——这正是 E2E 套件长期存在的价值。运行 E2E从零到完整报告前置条件从仓库根目录开始需要 Oracle AI Database 已运行并完成配置完整步骤见 展示项目 READMEdocker compose up -d ./db/setup-db.shagent 的.env含OPENAI_API_KEY按 agent README 配置。其余依赖由 Playwright 配置自动启动与复用concierge agent:8001和 frontend:3200。常用命令进入前端目录后执行对应 frontend/package.json 的 scriptscd frontend npm install # 首次——拉取 playwright/test npx playwright install chromium # 首次——下载浏览器 npm run test:e2e # 无头运行录制视频 npm run test:e2e:headed # 观看它驱动浏览器 npm run test:e2e:report # 打开 HTML 报告视频 trace运行结果产物每次测试在test-results/test/下记录一个.webm视频gitignored。跨会话召回用例现在运行在单个浏览器上下文中教导 → New thread→ 召回因此和其他 spec 一样只生成一个video.webm。HTML 报告内嵌视频失败时附带 trace可以逐帧回放浏览器操作与网络行为。将这套模式复用到你的 CopilotKit 项目从该 E2E 套件可以提炼出可迁移的通用模式真实栈优先测试直面真实的 CopilotKit UI、真实的 agent 和真实的数据库不 mock 任何一层才能抓住集成级 Bug如本文中的多轮关联缺陷确定性三件套global-setup重置状态 每次运行唯一 token 轮询等待异步管线记忆索引、embedding把偶发失败变成可重复的确定性验证把上游 Bug 写进测试用assertNoAgentError的异常特征串做快速失败让 workaround 一旦失效测试立刻报警而不是等用户反馈重试策略因场景而异工具驱动提示词宁可提高单次等待窗口也不重试纯对话场景才允许多轮尝试端口规划避免冲突测试 agent 用非默认端口:8001与本地开发:8000共存reuseExistingServer保证本地与 CI 行为一致。这套设计让 frontend/e2e 不只是测试更是展示项目Agent Spec × Oracle 记忆 × CopilotKit集成健康状况的持续体检报告。【免费下载链接】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),仅供参考
返回列表