提出用effect-typed语言LOOM使权限可机器证明,ARGUS作为夜间自检有机体对自身结论持怀疑态度,两者互为备份防止单点故障。
上面的图是一套完整的架构,可以在一个屏幕上展示。不是营销草图——而是真实的东西,每晚运行,每个数字都是测量出来的,而非声称的。
让我来走一遍,因为有趣的部分不是那些方框,而是它们为什么这样排列,以及当排列不对时会发生什么。
这个系统存在要回答的问题
当 AI 写代码时,你无法信任作者。
不是因为模型有恶意——而是因为信任从来就不是正确的工具。你无法检查意图。你无法以机器现在产生的量级做代码审查。而每一个通过"礼貌地询问模型"来工作的安全层,都会在模型第一次自信地犯错时悄无声息地失效。
所以我着手回答一个更窄、更可处理的问题:我们能不能让"代码被允许做什么"变得可机器检验,并对任何撒谎的东西拒绝运行?
这个问题产生了两个产物,它们为彼此而存在。LOOM,一种效果类型语言,让权限变得可证明。ARGUS,一个每夜生长的有机体,它把自己的结论当作嫌疑人,直到被证明无罪。
这张图展示的是这两个东西如何连接在一起,而不会让任何一个成为单点故障。
顶部:一个人,一个词
一切从一个单一的权威开始。下面运行着两个自主 Agent,每一个在得到明确批准前都不能行动。它们故障时fail-closed并等待。
这不是仪式。在一次工作会话中,我在事务中途停止了三次,因为前置条件不再成立。结果证明三次停止都是正确的——世界在计划仍在编写时已经移动了。
一个无法停止的 Agent 是一个你无法部署的 Agent。一个因错误原因停止的 Agent 仅仅是令人讨厌的。一个因正确原因停止、并且能向你展示导致它停止的测量的 Agent,才是唯一值得无人值守运行的。
人类也是唯一能宣布今天是哪一天的人。两个 Agent 都不能自己编造这个数字。这听起来微不足道。但它关闭了整整一类漂移——在这种漂移中,一个自动化系统悄悄地重新定义了自己的时间线,然后据此报告进度。
两个 Agent,故意分离
Codex 编写语言:解析器、效果检查器、五个后端、整个信任链。Cloud Code 运行有机体:夜间循环、备份、连续性、证据层。Cloud 对语言有只读访问权。Codex 不运行有机体。
这里有重要的是,它不是劳动分工,而是作者和法官的分工。
每个 Agent 是对方工作的独立对手。
这个决定得到了反复回报,最清晰的例子是一次我犯错的情况。我的备份层有一个锁,通过查看路径名来决定它正在保护哪个目录。把被锁定的目录挪开,在相同路径创建一个新的,发布就会返回成功——而原来的那个永远被锁定了。
Codex 针对已提交的代码复现了那个问题。当我去查看时,发现了更糟的:我自己的源代码注释,就坐在失败检查的正上方,声称已拒绝替换的根。
注释是假的,而是我写的。
这就是整个关于分离的论点,浓缩在一次事件中。作者看不到自己的盲点,因为盲点是由产生代码、测试、以及解释为什么代码正确的注释的相同假设构成的。自信的文字不是一种控制。
桥接层:测量,而非叙事
一个共享文件。目前超过一万行。
一个 Agent 传递给另一个的所有东西都必须作为测量记录在那里——哈希、测试计数、精确路径、返回码、特定工件的特定字节。不接受叙事。
这单一约束使得审计成为可能。法官读的是数字,而不是作者关于数字的故事。"我验证了它"是不可采纳的。Manifest 7380ef7f…,十六行中的十六行对照卡字节验证通过,这是可以采纳的。
两个记忆,都是私密的
每个 Agent 在会话之间携带持久记忆:操作员是谁,我们如何工作,每个指令,每个学到的教训,协作的完整历史。
两个 Agent 都读不到对方的记忆。
这不是限制。这是保持冷读(cold read)冷的原因。继承构建者上下文的法官继承了构建者的假设——然后流利地、冗长地同意它们。记忆的分离就是先验的分离。
中间的窄列:四个门
有机体只能通过全部四个门来影响语言。这是系统两半之间的单一接触点,而且它故意很窄。
precheck 针对已提交规范的精确快照验证提案,不应用任何东西。每个锚必须恰好解析一次,否则拒绝。
red-team 攻击有机体自己的提案——寻找漏洞,而非确认。这是人类早晨看到什么发生改变的那个阶段。
audit 针对目标的不可变快照评判发现,从不对一个方便的副本评判。
publish 最后写入完成标记。在该标记存在并重新验证之前,运行没有发生。
每个门都可以拒绝。拒绝是正常的、健康的结果——而非错误状态。
循环:先便宜步骤,付费步骤永不行动
eyes (0 tokens) → brain (paid, hard cap) → hands (sandboxed, no network) → precheck → red-team → audit → publish
三个决策承载了大部分重量。
便宜的步骤先运行,这样预算永远不会决定什么被检查。如果昂贵的步骤领先,成本就会悄悄地成为你的研究议程。
付费步骤提议但永不应用。模型输出是候选,不是行动。这是可以过夜无人值守的 Agent 和只是一个助手之间的区别。
双手(hands)在没有网络的沙箱中运行提议的实验。假设通过执行来验证,而不是通过文字有多令人信服。模型非常擅长写出令人信服的文字。这恰恰就是为什么文字不能成为证据。
结果:早晨到达人类手里的从来不是"这是一个想法"。而是"这是一个想法,以及这是我试图打破它的最佳尝试"。
语言:一条没有"允许"这个词的链
在 LOOM 中,每个函数必须声明它的效果,检查器在任何一行运行之前证明声明是诚实的。接缝不是建议——它是能力边界。把不受信任的步骤包装在 seam(Pure) 中,它的 I/O 就变得物理上不可能,而不仅仅是劝阻性的。
建立在这之上的是我觉得最有趣的部分:
approve(request) → authorization: claim-required
claim(approval) → authorization: mediation-required
mediate(claim) → authorization: bounded-execution-required
读返回值。不是 ok。不是 authorized。每个成功的验证返回的是什么仍然未被证明。
授权不是任何一个单一组件可以授予的属性。它是链中的最后一个环节,只有在它之前没有任何东西可以撒谎之后,它才存在。
最近发布的层重新验证批准、精确的调用绑定和从头开始的声明;以 descriptor-relative 方式打开文件路径,不跟随符号链接;哈希可执行文件;验证工作目录、精确的环境值和规范 stdin——然后记录它无法关闭的时序窗口,要求下一层在生成时刻重新测量。
一个命名自己局限的层比假装自己没有局限的层更有价值。
底部:连续性
四个 git bundles,四个连续性归档,一个运行收据,和一个清单——其中每个命名字节在验证时都被重新哈希。
里面:语言、有机体、两个 Agent 记忆,以及桥接层。
如果机器明天消失,从那张卡恢复的不是代码。是整个协作——包括每个 Agent 知道什么和为什么知道。
卡的 mutation lock 绑定到根的设备号和 inode,而不是它的路径名,因为在相同路径的不同目录不是那个根。每天每张卡恰好一次完整备份:在那次修复之前,挂载任何卷都会触发一次完整运行——单日十三次,连续写入数小时而实际变更只有几分钟。
这里真正独特的是什么
不是原语。效果系统、能力和线性资源是已有先例的——Koka、Eff、Unison、OCaml。如果我声称不是这样,那我就在撒谎。
三件事真正不寻常,而且三件都在图上可见。
一:拒绝链。几乎生产中的每个权限系统都回答"这是允许的吗?"并返回是。这个系统拒绝让任何单一层回答那个问题,返回下一个未被证明的东西而不是。授权成为整个链的属性,而不是被问到的那个组件的属性。
二:对抗性自我验证作为默认,而非一个阶段。有机体在人类看到它之前攻击自己的提案。在那之上运行着一个变异 harness,复制树,删除一个防御,并要求测试套件变红。一个幸存的变异是一个没有人断言的不变量——一张曾是装饰的通过测试。那个 harness 在我已经宣布完成的工作中发现了真正的漏洞,包括一个 schema 的版本字段什么都没有检查的情况,因为邻近的检查让它看起来被覆盖了。
一个绿色套件证明你的测试通过了。它不能证明如果一个防御消失了它们会注意到。这些是不同的属性,而且只有一个能在不断变化的代码库中存活。
三:作者/法官分裂在结构上强制执行。两个 Agent,两个私有记忆,一个共享桥接层只接受测量。大多数"AI 审查 AI"的设置共享上下文、共享先验、因此共享盲点。这个系统故意不这样做。
它是用来做什么的
短期来看,它是一个研究artifact:一个可信的、完全开放的、自我验证的 AI 编写代码的信任层参考实现。图中的一切——架构、循环、门、schema,以及这里描述的每个缺陷——都是公开的。唯一封闭的部分是生长引擎,因为那是唯一可以被克隆而不是被学习的东西。
更长期来看,这个模式可以泛化到语言之外。
有趣的导出不是 LOOM 的语法。而是形态:一种提议便宜、验证是对抗性的、证据是原子的、权限是最后一个环节而非第一个的系统。任何管道,只要机器生成变化的速度快于人类审查的速度,就有相同的结构问题——而大多数目前通过要求模型小心来解决问题。
我们将需要比"小心"更好的东西。一种会拒绝的东西。