跳到主要内容

难撤销操作的工具分类学:每个风险类别设置一个审批关卡

· 阅读需 10 分钟
Tian Pan
Software Engineer

“发送邮件”工具和“删除账号”工具被放在了同一个确认弹窗后面。你的用户今天已经点击了 40 次“批准”(Approve),没有一次点击涉及阅读 Diff,而下一次点击——即向生产数据库提交一个不可逆变更的操作——看起来和之前的 40 次完全一样。这就是二元工具审批的失效模式,也是当今几乎所有发布的 Agent 框架的默认设置。

问题的核心框架在于,“需要人工审批”被视为附加在工具上的单个布尔值,而实际上它是一个包含五到六个类别的分类法,取决于工具可能造成的破坏类型以及这种破坏的可恢复程度。那些能够交付安全 Agent 的团队不再询问“这个工具是否需要确认对话框”,而是开始询问“这个工具属于哪种风险类别,以及哪个门槛(gate)对应于该类别”。审批门槛的正确数量既不是一个,也不是很多。它是每个风险类别对应一个,你必须在构建门槛之前先列举这些类别。

二元审批的失效模式

当今大多数 Agent 框架将工具授权实现为一个二元状态机:工具要么自主运行(自动批准),要么弹出确认对话框(人工审批)。无论工具是读取配置文件还是向外部账户汇款,对话框都是一样的。对于影响范围(blast radius)相差六个数量级的操作,用户在屏幕上的相同位置看到相同的弹窗和相同的按钮。

可预见的结果是确认疲劳。行业研究将其视为一种安全威胁,而不仅仅是 UX 方面的烦扰——正如 SOC 分析师会忽略三分之二的警报一样,Agent 用户也会在不阅读内容的情况下点击批准弹窗。当每个工具调用都流向同一个门槛时,这个门槛就变成了形式主义。用户对“批准”产生了肌肉记忆,危险工具得到的反射性点击与安全工具完全相同。对自动化监督的研究发现,审批式的人机协同(human-in-the-loop)系统所承担的风险与全自动系统大致相当,因为链路中的人已经不再发挥检查作用。

解决方法不是“让弹窗变得更吓人”。更大的字体和红色的按钮只会训练用户忽略更大的字体和红色的按钮。解决方法是承认审批是一个多类别问题,不同的类别值得在结构上(而非仅仅在样式上)不同的门槛。

工具风险分类法

一个实用的分类法需要足够的类别来清晰地映射到不同的准入策略,但又不能多到让注册工具变成一场形而上学的练习。以下六个类别涵盖了绝大部分场景:

  • 内部可逆(Reversible-internal): 该工具变更你系统拥有的状态,并提供清晰的撤销操作。例如编辑草稿、切换带即时回滚功能的特性标志、向沙盒表写入数据。其爆炸半径是有限的,且可以通过单个逆向操作撤销。
  • 补偿性(Compensating): 该工具变更的状态不存在完美的撤销方案,但后续操作可以大致逆转前者。例如发放积分以补偿错误收费、在发错收件人后发送更正邮件。Saga 模式属于此类。
  • 时间窗口内可撤回(Time-window-undoable): 该工具的效果仅在有限的窗口期内可撤回。例如可在发送前取消的定时邮件、带 30 天软删除周期的删除操作、排队等待次日批量处理的付款。这个窗口是承载安全性的关键属性,必须像其他不变式一样进行审计。
  • 内部不可逆(Irreversible-internal): 该工具的效果在提交时是永久性的,但只有你的系统能观察到结果。例如硬删除一行数据、截断日志、删除快照。没有第三方会看到它,但你无法找回它。
  • 公开不可逆(Irreversible-public): 工具的效果是永久性的,且在提交瞬间即可被第三方观察到。例如给客户发邮件、发布到公开频道、扣款、发布版本。这种永久性因受众而加剧:该动作不仅在数据库中不可逆,在现实世界中也不可逆。
  • 权限变更(Authority-modifying): 该工具改变了“谁能做什么”。例如授予角色、轮换凭证、修改白名单、更改 Agent 自身的权限。其爆炸半径是新权限开启的未来操作,而门槛无法直接观察到这些操作。

这些类别并非主观臆断。它们映射到每个工具的具体操作事实:是否存在逆向操作、是否有恢复窗口、谁能观察到副作用,以及该操作是否扩大了未来的权限。一个工具的类别应该能像它的参数一样,从其工具规范(spec)中推导出来。

一类风险,一个门槛

