引用监视器。
在一套生产级智能体栈里,几乎每一项控制措施都至少不满足「一项控制值得被信任」所需的三条性质之一——而它通常不满足的恰恰是最要紧的那一条:做检查的那个东西,必须待在被检查的那个东西碰不到的地方。这项检验有五十年历史,拿它对着你自己的架构跑一遍大约只要十分钟,而它能解释为什么写进系统提示词里的一条护栏不算安全控制、而同一条规则写成文件系统策略就算。它同时也解释了代价:每当你把一项控制往智能体碰不到的方向再挪一步,它对「智能体当时想做什么」的理解就少一分。
三条性质,以及每条各在问什么。
引用监视器来自 James P. Anderson 1972 年为美国空军做的《计算机安全技术规划研究》,它写明了一套机制要被用来承担访问控制,必须满足什么。这个表述之所以原封不动地活到今天,是因为它不是一份设计——它是一项检验,而且跑起来很便宜。
# Anderson's three requirements, as questions about your stack
complete mediation Is there any path to the protected operation
that does not pass through this mechanism?
(if yes, the mechanism is advisory)
tamper-proof Can the thing being mediated modify, disable,
bypass or lie to the mechanism?
(if yes, you have a suggestion, not a control)
verifiable Is the mechanism small and simple enough that
you can convince yourself it is correct?
(if no, you cannot know whether it holds)
这三条不是一张可以部分满足的愿望清单。它们是相乘的:一套调解了每一次调用、却住在智能体自己进程里的机制,会被一次成功的代码执行击破;而一套防篡改、旁边却还有第二条绕路的机制,会被「走第二条路」击破。这项检验值得一跑的原因在于,一旦把问题真的念出声,失败往往一眼可见——而几乎没人念出声。
第三条是大家会悄悄放掉的那条,而它在智能体这里有个特定形态:如果你的策略决策点本身就是一个语言模型,那它在 Anderson 的意义上就不是可验证的——你没法把它的行为穷举出来,而且它继承了它所要保护的那套系统的每一处输入处理弱点。模型可以是个很好的检测器。它不能是你所依赖的那套机制,这正是护栏那一页展开的论点。
给你自己的控制打分,然后看提示词那一层三条全挂。
把你手上真正有的那些控制拿来过一遍这三个问题。这个练习很短,而各家团队的结果出奇一致:最有安全感的那些控制,恰恰得分最差——因为它们是写在最好写的地方,而不是写在能被强制执行的地方。
# mediation / tamper / verifiable "do not delete files" in the system prompt ✗ ✗ ✗ an LLM judge screening the agent's proposal ~ ✗ ✗ a check inside the tool-calling function ✓ ✗ ~ an approval step the agent can skip on retry ~ ✗ ✓ allow-list in the harness's own config ✓ ~ ✓ seccomp / Landlock policy on the process ✓ ✓ ✓ network policy at an egress proxy it cannot set ✓ ✓ ✓ a watchdog on separate hardware ~ ✓ ✓ ✓ satisfied ~ partially ✗ not satisfied
有两行值得留意。写在提示词里的规则三条全挂,而它仍是这个行业里最常见的「控制」:没有哪条路径是它必然会截住的、那段文字待在一个智能体的输入能够影响的上下文里,而且它没有可供测试的错误路径。最后一行才是有意思的那行——一个跑在独立硬件上的看守既真防篡改也真可验证,而它在「完全调解」上只拿到一个波浪号,因为它看不到每一次操作。这不是产品的缺陷。这正是整个话题所围绕的那笔取舍。
那条「第二条路」值得明确点名,因为现实中大多数失败就住在那儿:重试。一项被检查了一次、而操作却通过另一个入口被重试、恢复或重放的控制,等于什么都没调解。审批是典型案例——去看影响半径了解那条未被调解的路径值多少钱,以及重试放大了解第二次尝试是怎么走到那儿的。
「防篡改」意味着处在另一个信任域里——而每往外挪一步都要用词汇量来付账。
「防篡改」不是一项关于代码质量的性质。它是一句关于信任域的话:相对于某个特定对手,如果那个对手写不动这套机制,它对该对手就是防篡改的。所以要问的从来不是「这项控制写得好不好」,而是「智能体得攻破什么才能碰到它」,而答案是一把梯子。
- 同一个上下文。写在提示词里的规则与不可信输入共用一条通道。攻破成本:一次成功的注入。这属于指令层级的地界,而那个层级是一种偏好,不是一道边界。
- 同一个进程。工具封装里的一次检查。攻破成本:智能体内部的任意一次代码执行——而对一个编码智能体来说,那就是产品本身。
- 同一台主机、不同进程。一个策略守护进程或一个本地代理。攻破成本:一次本地权限提升。
- 同一台主机、由内核强制。seccomp 过滤器、一条 LSM 策略、在智能体启动之前就装好的网络命名空间。攻破成本:一个内核漏洞。这是当下大多数团队真正能踏上的第一级台阶。
- 不同的机器。一个出站代理、一个策略服务、一个摆在工具前面的中间人。攻破成本:抵达另一道信任边界上的系统。
- 不同的处理器。跑在 DPU 或某个主机寻址不到的独立管理控制器上的看守。攻破成本:固件。
接下来是通常会被漏掉的那部分。你每往上爬一级,监视器的词汇就更粗一层。工具封装里的检查知道用户、任务、参数取值与计划。一个 seccomp 过滤器知道系统调用号。一个出站代理知道目的地、方法与字节数。一个 DPU 看守知道流与 DMA。为了防篡改而把一项控制越往外推,它能表达的你的意图就越少——这意味着,可用的最强机制只能强制那些你已经设法用它的语言重新表述过的不变式。
这正是「模型应该拒绝」与「基础设施应该拦住」并不是同一个问题的两个竞争答案的原因。它们坐在一笔取舍的两头:理解力最好的那一层强制力最差,而强制力最好的那一层理解力最差。一份真正的设计会同时用上两者,并且明确说出自己把哪些不变式托付给了哪一层——错误在于,你以为自己有一项控制,实际上你有的是两层各一项、且都只写了一半。
把不变式一直重述下去,直到有东西能强制它。
可操作的方法直接从那笔取舍里长出来。对每一件你真正在依赖的事情,找出「词汇仍足以区分你在意的那几种结局」的最外那一层,并把控制放在那儿。如果没有哪一层能表达它,那你手上就不是一项控制——而是一个预期,而它应该被当作预期写下来。
- 把意图翻译成效果。「不要外泄客户数据」没有引用监视器可用,因为模型之外没有哪套机制能对它求值。「这个进程只能向这三个主机名开 TCP 连接」有,而它覆盖了同一份保证里相当大的一块。翻译本身就是那份工作;见智能体的出站控制与面向智能体的策略即代码。
- 把机制装在智能体之前,而不是围在它周围。一条由外壳在启动时施加、且事后无法放宽的策略满足防篡改;同一条策略如果智能体能通过某个工具重新配置它,就不满足。这就是你去找的时候环境权限长的样子。
- 让失败路径成为默认。一套会「失败放行」的机制,恰恰在你需要它的那一刻没有调解性质,而这正是失败拦截与失败放行的全部主题。
- 在监视器处记日志,而不是在调用方处。由被调解的那个组件写下的记录,可信程度等于那个组件。决策日志属于「做出那个决策的东西」——这正是决策回执背后的道理。
- 别让「可验证」这条性质变成空话。如果你的策略文件已经长成一千行条件判断,那你是拿第三条性质换了覆盖面。宁可要一个默认拒绝的小内核,加上一份你能一口气读完的短允许清单。