Comp AI CRM 中的 AI 工具审批实战:AI Elements Confirmation 组件与 AI SDK 工具审批工作流详解
2026/9/24 18:02:35 网站建设 项目流程
  • 后端
  • 前端
  • CRM
  • 人工智能
  • AI Agent

【免费下载链接】crm

Comp AI CRM is an open source, CRM designed for AI agents. Agentic-first CRM.

项目地址:https://gitcode.com/gh_mirrors/crm48/crm
点击查看免费下载

本篇文章以 Comp AI CRM 仓库中锁定的 ai-elements 技能集(见 skills-lock.json)为背景,深入讲解Confirmation组件——一个基于 alert 语义、专门用于管理 AI 工具执行审批工作流的 React 组件。你将掌握如何安装该组件、如何与 AI SDK 的useChat/streamText集成实现"危险工具执行前需人工确认"的完整闭环,理解approval-requestedapproval-respondedoutput-deniedoutput-available四类状态的渲染规则,以及全部子组件的 Props 契约,并学会基于仓库内的真实示例脚本(confirmation.tsx 等)快速落地生产级审批 UI。

一、Confirmation 组件是什么

Confirmation是 AI Elements 组件库中的一个 alert 型组件,专门用于承载"工具执行审批工作流"的三种关键状态:请求(request)接受(accept)拒绝(reject)。它解决的场景非常具体:当 AI Agent 即将执行一个具有破坏性或高风险的工具调用(例如删除文件、操作生产数据库、发送邮件)时,需要先向用户展示审批请求;用户做出决定后,组件再展示对应的执行结果。

组件背景:AI Elements 是基于 shadcn/ui 中有完整说明。该技能在本仓库中以ai-elements条目被锁定(来源为vercel/ai-elements,见 skills-lock.json),意味着 Comp AI CRM 的 Agent 开发工作流可以直接参考这套组件规范来构建聊天与工具审批界面。

该组件的核心设计目标:

  • 在工具执行前展示审批请求及"批准 / 拒绝"操作按钮;
  • 在用户响应后展示审批结果(批准或拒绝)与工具输出;
  • 通过 props 驱动的状态管理,而非组件内部维护状态,便于与 AI SDK 的消息流(ToolUIPart)天然对齐。

二、安装与前置条件

安装Confirmation组件只需一条命令:

npx ai-elements@latest add confirmation

如果你的项目使用 pnpm 或 bun 作为包管理器,请替换为对应的 runner(原文档与 SKILL.md 均强调这一点):

pnpm dlx ai-elements@latest add confirmation bunx --bun ai-elements@latest add confirmation

命令执行后,组件源码(而非编译产物)会被下载并放入你项目的 components 目录——默认是@/components/ai-elements/(或你在 shadcn/ui 的components.json中配置的目录)。这正是 AI Elements 的"代码即配置"哲学:安装完成后,你可以直接打开组件文件阅读实现或按需修改。

前置环境要求(来自 SKILL.md):

  • Node.js 18 或更高版本
  • Next.js 项目且已安装 AI SDK(组件示例依赖@ai-sdk/reactuseChataistreamTextDefaultChatTransport);
  • shadcn/ui 已安装(未安装时,执行安装命令会自动帮你装好);
  • 若组件样式不生效,请确认globals.css已按 Tailwind 4 规范引入 shadcn/ui 基础样式。

三、端到端集成示例:让危险工具执行前必须获得用户确认

原文档给出了一套完整的前后端集成示例(删除文件场景),这也是理解Confirmation组件如何嵌入真实 Agent 工作流的最佳入口。下面将完整呈现并逐段拆解。

3.1 前端:构建带审批的聊天 UI

将以下组件加入你的前端页面(原文档路径为app/page.tsx):

