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(合规要求)