CopilotKit × Langroid 集成实战:In-App 人工审批(HITL In-App)功能实现与 QA 验证全解析
CopilotKit × Langroid 集成实战In-App 人工审批HITL In-App功能实现与 QA 验证全解析【免费下载链接】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本文以仓库内 QA 文档 hitl-in-app.md 为骨架结合 Langroid 集成 showcase 的前端源码与 Playwright 端到端测试完整讲解应用内人工审批Human-in-the-Loop In-App这一交互模式的测试要点、前端实现原理与验证方法。读完本文你将掌握基于useFrontendTool的异步 Promise 工具如何在聊天之外弹出应用级审批弹窗以及如何用确定性测试资产验证通过/拒绝双分支行为。一、这是什么在应用内、而非聊天框内的审批弹窗HITLHuman-in-the-Loop人在环路的核心诉求是当 Agent 即将执行影响客户的高风险操作退款、降级、升级时必须先获得真人操作员的批准。传统做法是把确认按钮渲染在聊天气泡内部而本 Demo 采用的思路是——审批对话框出现在聊天界面之外的应用层面覆盖在页面之上与聊天记录完全分离。该 Demo 位于 Langroid 集成 showcase 中注册在 manifest.yaml 的hitl-in-app条目下路由为/demos/hitl-in-app官方描述为In-App Human in the LoopAgent 通过异步useFrontendTool请求审批UI 以应用级模态框弹出。QA 文档给出了完整的验收路径步骤操作预期结果1访问/demos/hitl-in-app工单面板渲染出工单 #12345、#12346、#123472提问Please approve a $50 refund to Jordan Rivera on ticket #12345.弹出审批对话框approval-dialogtestid且出现在聊天界面之外3点击 Approve对话框关闭Agent 在聊天中确认该决策4对 Reject 重复上述流程同样关闭对话框并由 Agent 确认拒绝结果这三张工单并非随机的演示数据它们被硬编码在 tickets-panel.tsx 的SUPPORT_TICKETS数组中分别覆盖退款 $50Jordan Rivera#12345、降级到 Starter 计划Priya Shah#12346与支付卡在 pending 需升级处理Morgan Lee#12347三类典型客服场景为 Agent 提供真实可引用的上下文。二、核心机制useFrontendTool的异步 Promise 握手本 Demo 在架构上属于仓库 route.ts 注释中所说的 Frontend-tools variants后端无需注册任何专属工具前端通过useFrontendTool注册处理函数Agent 只需声明调用该工具。这构成了 HITL In-App 与同目录下hitl-in-chat通过useHumanInTheLoop在聊天气泡内联渲染最本质的区别。先看 page.tsx 中工具的注册与处理器useFrontendTool({ name: request_user_approval, description: Ask the operator to approve or reject an action before you take it. The operator will respond via an in-app modal dialog that appears OUTSIDE the chat surface. The tool returns an object of the shape { approved: boolean, reason?: string }., parameters: z.object({ message: z .string() .describe(Short summary of the action needing approval (include concrete numbers / IDs).), context: z .string() .optional() .describe(Optional extra context — e.g. the ticket ID or policy rule.), }), handler: async ({ message, context }: { message: string; context?: string }) { return await new Promise{ approved: boolean; reason?: string }((resolve) { setDialog({ open: true, pending: { message, context }, resolve }); }); }, });关键点在于 handler 的返回值它不直接返回结果而是返回一个 Promise并将resolve函数存入组件状态。这个 Promise 会一直挂起直到用户在弹窗里点击 Approve 或 Reject——此时弹窗回调携带{ approved: boolean, reason?: string }调用resolveHandler 才真正完成结果作为工具返回值交还给 Agent。这正是人工审批挂起与恢复的核心握手协议Agent 发起工具调用 request_user_approval(message, context) │ ▼ handler 返回 pending Promiseresolve 存入 React 状态 │ ▼ 页面渲染 ApprovalDialogportal 到 body位于聊天之外 │ ▼ 用户点击 Approve / Reject ──► resolve({ approved, reason? }) │ ▼ handler 完成工具结果回传 AgentAgent 在聊天中确认参数定义使用zod的z.object描述工具入参message必填要求 Agent 给出包含具体数字与 ID 的操作摘要context可选用于携带工单 ID、策略规则等附加背景。前端状态的类型设计page.tsx 的DialogState显式表达了关闭与打开含 pending 参数与 resolve 函数两种形态保证任何时刻至多存在一个审批中的请求。三、审批弹窗如何跳出聊天界面createPortal 到body审批对话框定义在 approval-dialog.tsx它之所以能出现在聊天之外关键在于最后一行的createPortal(content, document.body)return createPortal(content, document.body);React Portal 会把 DOM 节点挂载到document.body下而不是组件树中聊天组件的内部因此弹窗成为页面级模态框。组件内部实现了完整的无障碍与交互细节useEffect挂载后再渲染规避服务端渲染与客户端水合时的 DOM 不一致遮罩层带data-testidapproval-dialog-overlay内容卡片带data-testidapproval-dialog并声明roledialog、aria-modaltrue展示 Agent 传回的message与可选context提供一个可选的Note文本域data-testidapproval-dialog-reason操作员可附加一句说明Agent 审批结果中会收到Approveapproval-dialog-approve与 Rejectapproval-dialog-reject两个按钮分别以{ approved: true/false, reason }触发onResolve。回到 page.tsx 的handleResolveconst handleResolve (result: { approved: boolean; reason?: string }) { if (dialog.open) { dialog.resolve(result); setDialog({ open: false }); } };点击按钮后先完成对 Agent 的 Promise 回执再将弹窗状态关闭保证审批结果先送达、界面后收起的顺序。页面布局上TicketsPanel负责展示工单列表每个工单带data-testidticket-12345之类的标识CopilotPopup作为聊天浮窗挂载而当dialog.open为真时ApprovalDialog才会渲染——这正是 QA 步骤中对话框出现在聊天之外的界面来源。四、聊天入口建议药丸与工单的联动为了让测试者及真实用户快速触发审批流Demo 通过useConfigureSuggestions预设了三个建议药丸见 suggestions.tsuseConfigureSuggestions({ suggestions: [ { title: Approve refund for #12345, message: Please approve a $50 refund to Jordan Rivera on ticket #12345 for the duplicate charge. }, { title: Downgrade plan for #12346, message: Please downgrade Priya Shah (#12346) to the Starter plan effective next billing cycle. }, { title: Escalate ticket #12347, message: Please escalate ticket #12347 to the payments team — Morgan Lees payment is stuck. }, ], available: always, });三个药丸标题与 QA 步骤 2 中的提问Please approve a $50 refund to Jordan Rivera on ticket #12345.一一对应available: always表示建议在对话任意阶段常驻可用。这样既降低了演示的门槛也为端到端测试提供了稳定的点击入口。五、端到端验证确定性双分支测试资产QA 文档的验收要点在 tests/e2e/hitl-in-app.spec.ts 中被翻译为可执行的 Playwright 用例。这套测试有几个值得借鉴的设计1. Portal 契约断言。测试使用body [data-testidapproval-dialog-overlay]定位符显式断言弹窗是body的直接子节点从 DOM 结构层面锁定了弹窗在聊天之外这一契约同时用toHaveCount(0)验证弹窗在初始状态与点击后正确卸载。2. 确定性 fixture 的双分支。测试注释说明了验证策略aimock 的确定性夹具为每个药丸生成两个分支的后续对话sequenceIndex0 对应 approve 回复、1 对应 reject 回复并用断言文本差异确保 approve/reject 真正影响对话走向——例如 approve 后助手回复以I am processing the $50 refund开头reject 后回复包含refund request was not approved升级场景则分别为Escalated ticket #12347与Not escalated。这种断言文本不对称的设计保证了测试不会出现两边同夹具的假阳性。3. serial 模式加载顺序。由于sequenceIndex匹配器按整个进程计数测试块声明了test.describe.configure({ mode: serial })强制 approve 用例先于 reject 用例执行确保分支夹具配对正确。4. 覆盖多种交互组合。除单次退款 approve/reject外还包含升级 #12347 approve/reject以及一个回归用例连续点击两个药丸先退款 #12345 再升级 #12347验证第二个药丸会挂载属于自己的全新审批对话框防止前一次审批残留污染后续流程。降级流程#12346因上游问题被显式跳过并留有 TODO 注释这种诚实标记未覆盖场景的做法同样值得参考。所有测试均设置了 60 秒级的弹窗等待超时与最长 240 秒的整体超时以容纳完整的 Agent 工具调用与回复流。六、如何运行与验证该 Demo 随 Langroid 集成 showcase 一起提供运行入口在仓库根目录的 README.md 与 package.json。验证路径分为两层手工 QA按 qa/hitl-in-app.md 的四个步骤逐条执行——打开/demos/hitl-in-app确认三张工单渲染、聊天输入框可用、无已打开的弹窗点击或输入退款药丸确认弹窗出现在聊天之外分别点击 Approve 与 Reject观察弹窗关闭且 Agent 在聊天中给出对应确认。自动化回归运行 Playwright 测试套件中的hitl-in-app用例其断言矩阵覆盖页面加载药丸引用工单退款/升级 × approve/reject以及连续多药丸各自弹出独立对话框等场景可作为 CI 中防止该交互回归的守卫。七、小结可复用的应用内审批范式从本 Demo 可以提炼出一个可复用的模式凡是需要在聊天之外、以应用级弹窗收集真人决策的交互都可以用useFrontendTool注册返回 Promise 的异步 handler createPortal挂载应用级模态框 resolve回执工具结果三步实现。前端只负责 UI 与状态Agent 无需感知弹窗存在仅通过一次工具调用的返回值{ approved, reason? }即可拿到审批结论——这与仓库内同目录的hitl-in-chat聊天气泡内联审批形成鲜明对照二者分别对应应用级审批台与对话内确认两种产品形态开发者可按实际业务场景择优选用。【免费下载链接】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),仅供参考