"use client"; import { useChat } from "@ai-sdk/react"; import { DefaultChatTransport, type ToolUIPart } from "ai"; import { useState } from "react"; import { CheckIcon, XIcon } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Confirmation, ConfirmationTitle, ConfirmationRequest, ConfirmationAccepted, ConfirmationRejected, ConfirmationActions, ConfirmationAction, } from "@/components/ai-elements/confirmation"; import { MessageResponse } from "@/components/ai-elements/message"; type DeleteFileInput = { filePath: string; confirm: boolean; }; type DeleteFileToolUIPart = ToolUIPart<{ delete_file: { input: DeleteFileInput; output: { success: boolean; message: string }; }; }>; const Example = () => { const { messages, sendMessage, status, addToolApprovalResponse } = useChat({ transport: new DefaultChatTransport({ api: "/api/chat", }), }); const handleDeleteFile = () => { sendMessage({ text: "Delete the file at /tmp/example.txt" }); }; const latestMessage = messages[messages.length - 1]; const deleteTool = latestMessage?.parts?.find( (part) => part.type === "tool-delete_file" ) as DeleteFileToolUIPart | undefined; return ( <div className="max-w-4xl mx-auto p-6 relative size-full rounded-lg border h-[600px]"> <div className="flex flex-col h-full space-y-4"> <Button onClick={handleDeleteFile} disabled={status !== "ready"}> Delete Example File </Button> {deleteTool?.approval && ( <Confirmation approval={deleteTool.approval} state={deleteTool.state}> <ConfirmationRequest> This tool wants to delete:{" "} <code>{deleteTool.input?.filePath}</code> <br /> Do you approve this action? </ConfirmationRequest> <ConfirmationAccepted> <CheckIcon className="size-4" /> <span>You approved this tool execution</span> </ConfirmationAccepted> <ConfirmationRejected> <XIcon className="size-4" /> <span>You rejected this tool execution</span> </ConfirmationRejected> <ConfirmationActions> <ConfirmationAction variant="outline" onClick={() => addToolApprovalResponse({ id: deleteTool.approval!.id, approved: false, }) } > Reject </ConfirmationAction> <ConfirmationAction variant="default" onClick={() => addToolApprovalResponse({ id: deleteTool.approval!.id, approved: true, }) } > Approve </ConfirmationAction> </ConfirmationActions> </Confirmation> )} {deleteTool?.output && ( <MessageResponse> {deleteTool.output.success ? deleteTool.output.message : `Error: ${deleteTool.output.message}`} </MessageResponse> )} </div> </div> ); }; export default Example;

关键机制拆解:

  1. 消息流中定位工具片段latestMessage.parts.find((part) => part.type === "tool-delete_file")从最新消息中取出delete_file工具对应的ToolUIPart。AI SDK 会以tool-<toolName>的形式为每个工具调用生成 UI 片段。
  2. 条件渲染deleteTool?.approval &&保证只有服务端真的发起了审批请求(approval对象存在)时才渲染ConfirmationdeleteTool?.output存在时才渲染执行结果。
  3. 用户决策回传addToolApprovalResponse({ id, approved })将用户的批准/拒绝结果按审批 ID 回传给聊天流。参考仓库中的示例脚本 confirmation.tsx,生产环境中对应的回调语义是respondToConfirmationRequest(以approved: true/false作为参数)——无论哪种命名,核心都是"把用户的决策绑定到具体的审批 ID 上"。
  4. 状态驱动 UIConfirmation接收approval(审批对象)与state(工具当前状态)两个 prop,内部依据它们决定渲染ConfirmationRequest(请求中)、ConfirmationAccepted(已批准)还是ConfirmationRejected(已拒绝)的内容块,以及是否显示ConfirmationActions操作按钮组。

3.2 后端:用 requireApproval 开启审批工作流

将以下路由加入后端(原文档路径为app/api/chat/route.tsx):

import { streamText, UIMessage, convertToModelMessages } from "ai"; import { z } from "zod"; // Allow streaming responses up to 30 seconds export const maxDuration = 30; export async function POST(req: Request) { const { messages }: { messages: UIMessage[] } = await req.json(); const result = streamText({ model: "openai/gpt-4o", messages: await convertToModelMessages(messages), tools: { delete_file: { description: "Delete a file from the file system", parameters: z.object({ filePath: z.string().describe("The path to the file to delete"), confirm: z .boolean() .default(false) .describe("Confirmation that the user wants to delete the file"), }), requireApproval: true, // Enable approval workflow execute: async ({ filePath, confirm }) => { if (!confirm) { return { success: false, message: "Deletion not confirmed", }; } // Simulate file deletion await new Promise((resolve) => setTimeout(resolve, 500)); return { success: true, message: `Successfully deleted ${filePath}`, }; }, }, }, }); return result.toUIMessageStreamResponse(); }

后端要点:

  • requireApproval: true是审批工作流的总开关。开启后,AI SDK 会在模型请求调用delete_file工具时暂停执行,生成一个带有approval.id的审批请求并推送到前端;只有前端通过addToolApprovalResponse回传approved: true,工具的execute才会真正运行。
  • 双保险的confirm参数:工具参数中的confirm: z.boolean().default(false)与审批流程形成纵深防御——即使审批环节被绕过或出现竞态,execute内还会二次校验confirm是否为true,否则直接返回success: false, message: "Deletion not confirmed"。这种"UI 审批 + 参数校验"的双层设计,是生产级 Agent 应用中防止危险操作误执行的常见模式。
  • convertToModelMessages将前端传来的UIMessage[](包含工具片段与审批结果)转换为模型可读的消息格式,保证审批决策能正确参与下一轮对话上下文。
  • toUIMessageStreamResponse()streamText结果以 UI 消息流的形式流式返回,前端DefaultChatTransport借此实时消费工具状态与输出。

四、状态机:组件的渲染决策规则

