让 AI 自动修主干,但不信任它

一个自动修复流水线的护栏设计

Posted by walikrence on September 8, 2026

背景脱敏:一个 Go 微服务仓库,主干有 20 多项架构门禁。某次门禁红跨夜 13.5 小时没人接管,于是做了一条「门禁红 → AI 自动修 → 自动合并」的链路。本文讲的不是怎么让 AI 修代码,而是怎么在不信任 AI 的前提下让它自动合并。

为什么已有的两套自动化都接不住

上线前已经有两套东西:

  1. 流水线失败诊断员:失败就拉 trace、分类、git 归因、让大模型只读归因、发飞书卡片。设计上只诊断不修。
  2. 棘轮债务清偿 bot:函数行数、参数个数这类「棘轮门禁」失败后,bot 自动把新违规吸收进基线让主干回绿,然后建 issue 让人异步清债。

问题是一个结构性错配:能被吸收的门禁都不阻塞主干(吸收后秒级回绿),阻塞主干的门禁都吸收不了(架构约束类,比如「配表访问不得留共享层」,没有基线可吸收)。两个集合不相交,清债 bot 的输入里永远不会出现阻塞级失败。

所以需要第三条链,专门处理「阻塞级、但有明确修复指引」的门禁。

链路

诊断员判定:阻塞级 且 门禁在自动修白名单
  → 建 ci-blocking issue(同 commit + 同门禁幂等)
  → 定时任务每 10 分钟认领
  → 独立 worktree 里 headless AI 修
  → 编排器独立复核:门禁转绿 + go build ./...
  → 发 MR
  → MR 流水线绿 → 自动合并
  → 复查主干是否真的回绿,没有则告警

护栏

用户裁定的是「CI 绿即自动合并」,这是最激进的档位,所以护栏必须够厚:

  • 白名单 fail-closed。只修 18 项白名单内的门禁;白名单加载失败,谁都不修。白名单按「与自己同仓同 commit」的相对路径加载,不从主工作区读,否则主工作区没同步会拿到旧版本。
  • AI 没有 git 写权。它只能在 worktree 里改文件、跑 go 和门禁脚本。commit、push、MR 全由编排器做。
  • 改到门禁本身直接判失败。改 scripts/ci/* 或任何 *_baseline.txt 一律失败。最省事的「修法」就是改门禁,必须堵死。
  • 两道独立验证。本地编排器复核(门禁转绿 + 编译)与 MR 流水线全绿,缺一不合。
  • 单次一条、每条只试一次。失败就弃 MR,飞书还给人。不重试是为了不让它在错误方向上越走越远。
  • 主干非 failed 不动手。认领时主干已绿(别人先修了),关闭遗留任务。
  • 合并后复查。合并了主干还红,立刻告警,因为可能是别的门禁或并发合入。

两条硬知识

AI 的自报完全不可信。 演练里 AI 最后一句是「门禁还在跑,稍后汇报」,然后就结束了,从头到尾没输出约定的 BLOCKING_FIX_DONE 标记。如果编排器信它的话,这就是一次「没修」。所以 fix_with_claude 只负责跑,除了明确的 GIVEUP 短路之外,不做任何成败判断;成败只由复核决定。

看 AI 改了什么必须 git status --porcelain AI 改动大量处于 staged 或 untracked 状态,git diff 只看 unstaged,会显示成空,误以为它什么都没改。三种状态都要覆盖。

怎么验证这条链是可信的

最有说服力的验证不是「找一个真实红去修」,而是受控演练:在独立 worktree 里 git revert 掉某个人类的历史修复,重现一个真实的门禁违规,让 AI 修,跑复核,不 push 不建 MR。

一次演练数据:AI 用了 487 秒、18 轮、约 1.4 美元修复成功,复核通过。这个成本对「主干红跨夜 13 小时」来说很划算。

教训

  • 自动化要分「能吸收的」和「必须修的」,两类失败的处理链完全不同。
  • AI 参与的自动化,信任边界要画在「AI 的输出」之外:它可以改文件,不能决定是否成功,不能触碰验证它的东西。
  • 自动修复的验证方式是「revert 一个真实修复再让它修」,比造假问题可信。
  • 最坏情况要有硬上限:单次一条、只试一次、失败还给人。