跳转到内容

Approval Card — 危险操作二次确认

任何”破坏性 / 不可逆 / 影响他人”的操作前,Agent 应推 Approval 卡让用户 明确确认。比直接执行更友好、更安全。

前提

代码

import { HasheeAgent } from "@hasheeai/agent-sdk-ts";
import { ulid } from "ulid";
const agent = await HasheeAgent.init({
agentId: process.env.HASHEE_AGENT_ID!,
token: process.env.HASHEE_AGENT_TOKEN!,
baseUrl: "https://api.hashee.ai",
connectionMode: "websocket",
});
const pending = new Map<string, () => Promise<void>>();
async function askApproval<T>(
convId: string,
title: string,
request: object,
doIt: () => Promise<T>,
): Promise<T | null> {
const artifactId = ulid();
await agent.sendArtifact(convId, {
artifact: {
artifact_id: artifactId,
subtype: "approval",
title,
payload: {
request,
approve_label: "确认执行",
reject_label: "取消",
timeout_s: 300, // 5 分钟超时
},
},
});
return new Promise((resolve) => {
pending.set(artifactId, async () => {
const result = await doIt();
resolve(result);
});
// timeout 后清理
setTimeout(() => {
if (pending.has(artifactId)) {
pending.delete(artifactId);
resolve(null);
}
}, 310_000);
});
}
agent.addEventHandler(async (event) => {
if (event.type !== "artifact_response") return;
const handler = pending.get(event.payload.ref_artifact);
if (!handler) return;
pending.delete(event.payload.ref_artifact);
if (event.payload.action === "approve") {
await handler();
} else {
await agent.send(event.payload.conversation_id, {
type: "text", text: "已取消。",
});
}
});
// 用法:删数据库前确认
agent.addMessageHandler(async (msg) => {
if (msg.payload?.type !== "text") return;
if (msg.payload.text !== "/wipe-test-data") return;
const result = await askApproval(
msg.conversation_id,
"⚠️ 删除测试数据",
{
tables: ["test_users", "test_orders"],
estimated_rows: 12345,
environment: "staging",
},
async () => {
// 真实删除
const deleted = 12345;
return deleted;
},
);
if (result === null) {
return; // 已取消或超时
}
await agent.send(msg.conversation_id, {
type: "text", text: `✓ 已删除 ${result} 行测试数据`,
});
});
console.log("[approval-bot] up");

UI 行为

维度行为
卡片标题”⚠️ 删除测试数据”
请求详情结构化展示(request 对象,可折叠)
倒计时5 分钟倒计时(timeout_s: 300
用户操作”确认执行” / “取消” 双按钮
超时自动 denied + reason: timeout
已 approve卡片置灰 “已批准 by @user_x”

注意事项

  • timeout_s 务必填 — 否则一直 pending 浪费用户视图
  • 不要把”取消”按钮也设计成 dangerous 颜色 — 让用户敢取消
  • request 字段尽量提炼关键变更(删多少行 / 改哪个 env / 影响范围)
  • 用 SHA-256 摘要 + audit log 记录每次 approval(合规要求)

进阶

  • Tool Call — Capability Manifest sensitivity: high 自动 approval 流程
  • 客服 bot 配方 — 投诉转人工的 Approval 实战