Confirmation组件通过stateprop 感知工具片段的当前状态。AI SDK 的ToolUIPart可能处于以下六种状态之一:

状态含义Confirmation 是否渲染
input-streaming工具输入参数正在流式生成
input-available工具输入已就绪,尚未请求执行
approval-requested已请求用户审批是(渲染请求内容与操作按钮)
approval-responded用户已作出审批决定是(渲染批准/拒绝结果)
output-denied审批被拒绝,输出被拦截是(渲染拒绝结果)
output-available审批通过,工具输出可用是(渲染批准结果)

从原文档的 Props 表与示例脚本可以确认如下渲染逻辑(仓库示例见 confirmation.tsx、confirmation-accepted.tsx、confirmation-rejected.tsx):

  • ConfirmationRequest:仅在approval-requested状态渲染,展示工具想要执行的操作说明;
  • ConfirmationAccepted:仅在approval.approved === true且状态为approval-respondedoutput-available时渲染,展示"已批准"的确认信息(通常搭配CheckIcon);
  • ConfirmationRejected:仅在approval.approved === false且状态为output-denied时渲染,展示"已拒绝"的提示(通常搭配XIcon,示例中使用text-destructive语义色);
  • ConfirmationActions:仅在approval-requested状态渲染,承载 "Approve / Reject" 两个操作按钮。

这种"内容块全部声明、由状态决定显隐"的组合式设计,使得组件可以无缝复用在审批流程的各个阶段,而无需在业务代码里手写大量if/else分支。

五、三种典型状态的可运行示例

原文档在 Examples 一节给出了三种典型状态,仓库的 scripts 目录中提供了对应的可运行 TSX 示例,均使用nanoid生成审批 ID,方便直接对照:

5.1 审批请求状态(approval-requested)

示例文件:confirmation-request.tsx

<Confirmation approval={{ id: nanoid() }} state="approval-requested"> {/* ...ConfirmationTitle / Request / Accepted / Rejected / Actions... */} </Confirmation>

state === "approval-requested"时,组件展示审批请求与操作按钮。仓库示例还演示了一个贴近生产数据库场景的变体——请求中嵌入待执行的 SQL 语句块:

<ConfirmationRequest> This tool wants to execute a query on the production database: <code className="mt-2 block rounded bg-muted p-2 text-sm"> SELECT * FROM users WHERE role = &apos;admin&apos; </code> </ConfirmationRequest>

这说明ConfirmationRequestchildren是任意 ReactNode,可以自由放入code块、表格、描述列表等内容,非常适合展示"将要执行的危险操作详情"。

5.2 已批准状态(approval-responded / output-available)

示例文件:confirmation-accepted.tsx

<Confirmation approval={{ approved: true, id: nanoid() }} state="approval-responded" > <ConfirmationTitle> {/* ...Request / Accepted / Rejected... */} </ConfirmationTitle> </Confirmation>

用户点击 Approve 后,approval.approved变为true,组件不再渲染操作按钮,仅展示ConfirmationAccepted中的"已批准"状态(CheckIcon+ 文案)。示例中图标使用了text-green-600 dark:text-green-400,随主题明暗自适应。

5.3 已拒绝状态(output-denied)

示例文件:confirmation-rejected.tsx

<Confirmation approval={{ approved: false, id: nanoid() }} state="output-denied" > {/* ...同样声明全部内容块,由状态驱动显隐... */} </Confirmation>

用户点击 Reject 后进入output-deniedConfirmationRejected生效,展示拒绝提示(XIcon+text-destructive语义色)。此时服务端将跳过工具执行、拦截输出,前端也不会出现deleteTool.output结果块(对应 3.1 中deleteTool?.output &&的条件分支不成立)。

六、Props 完整参考

以下为原文档给出的全部 Props 契约,完整继承并附上补充说明。

<Confirmation />(根组件,基于 shadcn/ui Alert)

Prop类型默认值说明
approvalToolUIPart["approval"]-审批对象,包含审批 ID 与审批状态(approved)。未提供或为undefined时组件不渲染。
stateToolUIPart["state"]-工具当前状态:input-streaminginput-availableapproval-requestedapproval-respondedoutput-deniedoutput-availableinput-streaminginput-available状态下不渲染。
classNamestring-应用到 Alert 容器的额外 CSS 类。
...propsReact.ComponentProps<typeof Alert>-其余 props 全部透传给 Alert 组件(Alert 本身继承HTMLAttributes<HTMLDivElement>,因此idaria-*data-*等原生属性均可使用)。

<ConfirmationTitle />(基于 AlertDescription)

用于在审批 alert 内展示标题或标签的样式化描述元素。

Prop类型默认值说明
...propsReact.ComponentProps<typeof AlertDescription>-其余 props 全部透传给 AlertDescription 组件。

<ConfirmationRequest />(请求内容块)

