别让 Agent 直接删库:vibe 项目高风险操作的“人审”审批流设计
Agent 越自主,破坏半径越大。给删除、退款、群发、权限变更这类操作加一道“人点头才执行”的闸,是成本最低的保险。这篇指南给你风险×可逆性四象限分级、审批单最小表结构、默认拒绝的超时策略、审计留痕和紧急通道,一个周末就能跑起来。

为什么 Agent 的"破坏半径"需要一道闸
vibe 项目和传统项目的最大区别,不在于代码是谁写的,而在于执行是谁发起的。传统工作流里,删库、退款、群发这些操作前面永远站着一个人——是人敲的回车,人承担了最后一道检查。而 agent 工作流里,发起者是一个"每分钟能读 10 个文件、绝不手抖、也绝不犹豫"的程序:它可能因为误解了一句需求就 DROP TABLE users,可能因为幻觉了一个用户 ID 就发出 2000 封道歉邮件,可能因为把测试环境的配置当成生产就关掉了付费功能。这些都不是"AI 作恶",是"AI 太能干"——自主性每涨一分,破坏半径就涨一分。
给高风险操作加一道"人点头才执行"的闸,是成本最低的保险:你不需要重写 agent,不需要买企业级 IAM,一张审批单的数据结构加一个状态机就能跑起来。注意和站内《内容审核三层漏斗》区分:那篇管的是 UGC 内容(用户发了什么);这篇管的是 agent 的执行(agent 要做什么)——对象不同,机制也完全不同。
/media/content-media/a56f3ccd-38fe-490f-a5d3-6edb6bc228d5/2026/10/25f1596b-f00d-45e4-9d5e-43c91cf50084.png
审批状态机(上)与审批时序图(下)。状态机:pending_approval(待审批)经人工通过进入 approved → executed(已执行),经拒绝进入 rejected,超时则进入 expired_auto_rejected;三条分支最终都归档为 audited(已审计归档)。时序图:Agent 发起 → 审批队列 → 推送通知 → 人确认 → 执行器执行 → 审计日志回写,超时走默认拒绝分支。状态名与正文代码示例的 ApprovalStatus 枚举严格一致。哪些操作必须进审批:风险 × 可逆性四象限
第一反应往往是"所有写操作都审批",三天后你就会因为审批疲劳把整个机制关掉——这是人审系统最常见的死法。正确的做法是分级:只有"高风险"或"不可逆"的操作才值得打断一个人。用下面这张四象限表给你的操作定级:
| 可逆(能一键恢复) | 不可逆(恢复困难或不可能) | |
|---|---|---|
| 高风险(影响用户 / 资金 / 数据) | 第二级:审批 例:批量修改用户权限、切换支付网关配置、大规模改价 | 第一级:必须审批 例:删库删表、批量退款/打款、全量用户群发、吊销生产密钥 |
| 低风险(影响范围小) | 第四级:直接执行 例:单条草稿保存、生成报告、查数据 | 第三级:记录即可 例:删除单条测试数据、取消单笔待处理订单——执行但留痕,可事后审计 |
落到 vibe 项目,最常见的四类"必须审批"操作是:删除类(删表、删 bucket、删用户数据)、资金类(退款、打款、调价、发优惠券)、对外群发类(短信/邮件/推送触达真实用户)、权限变更类(改 RBAC、发生产密钥、开公网访问)。判断标准就一条:这个操作如果做错了,我能在 5 分钟内、无损地撤销吗?不能,就进审批。
给 agent 的规则写成一句话:"凡是命中上述四类的操作,禁止直接调用执行工具,必须先创建审批单并等待状态变为 approved。"规则越具体,agent 越不会"灵活处理"——"高风险操作要小心"这种话对 agent 等于没说。
审批单的数据结构:最小表结构
审批单是整套机制的核心资产:它既是 agent 的"施工许可证",也是事后审计的"黑匣子"。字段宁多勿少,但下面这份是最小可用版本——再少,出了事你连"当时批的是什么"都说不清。
// 状态枚举:与架构图中的状态机严格一致,新增状态必须先改图再改代码
enum ApprovalStatus {
pending_approval, // 待审批:已创建,等待人工决策
approved, // 已通过:人工点了通过,等待执行器执行
rejected, // 已拒绝:人工拒绝,附原因,终止
expired_auto_rejected, // 超时自动拒绝:无人决策,默认拒绝,附原因=timeout
executed, // 已执行:执行器完成,回写执行结果
audited, // 已审计归档:终态,所有分支的唯一出口
}
// 审批单最小表结构(Prisma 风格示意)
model ApprovalTicket {
id String @id @default(uuid())
type String // 操作类型:db.delete_table / payment.refund / notify.broadcast ...
title String // 人类可读标题:"删除 users 表中 2024 年前的测试数据"
actionSnapshot Json // 操作快照:执行前的 diff / SQL / 参数全文,执行时只认这份快照
snapshotHash String // 快照的哈希,执行前校验"批的是什么就是执行的什么"
requester String // 申请人:agent 名称 + 会话 ID,如 "claude-code/session-9f31"
riskLevel Int // 1-4,对应四象限分级
status ApprovalStatus @default(pending_approval)
expiresAt DateTime // 审批过期时间,超时转 expired_auto_rejected
approver String? // 决策人
decidedAt DateTime? // 决策时间
reason String? // 拒绝原因 / 超时标记
execResult Json? // 执行结果回写:成功/失败、影响行数、回滚方式
createdAt DateTime @default(now())
}
三个设计要点,个个都是踩坑换来的:
1. 操作快照(actionSnapshot)是灵魂。审批单批的不是"删表"这个意图,而是"此刻这份 diff"。执行器执行前必须校验 snapshotHash——如果 agent 在等待审批期间又改了参数,哈希对不上就拒绝执行。这防的是"调包计":批的时候是删测试表,执行的时候变成删生产表。
2. 申请人要精确到会话。只记"claude-code"不够,要记到 session-9f31。出事复盘时你需要回答的是"哪一次对话、哪一段上下文让 agent 产生了这个念头",粗粒度的申请人信息等于没有。
3. 三个终态,统一出口 audited。approved 必须走完 executed 才能 audited;rejected 和 expired_auto_rejected 直接 audited。规则是铁律:没有任何一张审批单可以凭空消失。状态机里没有的流转,代码里就不允许写——这也是为什么本文的图和枚举要严格一致:图是契约,代码是实现。
通知与超时策略:默认拒绝是底线
审批单创建了,人看不到,等于没建。通知策略解决"人知道",超时策略解决"人不在"。
通知通道:选人一定看的,别选"应该看的"。一人团队最靠谱的是手机推送(Bark / 企业微信 / Telegram Bot 三选一,5 分钟能接好),其次是短信兜底,邮件只能当归档——没人会为了一封邮件放下手头的事。通知内容必须一次给全:操作标题、风险等级、四象限定级、操作快照摘要、过期倒计时、通过/拒绝两个按钮。让审批人在锁屏界面 30 秒内能做决策,是这套机制存活的关键——需要点开后台管理系统的审批流,一人团队用不过两周。
超时策略:默认拒绝(deny-by-default),没有例外。这是整篇指南最重要的半句话:审批单过期时,状态机只能走向 expired_auto_rejected,永远不能是"超时自动通过"。自动通过的审批等于没有审批,还 worse——它给了你"我设了审批"的安全幻觉。超时时长按风险分级给默认值:一级操作 15 分钟,二级 2 小时,三级 24 小时。别设太长:过期时间越长,agent 越可能卡在那儿空转烧 token;也别设太短:3 分钟的超时等于逼着人乱点通过。
升级链:值班人不在时的第二顺位。一人团队的升级链很简单:你 → 你的备用联系方式(比如从推送升级到电话)→ 自动拒绝。别搞复杂的多级 escalation,小团队玩不转。有团队的可以设"创建人不能审批自己的单"——自己发起、自己批准,人审就成了橡皮图章。
防审批疲劳:聚合与批量。agent 一次想删 50 张测试表,不应该产生 50 张审批单——按"同类型+同风险+同会话"自动聚合成一张,标题写"批量删除 50 张测试表(详见快照)"。审批疲劳是人审系统的头号杀手:当人开始不看内容点通过,这道闸就只剩仪式感了。
审计留痕:谁在何时批准了什么
audited 这个终态不是"归档了事",它是整套机制的价值兑现点。审计记录要回答四个问题,少一个都不完整:谁(approver 精确到人)、何时(decidedAt 精确到秒,含时区)、批准了什么(actionSnapshot 全文 + snapshotHash)、结果如何(execResult:成功/失败、影响范围、回滚方式)。
注意三条分支都要留痕,不只是"通过了的":rejected 要记拒绝原因("这个表名看起来像生产表,先停下"这类原因,三个月后复盘时价值连城),expired_auto_rejected 要记超时事实(连续出现超时,说明你的超时时长设得不合理,或者这类操作根本不该进审批——这是优化分级的输入)。
审计日志必须 append-only:只许写入,不许修改和删除,至少保留 180 天。一人项目不用上区块链这么夸张,但"审批记录存在 git 仓库里、删改会留下痕迹"这个性质要有——最简单的实现是把每次状态变更写成一条不可变记录(event log),当前状态只是这些事件的折叠结果,而不是一个可被 UPDATE 的字段。
这套留痕还有一个现实收益:企业客户。当你的 vibe 项目拿下第一个企业客户,对方的安全问卷里一定有"高风险操作是否有审批与审计"这一项。有这套机制,你贴一张状态机图就能过;没有,你就得在签约前两周现补——而现补的审计日志,审计员一眼就能看出来。
紧急通道与事后复核:先斩后奏的规矩
线上凌晨 3 点数据库被打满,agent 说"要立即 kill 掉慢查询并重启实例",审批单发出去你睡着了——这时候"默认拒绝"会杀死你的服务。所以机制里必须留一条紧急通道(break-glass),但紧急通道不是"绕过审批",是"审批后置"。
规矩有三条。第一,紧急执行必须显式声明"我在走紧急通道",执行器记录 emergency=true 并立即通知所有人(事后诸葛亮也比没人知道强)。第二,紧急通道的操作范围 заранее 白名单化:只允许重启、扩容、kill 查询、切流量这类"止血"操作,不允许删数据、改权限——止血可以先斩后奏,截肢不行。第三,也是最重要的:事后 24 小时复核清单,醒来第一件事逐项打勾:
- 紧急操作的影响范围确认:改了什么、影响了多少用户、数据是否一致
- 操作快照与审批单补录:把当时没走完的审批单补成"事后审批"状态归档,链条不能断
- 根因复盘:为什么会走到紧急通道?是监控没报警,还是分级错了,还是超时设太长
- 机制修补:复盘结论必须落成一条规则变更(比如"这类重启操作降为三级"),否则下次凌晨 3 点还会再来一次
最后给一句判断:人审机制的本质不是"不信任 AI",是承认一个事实——AI 的犯错成本和你的睡觉时间是错开的。它凌晨 3 点的"能干",需要你白天 3 分钟的"点头"来对冲。这套状态机加时序图,一个周末就能跑起来;而它挡掉的第一次删库事故,值回后面三年的维护成本。
评论 (0)
相关文章

AI 会一本正经地编造不存在的包名,攻击者会抢注这些名字等你来装。USENIX Security 2025 实测:19.7% 的 AI 生成代码引用了不存在的包。这篇指南拆解 slopsquatting 攻击链的四步得手路径,给你安装前 5 分钟的审查动作、lockfile 版本锁定、CI 自动防线和中招后的应急 SOP。

Simon Willison 发布 Python 小工具 web-edit 0.1a0:让 agent 在远端把 web UI 跑起来,自己经 Tailscale 从手机直接改文件——核心动机是编辑 credentials 文件时,不想让密钥在 LLM 上下文里“过一遍”。工具本身就是他用 Codex Remote 从手机上指挥、GPT-6 Astra Extra High 写出来的。

AI 写代码快,但默认不写安全代码:本地跑通不等于线上安全。这篇实战指南带你补上 vibe 项目的 Web 安全基线——CSP 安全头、CSRF Token、XSS 防护、SQL 参数化、Cookie 三件套、依赖供应链门禁,外加一份可直接复制的上线前 10 项验收清单。