资讯动态

CopilotKit × Agno 实战:用前端工具 + 应用级模态框实现 In-App HITL 人工审批

发布时间:2026/9/12 6:55:03 来源:尧图企业网站定制
CopilotKit × Agno 实战用前端工具 应用级模态框实现 In-App HITL 人工审批【免费下载链接】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 开源仓库中 Agno 集成的hitl-in-app演示为骨架完整讲解如何在聊天界面之外、应用页面层级实现 Human-in-the-LoopHITL人工审批Agent 在执行退款、降级套餐、升级工单等影响客户的操作前会通过前端工具挂起执行并在应用顶层弹出审批对话框由操作员点击「Approve / Reject」后把结果回传给 Agent 继续执行。读完本文你将掌握useFrontendTool挂起 Promise、createPortal应用级弹窗、Agno 侧external_execution工具的完整实现链路以及对应的 QA 验证清单与 E2E 断言策略。一、什么是 In-App HITL审批弹窗位于聊天之外hitl-in-app演示的核心特征是「审批发生在应用页面层级而非聊天气泡内部」。原 QA 文档qa/hitl-in-app.md开篇即点明其定位frontend-tool app-level modal。与「在聊天流内呈现按钮」的 in-chat HITL 方案相比In-App HITL 的关键差异在于审批对话框通过 React Portal 渲染为body的直接子节点覆盖整个页面聊天面板右侧的CopilotPopup与工单面板左侧的 Support Inbox同时保持可见操作员可以在不打断对话上下文的前提下完成高风险操作的授权。因此 QA 文档将验证重点放在「approval-dialog是否出现在聊天区域之外并覆盖页面」这一条上这正是该模式与聊天内嵌 HITL 的分水岭。二、整体架构与一次审批的完整链路整个演示由前端 Next.js 应用与 Agno Python Agent 后端两部分组成。运行时通过 CopilotKit 路由 以 AG-UI 协议代理到 Agno 后端// showcase/integrations/agno/src/app/api/copilotkit/route.ts const AGENT_URL process.env.AGENT_URL || http://localhost:8000; function createMainAgent() { return new HttpAgent({ url: ${AGENT_URL}/agui }); }hitl-in-app这一 agent 名称被映射到主 Agentroute.ts 中的mainAgentNames数组前端通过agenthitl-in-app指定路由page.tsx。一次完整审批的调用链为用户点击建议 pill如 Approve refund for #12345消息发送给 Agno AgentAgent 依据 instructions 判断该操作影响客户调用request_user_approval工具该工具在前后端均被声明为「由前端执行」Agno 后端发出工具调用事件后暂停本轮运行前端useFrontendTool的 handler 收到调用参数返回一个挂起的 Promise其resolve被存入 React state页面据此渲染应用级审批弹窗Portal 到body操作员点击 Approve/Reject弹窗调用resolve({ approved, reason? })Promise 完成工具结果Tool Result返回给 AgentAgent 恢复运行按结果输出确认或拒绝的话术。三、前端实现useFrontendTool 挂起 Promise前端核心在 page.tsx 中。首先通过useFrontendTool注册与后端同名的工具并用zod描述参数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 }) { return await new Promise{ approved: boolean; reason?: string }( (resolve) { setDialog({ open: true, pending: { message, context }, resolve }); }, ); }, });这是整个模式的精髓所在handler 返回的 Promise 不立即完成而是把resolve函数劫持进 React state。只要弹窗未操作该 Promise 就保持 pendingAgent 的运行就被前端工具机制挂起操作员点击按钮时resolve被调用Promise 完成工具结果自动回传 Agentpage.tsx。为此页面维护了一个带判别联合discriminated union的 state把resolve与待审批内容绑定在一起type ResolveFn (value: { approved: boolean; reason?: string }) void; type DialogState | { open: false } | { open: true; pending: PendingApproval; resolve: ResolveFn };当用户点击按钮时handleResolve先调用存储的resolve(result)完成 Promise再关闭弹窗const handleResolve (result: { approved: boolean; reason?: string }) { if (dialog.open) { dialog.resolve(result); setDialog({ open: false }); } };页面布局同时渲染三部分左侧工单面板TicketsPanel、右侧CopilotPopup聊天、以及条件渲染的ApprovalDialogpage.tsx。四、应用级模态框ApprovalDialog 与 createPortalApprovalDialogapproval-dialog.tsx负责把审批 UI 提升到应用层级。其注释明确说明模态框被portal 到body而非渲染在聊天气泡树内。关键实现通过createPortal(content, document.body)完成并设置fixed inset-0 z-50全屏遮罩与roledialog、aria-modaltrue无障碍语义。同时为 QA 提供了三个稳定的测试锚点data-testidapproval-dialog-overlay遮罩层验证 Portal 位置与开闭状态data-testidapproval-dialog对话框主体QA 文档验证其出现在聊天之外data-testidapproval-dialog-reason可选备注输入框data-testidapproval-dialog-approve/approval-dialog-reject审批 / 拒绝按钮。组件还包含一个可选备注reason文本框——这对应前端工具返回结构中的reason?: string字段onClick{() onResolve({ approved: true, reason: reason.trim() || undefined, }) }useEffect中先setMounted(true)再返回内容是为了避免 SSR 时document未定义导致的报错——这是 Next.js 中使用createPortal的标准做法。E2E 测试通过body [data-testidapproval-dialog-overlay]这一选择器断言弹窗确实挂载在body下hitl-in-app.spec.ts从测试层面锁定了「应用级弹窗」这一契约。五、后端实现Agno 的 external_execution 工具在 Agno 侧同名工具定义于 main.py其签名与前端的 zod schema 一一对应tool(external_executionTrue, external_execution_silentTrue) def request_user_approval(message: str, context: str ): 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 }. 两个装饰器参数是关键external_executionTrue声明工具由外部前端执行Agno 只负责发出工具调用事件并等待结果而不是自己执行external_execution_silentTrue工具调用过程中不产生多余的事件输出保证前端只收到一次干净的工具调用。该工具被注册进主 Agent 的tools列表main.py并在 Agent 的 instructions 中明确约束了使用时机main.pyUSER APPROVAL (HITL): When asked to take any action that affects a customer — for example issuing a refund, updating a plan, cancelling a subscription, escalating a ticket, or sending a credit — call request_user_approval FIRST with a short summary and optional context. Follow the tool result: if approved, confirm in one short sentence; if rejected, acknowledge and do not retry.此外Agent 配置中db_create_session_db()与tool_call_limit15也与 HITL 相关审批期间 Agent 运行会被挂起等待前端响应需要可写会话存储以支持运行恢复见 main.py 的注释说明。六、运行前提执行 QA 测试前需满足原文档列出的两个前置条件前提说明Demo 已部署在/demos/hitl-in-app对应源码位于 hitl-in-app/page.tsx通过next dev或next start启动Agent 后端健康Agno 后端监听AGENT_URL默认http://localhost:8000路由的GET /api/copilotkit健康检查会返回agent_status字段见 route.ts七、QA 测试步骤In-App HITL 验收清单以下为原 QA 文档hitl-in-app.md的完整验收步骤并补充了可对照的测试锚点与断言细节。7.1 基础功能导航到/demos/hitl-in-app页面加载CopilotKitagenthitl-in-app并默认展开CopilotPopup。验证工单面板与三张工单渲染左侧 Support Inbox 应显示#12345Jordan Rivera退款争议金额 $50.00、#12346Priya Shah降级到 Starter、#12347Morgan Lee升级到支付团队三张工单。工单数据硬编码于 tickets-panel.tsxE2E 通过getByTestId(ticket-12345)等锚点断言可见性。验证右侧聊天正常渲染聊天输入框占位符为 Type a messageCopilotPopup的labels.chatInputPlaceholder配置。同时初始状态下不应存在任何审批弹窗approval-dialog-overlay计数为 0。7.2 功能专项检查批准路径refund #12345点击建议 pillApprove refund for #12345。三个建议 pill 由 suggestions.ts 通过useConfigureSuggestions注册available: always并带有完整消息模板例如Please approve a $50 refund to Jordan Rivera on ticket #12345 for the duplicate charge.。验证审批对话框出现在聊天之外并覆盖页面断言data-testidapproval-dialog可见且其挂载位置为body [data-testidapproval-dialog-overlay]Portal 契约。点击Approveapproval-dialog-approve。验证弹窗关闭且 Agent 继续跟进approval-dialog-overlay数量在数秒内降为 0随后聊天区出现 Agent 的确认消息E2E 断言开头短语 I am processing the $50 refund见 hitl-in-app.spec.ts。拒绝路径downgrade #12346点击建议 pillDowngrade plan for #12346消息模板要求降级到 Starter 计划、下一账单周期生效。对话框出现后点击Rejectapproval-dialog-reject。验证 Agent 确认收到拒绝拒绝结果{ approved: false, reason? }回传后Agent 应acknowledge and do not retry按 instructions 约定不再重试。E2E 中对应的拒绝分支断言短语如 refund request was not approved / Not escalatedhitl-in-app.spec.ts。7.3 错误处理无未捕获的 console 错误全程页面加载、弹窗开合、审批与拒绝控制台不得出现未捕获异常。这一点在 E2E 测试中由test.describe.configure({ mode: serial })与严格断言组合保障。八、E2E 测试要点如何真实地验证审批结果仓库为该 demo 配套了完整的 Playwright 测试 hitl-in-app.spec.ts其设计对理解本模式极有价值Serial 模式是载重结构load-bearingaimock 确定性夹具按sequenceIndex0 approve 分支、1 reject 分支匹配因此批准测试必须在拒绝测试之前运行依赖测试顺序保证分支正确。Portal 契约断言所有弹窗可见性断言都使用body [data-testidapproval-dialog-overlay]直接验证弹窗是body的子节点而非聊天树内部。多轮审批回归测试「先批准 #12345 再升级 #12347」确保每个 pill 都触发自己独立的request_user_approval调用并挂载独立弹窗——这对应一个曾修复的 aimock 多 pill bug夹具需通过toolCallId串联后续分支避免首个审批后第二个 pill 直接跳过弹窗hitl-in-app.spec.ts 的注释详述了该回归案例。已知暂缓项downgrade #12346的审批/拒绝流程测试被显式跳过TODO注释标明上游 bug 于 2026-05-07 存在超出本次改写范围QA 执行时应知悉此限制。九、常见问题与排查建议现象排查方向弹窗未出现在页面顶层而是嵌在聊天内检查是否使用createPortal(content, document.body)E2E 用body [data-testidapproval-dialog-overlay]选择器可直接定位点击 Approve/Reject 后 Agent 无后续响应确认resolve是否被正确调用、handleResolve是否先resolve再关闭弹窗后端检查request_user_approval是否带external_executionTrue审批后对话无工具结果、Agent 卡住检查 Agent 的会话存储db是否可写HITL 挂起依赖运行恢复以及tool_call_limit是否过低导致运行被截断页面首个审批完成后后续 pill 不弹窗参考多轮审批回归确保夹具按toolCallId串联后续分支而不是依赖hasToolResult布尔切换SSR 下document未定义报错使用mountedstate useEffect延迟渲染 Portal 内容approval-dialog.tsx 的标准做法十、小结In-App HITL 的本质是把「需要人工裁决的暂停点」从模型内部转移到前端应用层Agno 侧用external_execution工具声明这段代码由前端跑前端用useFrontendTool 挂起 Promise 制造暂停再用createPortal把裁决 UI 提升到页面层级。三者结合既保证了操作员拥有全页面上下文工单、金额、政策都在视野内也让 Agent 的每次高风险操作都有据可查、可控可拒。配合仓库内的 QA 清单 与 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),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价