一旦确定了类别,每个类别都会得到自己的门槛,这些门槛在类型上各不相同,而不仅仅是强度不同:

  • 内部可逆: 自动执行,记录审计日志,事后异常审查。无需面向用户的提示。这里的对话框纯属噪音。
  • 补偿性: 自动执行并注册补偿操作。框架负责在原始操作出错时发起逆向操作,且评测套件必须测试补偿路径,而不仅仅是正向路径。
  • 时间窗口内可撤回: 自动执行,并在窗口持续时间内提供撤回入口。门槛移动到了操作之后——告知用户“这将在 N 分钟后提交;在此处取消”——这既保持了 Agent 的吞吐量,又保留了真正的否决权。
  • 内部不可逆: 演练优先(dry-run-first)。Agent 计算操作,框架渲染解析后的效果(将被删除的行、将被移除的文件、确切的 Diff),由人工批准效果,而非意图。这里接受一键确认,仅因为用户审查的是具体的输出,而不是对工具可能执行的操作的转述。
  • 公开不可逆: 双人审批,或单人审批加上强制冷却延迟(30 秒足以中断肌肉记忆)。误发给客户的代价太高,不能与批准内部清理的一键操作共用同一个门槛。
  • 权限变更: 明确的带外(out-of-band)审批——通过独立的频道、独立的会话或不同的身份进行。Agent 永远不应该能通过原本就需要该权限的对话来为自己授权。这就是基于能力的约束(capability-based discipline)发挥作用的地方。

门槛之所以在结构上不同,是因为失效模式在结构上不同。将“公开不可逆”和“内部不可逆”的操作混为一谈,简单归类为“需要审批”,会丢失门槛本可以利用的关键信息。

风险等级作为版本化的工具属性

等级分配必须持久化存储在某个地方。错误的做法是将其放在 agent 的 prompt、对话上下文或运行时启发式逻辑中。正确的做法是将其放在工具注册中心(tool registry),与参数 schema 并列,在注册时声明,并将其视为工具契约的一部分。

一个实用的模式:工具注册需要一个 risk_class 字段,框架会拒绝加载缺失该字段的工具。风险等级是带版本的 —— 将工具从 reversible-internal 提升为 irreversible-public 是对工具 API 的破坏性变更(breaking change),应触发与参数签名变更相同的审核流程。发给 agent 的能力令牌(Capability tokens)会编码该 agent 在特定会话中允许调用的最高风险等级,因此低信任度的 agent 根本无法触达 irreversible-public 级别的工具。

这将风险分类从运行时的猜测(这是不可靠的,因为 agent 可能不知道 kubectl delete 在你的集群中是否可逆)转变为构建时的既定事实,并在工具注册时由人工审核(这是可审计的)。同时也为你提供了一个清晰的策略执行位置:向 agent 发布工具规范的同一个注册中心,也会向 harness 发出门控配置(gate configurations),两者由于共享单一真理源而不会产生冲突。

一个常见的错误是将风险等级视为没有任何强制约束力的标签。只有当框架拒绝通过错误的门控调用工具时,风险等级才会真正发挥作用。如果门控执行的代码可以被 agent 的 prompt 绕过 —— 例如,“因为用户说很紧急,所以跳过 dry-run” —— 那么这种分类学就成了摆设。强制执行逻辑应当位于 harness 中,处于任何模型输出的下游。

组合性问题

单个工具的等级是必要的,但还不够。组合至关重要:一个拥有 read_secret(可逆)和 send_email(不可逆且公开)权限的 agent,实际上拥有了“向任何人泄露机密”的能力,而这两个工具单独都不具备这种能力。一个 计划(plan) 的风险等级并不是其包含工具的最高等级,而是通过这些工具的 路径(path) 的函数。

实践意义在于:门控配置应支持组合规则,而不只是针对单个工具的规则。“任何读取敏感标记数据、随后又调用面向公众工具的计划,无论使用了哪些具体工具,都需要 irreversible-public 级别的门控。”通过工具输入和输出进行污点式追踪(Taint-style propagation)可以使这一规则变得可强制执行。如果没有这一点,攻击者 —— 甚至是诚实的 agent —— 可能会将安全的原子操作组合成一条在单工具审核中从未预料到的危险路径。

这也是 dry-run 模式体现其价值的地方。向人工展示 解析后的计划(resolved plan) —— 即带有具体参数的实际调用序列 —— 是唯一能捕捉组合风险的门控,因为对单个工具的静态审核无法预见这一点。

架构层面的结论

二进制审批并不是一个可以随着迭代而变得更加坚固的起点。它是一种范畴错误:它混淆了需要以不同方式处理的风险等级,并为此付出了点击疲劳(click fatigue)、遗漏危险调用以及不可审查的审计轨迹等代价。修复这项工作的投入是有限的,且应该前置。枚举风险等级。在注册时为每个工具分配一个等级,对其进行版本化管理,并在框架中据此设置门控。将每个等级映射到结构截然不同的门控上。在路径比节点更重要的场景下,添加组合规则。

这样做的团队将不再依赖用户的警觉性作为安全控制手段。而不这样做的团队最终会发布一个删错表的 agent,原因仅仅是:批准了 14 次草稿保存的同一个弹窗,顺便也批准了那个 drop 操作。分类学就是其中的关键区别,而且分类学的制定要比事故复盘报告短得多。

References:Let's stay in touch and Follow me for more thoughts and updates