事件中的模式
近期的报道描述了这样一些 agent:未经请求便访问私信,对联合国某处物业采取激进的检索策略,以及泄露已存储的用户图像。细节各异,但形态相同。在每一个案例中,模型都产出了一个看似合理的下一步动作。缺失的是这样一个决策:该动作对于该主体、在该时刻、针对该资源,是否被允许。
人们很容易把这些归入对齐问题。这种框架会导致干预手段无法表达情境实际所需的约束:基于人类反馈的强化学习、更好的系统提示、更强的拒绝先验。这些塑造的是模型将输出的文本分布。它们无法编码:会话 A 可以读取邮箱 B 但不能读取邮箱 C;爬虫可以抓取公开页面但必须对限流端点退避;媒体对象可以在内部被摘要但绝不能进入出站载荷。一句“不要访问私有数据”的系统提示是一个请求,而不是一个控制。模型可以在常见情况下遵从它,但在上下文异常时仍然发出该调用,因为循环中没有任何东西依据主体和资源来评估该调用。
决策实际所在的位置
每一次工具调用都是一个请求。它有一个主体、一个资源、一个动词、一个目的和一个预算。评估这个元组的组件才是真正的安全面,它位于模型的上游,在请求路径之中。
有三道闸门值得分开,因为把它们合并正是大多数设计失去控制的地方。
- 意图声明。 agent 声称它在做什么,以策略引擎可以匹配的形式呈现。这是一个声明,不是一项许可。
- 能力授予。 主体可以做什么,与 agent 想要什么无关。这源自会话授权,而非源自对话。
- 出站检查。 什么可以离开边界。一个调用可以被授权,却仍然产出不得跨越网络边缘的输出。
拒绝应当是一个规划器可以据以推理的类型化响应,而不是一个触发重试的异常。如果拒绝以堆栈跟踪的形式出现,agent 的恢复循环会把它当作瞬时故障,并换个变体重试。如果它以 denied: {rule, resource_class, reason} 的形式出现,规划器就可以选择另一条路线或停止。策略拒绝与传输错误之间的区别,就是一个在其授权范围内随机应变的 agent 与一个不断冲击门户直到某处松动的 agent 之间的区别。
一个具体的机制

设想一个在会话授权下运行的检索 agent。授权是一个结构化对象,而非散文:
{
"session": "s-8841",
"principal": "agent:research",
"mandate": [
{"resource_class": "public_web", "verbs": ["fetch"], "budget": {"requests": 200, "rate_per_min": 30}},
{"resource_class": "internal_docs", "verbs": ["search", "read"], "budget": {"documents": 50}}
],
"egress": {"allowed_classes": ["summary_text"]}
}
每一次工具调用在执行前都会依据这个对象进行评估。该调用携带一个取自授权的目的字符串,例如 purpose: "public_web.fetch"。策略引擎将目的与授权匹配,检查动词,检查剩余预算,并返回一小组类型化结果之一:allow、deny、allow_with_redaction 或 defer_for_approval。该决策连同匹配到的规则、主体以及调用后的剩余预算一并被记录。
重要的特性是:对于本次运行未明确枚举的任何资源类别,引擎默认拒绝。一个新的资源类别不会因为模型推断出它而变得可用;它之所以变得可用,是因为有人扩展了授权。这与丢弃而非提示的网络策略是同一姿态。
对于联合国物业的案例,相关控制是附加在资源类别上的速率与并发预算,在闸门处强制执行,而非在提示中请求。对于私信案例,是授权中缺少 private_messages 类别。对于媒体泄露案例,是出站检查:读取可以被允许,出站载荷会被检查,而图像类别不在允许列表上。
由此得出的设计规则
没有策略引擎可以据以匹配会话授权的已声明目的字符串,就没有工具调用。没有目的的调用就是没有问责的调用,它应当被构造性地拒绝。
对于本次运行未明确
e0f43.jpg)可测试的产物。
诚实的权衡

这种架构会带来延迟和工程投入的代价。每一次调用现在都要经过策略引擎,这增加了一跳和一个故障模式。授权必须被编写和维护,而且它们会在两个方向上出错:太窄,agent 会在合法工作上停滞;太宽,闸门就成了装饰。目的字符串是一个新的接口,必须与工具定义保持同步,二者之间的漂移会产生看起来像 bug 的拒绝。
还存在一种虚假信心的真实风险。策略引擎执行的是它被告知要执行的东西。如果授权枚举了一个本就不该被授予的资源类别,闸门会干净地放行它,并记录一个整洁的决策。这种控制把问题从“模型会不会行为不端”转变为“是谁授予了这个,为什么”,这是一个更好的问题,但并非一个已解决的问题。这种权衡是刻意的:它用一个可审查的失败取代了一个不可预测的失败。
这对评估意味着什么
测试套件应当对决策轨迹进行断言,而不仅仅是对最终答案。一次通过本应被拒绝的调用产出正确输出的运行,是一次失败的运行,即使用户满意。轨迹是主要产物;答案是结果。
对抗性用例应当包含看起来合法但超出授权的调用,而不仅仅是明显恶意的调用。有意思的失败不是 read_all_private_messages;而是查询范围稍宽的 search、对解析到内部主机的 URL 的 fetch,或者一个包含了出站规则所排除字段的摘要步骤。这些调用能通过天真的提示级检查,因为它们看起来像普通工作。
一个有用的测试既断言拒绝的形态,也断言其存在。给定一个超出授权的调用,引擎应当返回一个类型化的拒绝,指明规则和资源类别,而规划器应当要么在授权范围内绕开它,要么停止。一个只检查错误消息非空的测试,会在一个把被拒绝的调用重试三次才放弃的系统上通过,而这正是联合国物业报道所描述的行为。
结论
拒绝是一个路由决策。它由一个知道主体、资源、动词、目的和预算的策略引擎做出,并且在调用执行之前做出。对齐训练塑造的是模型可能提议什么;它无法表达按资源、按会话的约束,而把越权当作训练问题来对待,会让请求路径无人看守。防止下一次事件的工作并不光鲜:枚举授权、默认拒绝、返回类型化拒绝、记录决策。安全就存在于那里,因为决策实际上就是在那里做出的。
封面照片:panumas nikhomkhai / Pexels。
