观察一个 Agent 思考多步任务的过程,你会注意到一些熟悉的东西:它的规划方式就像你调试代码一样。尝试一种方法,观察结果,如果错了,就撤回并尝试另一种。Agent 将其计划描述为一棵选项树,它可以探索、剪枝和重新访问。这种心智模型在代码沙箱中是正确的,因为在那里的每个操作都有隐式的撤销功能。但在 Agent 接触到现实世界的那一刻,这种模型就错得离谱且危险。
发出的邮件无法撤回。扣款的银行卡在没有退款流程、手续费以及已经看到通知的客户的情况下,是无法撤销扣款的。除非有人设置了软删除,否则被删除的数据行就彻底消失了。一条发布的 Slack 消息可能已经被阅读了。Agent 的规划模型没有原生的“单向门”概念——即一旦采取行动,就再也无法假装它从未发生过。
这不是一个模型智能问题。即使是更聪明的模型仍然不知道你的哪些工具是可逆的,因为可逆性不是操作本身的属性。它是操作所落地系统的属性。你必须明确告诉它。
这种假设在你看到它之前就已经根深蒂固了
Agent 在几乎完全可逆的环境中学习规划。代码就是最明显的例子:编辑文件、运行测试,如果失败就还原,工作区就会回到初始状态。版本控制让代码库的整个历史记录都变成了“双向门”。强化学习环境则更甚——每个回合都以完全重置结束,因此 Agent 对“后果”的认知被局限在一个回合内,而这个回合在下一次 env.reset() 时就会消失。
甚至 Agent 自己的草稿本 (scratchpad) 也在强化这一点。推理过程可以自由探索:提出子计划、评估、丢弃。Agent 的任何“思考”都不产生成本,也不会持久化。因此,当一个模型足够强大到可以操作真实工具时,它已经在那种撤退总是可行且通常免费的世界里度过了整个成长期。
然后,我们给它一个 send_invoice 工具、一个 delete_customer 工具和一个 transfer_funds 工具,并用同样的扁平列表、同样的 JSON schema、以及与 get_weather 同样中性的语气来描述它们。在那个接口中,没有任何信号表明这四个工具中有三个是单向门。Agent 将它们视为是可以互换的,因为从结构上讲,是我们让它们变得可以互换的。
可逆性是一个等级,而非一个标志
人们很容易将工具简单地分为“可逆”和“不可逆”然后就此了事。这种二元划分太粗糙了,因为现实中的操作处于一个光谱上,取决于撤销的成本有多高以及撤销得有多彻底。Jeff Bezos 关于单向门与双向门的构想是正确的直觉,但生产系统需要的不仅仅是两个类别。一个可行的模型有四个等级。
Tier 0 — 免费撤销。 操作可以完全、即时且无成本地还原。例如读取数据、写入临时命名空间、暂存尚未提交的更改。Agent 在这里可以像探索代码一样进行探索。
Tier 1 — 可补偿。 操作可以撤销,但撤销本身是一个不同的操作,具有自己的成本和故障模式。例如扣款可以退款、部署可以回滚、创建的记录可以删除。撤销是真实的但并非免费,而且至关重要的是,撤销动作本身也可能失败——这意味着一个假设撤销“自然可行”的无知 Agent 甚至在这里也会犯错。
Tier 2 — 可观察且不可逆。 操作无法撤销,且已经有人察觉。例如发出的邮件、发布的消息、触发的 webhook、发布的版本。你可以发送更正,但你无法抹去原件已被看到的事实。其成本是声誉和社会层面的,而不仅仅是操作层面的。
Tier 3 — 灾难性。 操作无法撤销且损害会累积。例如删除生产数据的唯一副本、向外部账户汇款、向监管机构提交申报、终止其他系统依赖的基础设施。在这些操作中,“让 Agent 尝试一下”不再是开发上的便利,而变成了法律责任问题。
划分等级的目的不是为了分类而分类,而是因为每个等级都需要不同的控制措施。一个对这四个等级一视同仁的系统,要么会因为对低成本操作过度设限而变得毫无用处,要么会因为对高成本操作疏于防范而导致下一次事故审查。
“让 Agent 尝试一下”是代码习惯,而非现实习惯
编程 Agent 最多产的特性,恰恰也是无法迁移的特性。在一个代码库中,“尝试、观察、修正”的循环就是游戏的全部。我们积极地希望 Agent 尝试一种方案、读取失败的测试并进行调整。可逆性保证了该循环的安全,并且它在代码中是如此可靠地存在,以至于没人会提起它。
把这个循环带入现实世界,情况就反转了。“尝试一次放款,观察财务是否抱怨,然后修正”这不是迭代——这是一系列穿着迭代外衣的不可逆承诺。Agent 的规划器看不出区别,因为这种区别在工具的 schema 中是不可见的。两个循环看起来都一样:调用工具、读取结果、决定下一步。
这就是为什么在系统提示词 (system prompt) 中简单地加入一句“小心不可逆操作”不起作用的原因。这条指令要求模型去推断一个它无法可靠观察的属性——可逆性。模型会进行猜测,并且它会根据工具名称的表面特征进行猜测,而这恰恰是错误的信号。cleanup_temp_files 听起来很安全,但可能是 Tier 3;send_reminder 听起来微不足道,却是 Tier 2。提示词无法修复接口未能编码的信息。
将不可逆性作为工具的一等属性
修复方案存在于工具层,而非提示词(prompt)中。Agent 可以调用的每个工具都应在其契约中包含明确的不可逆性层级(reversibility tier),就像它携带参数 Schema 一样。这能实现三件事。
它让 规划器(planner) 正确推理。如果 Agent 看到 transfer_funds 是 Tier 3,它可以被指示——并越来越多地被训练——去以不同于 Tier 1 的方式处理路由到该工具的计划:首先收集更多确定性,如果有 Tier 1 的替代方案则优先选择,并且绝不为了“看看会发生什么”而投机性地放置 Tier 3 动作。
它让 运行时(runtime) 无需模型配合即可强制执行策略。Tier 级及以上的动作在执行前会被拦截。现在的框架将此作为中间件交付:一个钩子(hook)根据策略检查每个提议的工具调用,并在利害关系跨越界限时中断执行以进行人工审查,而无需更改工具或提示词。模型可能会出错、被越狱或产生混淆,但关口依然稳固,因为关口并不信任模型。
它让你能够 构建层级所暗示的试运行(dry-run)和确认模式。对于 Tier 1 及以上,工具不应直接执行。它应该支持一种预览模式,返回 将会发生什么——收款人、金额、匹配删除谓词的行——而不提交。这里的“授权环(permission loop)”框架非常有用:设计良好的工具会准备动作,向调用者报告其具体的意图,并在获得明确批准后才执行。该批准对于 Tier 2 和 3 可以是人工,对于 Tier 1 可以是自动策略检查。无论哪种方式,Agent 都不再是单向门前的最后一道防线。
设计动作层级,让错误的计划无法被表达
这种方法最强大的版本不是限制单个调用,而是塑造动作层级,使 Agent 无法 规划 一条穿过它无法退回的门的路径。
尽可能将动作降低一个层级。一个执行软删除的删除工具是 Tier 1 而不是 Tier 3。一个创建 待处理(pending) 支付、需要单独释放步骤的付款工具,将一个 Tier 3 动作拆分为一个 Tier 0 草案和一个受控的 Tier 3 释放。一个始终通过 15 分钟发件箱路由的“发送电子邮件”工具,在这 15 分钟内是可恢复的——你从一个单向门中制造出了一个双向门。Agent 安全工程的大部分工作正是如此:寻找可以通过微小的架构变更将不可逆动作转换为可补偿动作的地方。
在无法消除不可逆性的地方,让补偿变得明确。借鉴分布式事务中的 Saga 模式:每一个在序列中途可能失败的前向动作都有一个定义的补偿动作,并且补偿是幂等的,因此可以重试。如果 Agent 订了机票但随后未能预订酒店,必须有 某些东西 来取消机票——而这“某些东西”应该是设计好的补偿机制,而不是 Agent 在压力下即兴发挥的补救措施。一个没有多步 Tier 1 计划补偿图的 Agent,距离一个无人负责的半成品事务仅有一步之遥。
并且要求与层级成比例的确定性阈值。贝佐斯启发法——对可逆决策以大约 70% 的信心行动——对于 Tier 0 和 1 是一个很好的默认值。对于 Tier 3,这是鲁莽的。灾难性动作的关口应该要求接近绝对的确定性 以及 人工参与,因为这种不对称性是绝对的:快速行动的收益很小,而错误行动的代价是无穷的。
你的 Agent 并没有鲁莽的性格。它拥有一种可逆的世界观,这是从那些撤回动作总是免费的环境中诚实地学到的,然后被部署到了一个通常并非如此的世界。模型无法推断你的哪些工具是单向门,任何系统提示词都无法可靠地教给它。
所以,将其编码。为每个工具标记不可逆性层级。在运行时而非提示词中管控 Tier 2 和 3。为 Tier 1 及以上提供试运行模式和授权环。并将你的设计精力花在降低动作层级上——软删除、待处理状态、发件箱、补偿——这样即使当 Agent 像一切皆可撤回那样进行规划时,底层的系统也能使其基本成真。Agent 假设存在的撤销按钮并非免费。但它是你可以构建的东西。
会员专享
余下内容仅对会员开放。
会员可读完整内容 —— 每一个公开发布的观点背后,那些框架、决策与推理的全貌。
- —完整文章,包含未公开存档的部分
- —可落地的工作框架,附带权衡与决策依据
- —新文章抢先看,先于公开发布
随时取消 · 一次订阅,畅读全部