Human Approval UI
Human Approval UI 是前端展示 Agent 高风险操作审批请求的组件。它让用户在 Agent 执行不可逆操作前进行确认、拒绝或修改。
#type / concept
#status / evergreen
#tech / frontend
#tech / ai
[!info] related notes
- 所属 MOC: AI Agent Application MOC
- 上游概念: Human-in-the-loop
- 相关: Tool Call UI, Chat UI
- 后端: Approval Checkpoints
Human Approval UI
一句话定义
Human Approval UI 是前端展示 Agent 高风险操作审批请求的组件。它在 Agent 执行不可逆操作(删除、支付、发布)前暂停,让用户确认、拒绝或修改操作参数。
它解决什么问题
Agent 调用高风险工具时,需要人类审批。但审批不是简单的”确定/取消”弹窗:
- 用户需要看到完整操作内容(调用什么工具、传了什么参数)
- 用户需要理解风险(这个操作的影响范围)
- 用户可能需要修改参数(不是拒绝,而是改一下再执行)
- 审批需要超时处理(用户长时间不响应怎么办)
核心原理
审批卡片的信息结构
┌─────────────────────────────────────────┐
│ ⚠️ 需要确认 │
│ │
│ Agent 请求执行以下操作: │
│ │
│ 🔧 删除记录 │
│ record_ids: ["id_1", "id_2", "id_3"] │
│ count: 3 条 │
│ │
│ ⚠️ 此操作不可撤销 │
│ │
│ [✅ 批准] [❌ 拒绝] [✏️ 修改参数] │
│ │
│ ⏱️ 自动拒绝倒计时: 60s │
└─────────────────────────────────────────┘
审批状态机
interrupt_received → showing_approval → approved → resume
→ rejected → resume (with rejection)
→ modified → resume (with modifications)
→ timeout → auto_reject → resume
典型工程实现
Approval Card 组件
function ApprovalCard({ interrupt, onResume }: ApprovalCardProps) {
const [countdown, setCountdown] = useState(60);
const [modifying, setModifying] = useState(false);
const [modifiedArgs, setModifiedArgs] = useState(interrupt.tool_call.arguments);
// 超时自动拒绝
useEffect(() => {
const timer = setInterval(() => {
setCountdown(prev => {
if (prev <= 1) {
onResume({ decision: 'rejected', reason: 'timeout' });
return 0;
}
return prev - 1;
});
}, 1000);
return () => clearInterval(timer);
}, []);
return (
<div className="approval-card">
<div className="approval-header">
<span className="warning-icon">⚠️</span>
<span>需要确认</span>
</div>
<div className="approval-body">
<p>Agent 请求执行以下操作:</p>
<div className="tool-call-preview">
<strong>{interrupt.tool_call.name}</strong>
<pre>{JSON.stringify(interrupt.tool_call.arguments, null, 2)}</pre>
</div>
{interrupt.message && (
<p className="risk-message">⚠️ {interrupt.message}</p>
)}
</div>
{modifying ? (
<div className="modify-form">
<textarea
value={JSON.stringify(modifiedArgs, null, 2)}
onChange={e => setModifiedArgs(JSON.parse(e.target.value))}
/>
<button onClick={() => onResume({
decision: 'approved',
modifications: modifiedArgs,
})}>
修改后执行
</button>
</div>
) : (
<div className="approval-actions">
<button className="approve" onClick={() => onResume({ decision: 'approved' })}>
✅ 批准
</button>
<button className="reject" onClick={() => onResume({ decision: 'rejected' })}>
❌ 拒绝
</button>
<button className="modify" onClick={() => setModifying(true)}>
✏️ 修改参数
</button>
</div>
)}
<div className="countdown">
⏱️ 自动拒绝倒计时: {countdown}s
</div>
</div>
);
}
常见设计模式
1. 一键审批
低风险操作(如查询)只需一键确认。
2. 参数修改
用户可以修改工具参数后再执行。
3. 批量审批
多个低风险操作打包成一个审批请求。
4. 上下文关联
审批卡片显示相关上下文(如要删除的记录详情)。
常见坑
- 审批信息不足: 用户看不到完整参数,无法判断
- 不做超时处理: 用户永远不响应,Agent 永远卡住
- 审批点太多: 每个操作都要审批,用户体验差
- 没有撤销机制: 批准后发现错了无法撤销
- 移动端适配差: 审批卡片在手机上显示不全
和其他概念的关系
- vs Human-in-the-loop: HITL 是概念,Approval UI 是前端实现
- vs Tool Call UI: Tool Call UI 展示所有工具调用,Approval UI 专注审批场景
- vs Approval Checkpoints: Checkpoints 是后端机制,Approval UI 是前端组件