Prop类型默认值说明
childrenReact.ReactNode-审批请求阶段展示的内容。仅在状态为approval-requested时渲染。

<ConfirmationAccepted />(已批准内容块)

Prop类型默认值说明
childrenReact.ReactNode-审批通过后展示的内容。仅在approval.approved === true且状态为approval-respondedoutput-available时渲染。

<ConfirmationRejected />(已拒绝内容块)

Prop类型默认值说明
childrenReact.ReactNode-审批被拒后展示的内容。仅在approval.approved === false且状态为output-denied时渲染。

<ConfirmationActions />(操作按钮容器)

Prop类型默认值说明
classNamestring-应用到操作容器 div 的额外 CSS 类。
...propsReact.ComponentProps<"div">-其余 props 透传给 div 元素。仅在状态为approval-requested时渲染。

<ConfirmationAction />(单个操作按钮,基于 shadcn/ui Button)

Prop类型默认值说明
...propsReact.ComponentProps<typeof Button>-其余 props 全部透传给 Button 组件。默认带有h-8 px-3 text-sm尺寸样式,示例中通过variant="outline"(Reject)与variant="default"(Approve)区分主次。

七、特性总览与无障碍

原文档列出的特性清单,对应到实际能力:

  • 基于 Context 的状态管理:审批工作流的状态由approval/stateprops 驱动,组件内部通过上下文协调各内容块的显隐;
  • 按审批状态条件渲染:请求、批准、拒绝三种内容块可同时声明,由状态自动决定渲染哪个;
  • 覆盖四种关键状态approval-requestedapproval-respondedoutput-deniedoutput-available
  • 基于 shadcn/ui 的 Alert 与 Button 构建:视觉与交互语义与 shadcn/ui 生态一致;
  • TypeScript 全面类型支持ToolUIPart泛型约束可精确描述工具输入输出类型(如示例中的DeleteFileToolUIPart);
  • Tailwind CSS 可定制样式:组件代码就在项目内,可直接调整类名;
  • 键盘导航与无障碍支持:继承自 shadcn/ui Alert / Button 的可访问性实现;
  • 主题感知、自动暗色模式:示例中text-green-600 dark:text-green-400text-destructive等语义色会自动适配明暗主题。

八、在本仓库中的定位与扩展建议

  • 技能来源锁定:本仓库通过 skills-lock.json 锁定了ai-elements技能(来源vercel/ai-elements),因此.agents/skills/ai-elements/目录下的 SKILL.md、references/confirmation.mdscripts/示例共同构成了 Comp AI CRM 中构建 Agent 聊天与工具审批界面的标准参考。
  • 代码即配置的扩展方式:安装后组件源码位于@/components/ai-elements/confirmation.tsx(对应项目内路径)。由于代码直接落入你的项目而非封装在库里,你可以像 SKILL.md 中演示的那样直接编辑组件源码——例如移除圆角、替换图标、调整按钮尺寸等,所有修改即时生效。
  • 与 CRM Agent 场景的结合:作为 Agentic-first 的 CRM,Comp AI CRM 的 Agent(如删除记录、批量修改字段、发送消息等工具调用)完全可以在前端复用这套审批模式:把delete_file换成delete_contact/archive_deal,把filePath换成 CRM 记录 ID,即可为危险操作加上"人工确认"的安全闸门。审批请求内容块中甚至可以渲染记录的摘要信息,让用户在做决定前看到完整上下文。

实用排查提示(源自 SKILL.md):若组件样式缺失,检查globals.css是否正确引入 Tailwind 4 + shadcn/ui 基础样式;若 CLI 未添加文件,确认当前目录是项目根目录、components.json配置正确;若@/导入报 "module not found",检查tsconfig.json是否配置了"@/*": ["./*"]路径别名;若主题切换不生效,确保应用使用data-theme属性切换机制。

九、小结

Confirmation组件把"AI 工具执行前的人工审批"这个高频需求抽象为声明式、状态驱动的 UI 原语:前端通过useChataddToolApprovalResponse回传用户决策,后端通过streamTextrequireApproval: true挂起危险工具的执行,两者配合即可在几分钟内为任何 Agent 应用补齐安全审批闭环。结合仓库内的 SKILL.md、references/confirmation.md 与 scripts 下的四个示例文件,你可以直接对照实现,并根据项目需要自由定制组件样式与内容块。

  • 后端
  • 前端
  • CRM
  • 人工智能
  • AI Agent

【免费下载链接】crm

Comp AI CRM is an open source, CRM designed for AI agents. Agentic-first CRM.

项目地址:https://gitcode.com/gh_mirrors/crm48/crm
点击查看免费下载

相关推荐

上一篇:OpenCLI 测试指南:四层测试体系、本地最小充分验证策略与 E2E 实战手册
下一篇:Terminal.Gui项目中的Scheme机制深度解析

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询