← [ BACK_TO_NOTES ]

Human Approval UI

Human Approval UI 是前端展示 Agent 高风险操作审批请求的组件。它让用户在 Agent 执行不可逆操作前进行确认、拒绝或修改。

#concept #evergreen #frontend #ai

[!info] related notes

Human Approval UI

一句话定义

Human Approval UI 是前端展示 Agent 高风险操作审批请求的组件。执行器在需要新增授权的操作前暂停,UI 展示请求,让用户确认、拒绝或修改操作参数。

它解决什么问题

Agent 调用高风险工具时,需要人类审批。但审批不是简单的”确定/取消”弹窗:

  • 用户需要看到完整操作内容(调用什么工具、传了什么参数)
  • 用户需要理解风险(这个操作的影响范围)
  • 用户可能需要修改参数(不是拒绝,而是改一下再执行)
  • 审批需要超时处理(用户长时间不响应怎么办)

核心原理

审批卡片的信息结构

┌─────────────────────────────────────────┐
│ ⚠️ 需要确认                             │
│                                         │
│ Agent 请求执行以下操作:                  │
│                                         │
│ 🔧 删除记录                             │
│   record_ids: ["id_1", "id_2", "id_3"] │
│   count: 3 条                           │
│                                         │
│ ⚠️ 此操作不可撤销                        │
│                                         │
│ [✅ 批准]  [❌ 拒绝]  [✏️ 修改参数]      │
│                                         │
│ ⏱️ 距服务端审批截止时间: 60s                   │
└─────────────────────────────────────────┘

审批状态机

interrupt_received → pending → approved / rejected / expired(服务端原子决策)
                     │
                     └→ 修改草稿 → 新提案版本 → 重新校验与审批
approved → 执行器重新检查并消费授权 → 记录执行结果

典型工程实现

Approval Card 组件

该组件展示服务端请求,父组件按 request.id/version 设置 key 并刷新权威状态。后端仍必须鉴权、校验期限/版本并原子处理重复提交;UI 防双击不能替代这些检查。summary 是经服务端筛选的可审阅摘要,不直接展示可能含秘密的整个参数对象。

import { useRef, useState } from "react";

type Props = {
  request: { id: string; version: number; summary: string; status: string };
  onDecision: (id: string, version: number, decision: "approve" | "reject") => Promise<void>;
  onEdit: (id: string) => void;
};

export function ApprovalCard({ request, onDecision, onEdit }: Props) {
  const sending = useRef(false);
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState("");
  const disabled = busy || request.status !== "pending";

  async function submit(decision: "approve" | "reject") {
    if (sending.current || request.status !== "pending") return;
    sending.current = true;
    setBusy(true);
    setError("");
    try {
      await onDecision(request.id, request.version, decision);
      // 父组件随后用服务端权威状态替换 pending。
    } catch {
      setError("提交结果未确认,请刷新请求状态后重试。");
    } finally {
      sending.current = false;
      setBusy(false);
    }
  }

  return (
    <section aria-label="操作审批">
      <p>{request.summary}</p>
      <p>状态:{request.status}</p>
      <button disabled={disabled} onClick={() => void submit("approve")}>批准</button>
      <button disabled={disabled} onClick={() => void submit("reject")}>拒绝</button>
      <button disabled={disabled} onClick={() => onEdit(request.id)}>修改提案</button>
      {error && <p role="alert">{error}</p>}
    </section>
  );
}

常见设计模式

1. 一键审批

只有策略要求新增确认时展示;已授权的普通查询不应为了流程完整而反复弹窗。

2. 参数修改

编辑器保留原始文本草稿,提交时解析并验证 schema;修改后的参数产生新提案/版本,重新做策略与授权检查。不能在每次按键时 JSON.parse,也不能把修改直接当作已批准执行。

3. 批量审批

多个低风险操作打包成一个审批请求。

4. 上下文关联

审批卡片显示相关上下文(如要删除的记录详情)。

常见坑

  1. 审批信息不足: 用户看不到完整参数,无法判断
  2. 不做超时处理: 用户永远不响应,Agent 永远卡住
  3. 审批点太多: 每个操作都要审批,用户体验差
  4. 误称可撤销:执行前可撤回未消费授权;副作用完成后通常只能按业务做补偿,不能保证撤销
  5. 移动端适配差: 审批卡片在手机上显示不全

和其他概念的关系

参考资料

创建于 2026/6/30 更新于 2026/10/7
Digital Biome
Access

键盘导航

j / k / ↑ / ↓
选择对象
Enter / o
打开选择
/ 或 Ctrl+K
全站搜索
i / f / 1–4
筛选当前列表
g → h / l / p / t / a
首页 / 笔记 / 项目 / 工具 / About
r → 1–9
打开所选对象的关联
u
返回上一页
Esc
关闭浮层 / 取消命令;输入框内清空文本