Git 作为 agent 信任基座
问题所在。 当多个 AI agent 协作完成一个不可逆且对外可见的动作(发布、部署、发送)时,你不能指望任何一个 agent 是诚实的:生成这份工作的 agent,恰恰是判断它该不该上线的最差人选;而被问"你做到合规了吗"的 agent,完全可以直接回答"是"。让 agent 审查 agent 层层串联也解决不了这个问题——同一模型家族的审查者会共享同样的盲点(slop-is-a-context-deficit 实验的告诫:同一家族的评判者会漏掉同样的东西),而且这整条链条都不具备防篡改的证据能力。
模式的精确表述(2026-07-16 修正)。 权威来源不是 git——而是一个可重复执行的确定性检查。不可化简的机制是:关卡对制品重新跑一遍确定性 lint,而不是相信任何 agent 的自我报告。 git 的本质职责是另一件、更具体的事:内容寻址,把被检查过的制品和最终发布的制品绑定在一起(从而消除检查时刻与使用时刻之间的间隙)。git 贡献的其他一切都有价值,但都是可替代的。
- 角色间的权力分立。 生成者(generator,负责产出)· 审计者(auditor,运行 lint 并提交记录)· 发布者(publisher,只对已验证、已内容寻址的记录采取行动)。任何角色都不能替另一个角色做事。
- 关卡的完整性来自重跑本身,而不是来自 git。 发布者不会相信一个"通过"的状态——它会重新跑一遍确定性 lint(或者把已提交的 lint 结果与已提交的制品对照验证)。agent 没办法凭嘴上功夫蒙混过一个会被重新执行的检查;初稿里"读取 git"的说法,把功劳错记到了 git 头上。git 与通过/不通过的判定只是偶然相关;真正换来信任的是重跑这个动作。
- git 真正承重的地方——TOCTOU 间隙。 没有内容寻址的话,agent 可以让制品 v1 通过 lint,然后偷换成不合规的 v2 再发布(检查的是一个东西,发布的是另一个)。git 堵上了这个漏洞:发布者发布的,恰好就是那个内容哈希被提交过、被 lint 过的制品。要做到这一点,你需要内容寻址加不可变性——也就是 git 或与之等价的东西。这才是 git 出现在这个流程里的原因,而不是通过/不通过的判定本身。
- 硬规则是代码;软判断是 agent。 机械化的、可用 diff 检验的规则(完整性、格式、来源、频率)都放在确定性 lint 里,agent 没法靠争辩绕过去。agent 的判断(品味、语域)只作为建议,绝不作为关卡——这样就绕开了同家族合谋的问题。
git 的其他贡献——真实存在,但可替代。 带时间戳、防篡改的来源记录(一个签名的仅追加日志同样能做到);依赖历史的检查所读取的、持久化的有序历史(速率冷却、去重、平台期检测——任何有序存储都行,git log 只是恰好方便);零额外工具的人工审计/回退(git log/diff/revert)。对一个原生使用 git 的操作者来说,这些几乎是免费的,这就是为什么 git 是实用意义上的基座,尽管从逻辑上讲只有内容寻址才是必需的。
这带来的一种解耦。 如果威胁模型里根本不存在偷换向量——发布和审计在一个原子步骤里完成,中间没有 agent 可利用的间隙——那么关卡就完全不需要 git 了,git 会退化成纯粹的审计存储。关卡的完整表述是:确定性重跑 +(当且仅当存在 TOCTOU 间隙时才需要内容寻址)。 git 只是获得后一个条款的一种方式,而不是前一个条款的来源。
一个熟悉想法的推广。 这就是"约束要机械化,而不是靠社会化默契"(vault 主人的工程原则)应用到 agent 协作上的样子:与其信任 agent 会遵守规则,不如把规则做成 agent 的输出必须通过的一次重跑,并锚定到实际发布的那份确切内容上。这其实是 CI 门禁式(continuous-integration,持续集成)部署,只是重新套用到了一个工人是语言模型的世界里:流水线不信任提交者,而是对已提交的内容重新跑一遍检查——阻止"检查"和"部署"之间被偷换的,正是那个提交哈希。
重跑本身就是一次 agent 的生成——"验证就是生成一个 agent"(2026-07-16)。 这个重跑不是"同一进程再调用一次函数";它的实际形态是一个新生成的、干净上下文的 agent,去 shell 出确定性 lint,对已提交的内容执行检查。生成新 agent 之所以重要,而不只是重新执行这件事本身,有两个原因:(1)干净的上下文防止了同上下文合谋——验证者与产出这份制品的生成者之间不共享任何状态、不共享任何自圆其说的理由、不共享任何半成型的故事(这正是 executor-acceptance-test 用在发布关卡上:一个干净的 agent,要么给出引用要么判失败);(2)整台机器的验证接口是统一的——不管是确定性关卡还是判断性关卡,一律都是"生成一个 agent 去重新检查",这样需要推理的信任原语就只有一个。确定性 lint 是那个被生成出来的 agent 所运行的东西;生成动作决定的是"谁来运行它"。
由此衍生出的构建纪律——"重跑验证不了的东西,就不许进来。" 因为验证就是一次生成 agent 的重跑,一个组件只有在这样的重跑能够检查它时,才配在这台机器里占有一席之地。一个任何重跑都无法确认的字段(一个无法证伪的断言、一句无法核实的"相信我")不会被降格为软性建议留下来——而是被直接排除在外。这让机器里的每一道关卡都能被同一个原语证伪,也是为什么面对"第一条规则没有在代码里强制执行"这种担忧,正确的修法不是加一个哈希相等断言,而是指出:发布者 agent 已经在对已提交(已哈希)的内容重跑了——真正起强制作用的是重跑本身,而不是某个专门定制的检查。
落地实例。 utter-manual——发布者是一个新生成的 agent,它对审计者已提交的账本记录重新跑一遍机械 lint,只有结果干净时才执行那个不可逆的动作(发到一个真实账号上),因此 agent 无法靠嘴上功夫说动机器发布不合规的内容。
这套基座横跨人↔agent,不只是 agent↔agent(2026-07-16)。 在需要品味判断的步骤上(lint 检查不了的那部分残余——这个视频好不好、它代不代表我),判官是人,他们的"同意"也用同样的方式绑定:记录成一个 token,其内容哈希必须与已提交的制品一致(human-gated-publish,utter-manual 的 publish.human_approval)。发布者的关卡因此变成两把锁——一个确定性重跑的 PASS,加上一个人对匹配哈希给出的 approve。于是,阻止 agent 发布"被检查过的东西之外的内容"的那套内容寻址机制,同样也阻止机器发布"人没有认可过的内容":对一个人来说,"认可这份确切字节"和对 lint 来说"重跑这份确切字节"一样防篡改。信任的原语(内容寻址式授权)在确定性关卡和人工关卡上是统一的。
边界所在。 这套基座保证的是符合已声明的规则、且检查与发布之间不会被偷换——而不是规则本身是对的。一份错误的 lint spec,会把"错了但合规"的工作照样发出去。而硬/软的划分意味着,任何只能靠品味判断(而非可用 diff 检验的规则)的东西,最终仍然落在建议性的 agent 加人工签字上,而不是落在关卡上。这套模式把信任,从"agent 是诚实的"挪到了"规则是对的、并且有人在盯着那些构成性的 diff"——这是一个严格意义上更好的信任落脚点,而不是一个没有信任的地方。
同类项:slop-is-a-context-deficit(为什么 agent 审 agent 不够)· executor-acceptance-test(生成干净 agent 重新检查,这里被用作发布关卡)· trust-follows-provenance(靠重新推导来验证,而不是靠点头认可)· utter-manual(落地实例)· vault 自身的 pre-commit lint(同一种哲学在笔记层面的体现)。
来源:2026-07-16 的设计,从 utter-manual 架构中泛化而来;正是这个模式,让不可逆的对外动作得以委托给不受信任的 agent 去执行。