分析了八个开源仓库15万次失败CI运行,发现大部分并非代码缺陷而是环境问题如注册表宕机、磁盘满、Node堆内存溢出等。
我是 Kay,Latchkey 的 CTO 兼联合创始人。我们构建的是托管式 GitHub Actions 运行器,能在任务执行过程中自动修复失败。在推出任何功能之前,我们想弄清楚 CI 到底哪里会出问题。不是人们抱怨的那些,而是日志里实际记录的。
所以我们提取了八个生产级开源代码库三个月来所有失败的 GitHub Actions 运行记录,逐条阅读。

156,808 次失败运行,覆盖 Go、Python、C++、TypeScript 和 Rust。
令我意外的事
我原本对红色构建的认知是"有人把东西改坏了"。读了上千条之后,这个认知大多数时候都是错的。
包注册表返回 500。Docker Hub 触发限流。运行器磁盘被 Gradle 缓存填满。Node 触及默认堆内存上限,收到 SIGKILL,退出码 137。有人更新了 package.json 里的 packageManager,导致 pnpm 不在 PATH 中。
这些都不是代码缺陷。没有哪个 commit 能修复它们。环境垮了,正确的做法是修复环境然后重新运行这一步。
然而没有人去修复它们。都是人工重新跑。有人打开运行记录,读完一整面日志,判断"嗯,这个不稳定",点击按钮,等上十一分钟。一整天,每个团队都在这样重复操作。
这才是真正的成本。不是计算资源,而是中断。
这些是我们能够编写确定性检测器的故障类别——这个门槛比"存在的故障类型"要窄得多。
难点在于知道什么不能修
这是整个设计问题的核心,也是我认为大多数"AI 帮你修 CI"的宣传失败的地方。
一旦你能重试某个步骤,重试所有东西就变得轻而易举,同时却是一场灾难。一个不断重跑失败测试直到它变绿的工具,不是治愈工具,而是带着绿色勾勾发货 bug 的机制。--legacy-peer-deps、pip install --no-deps、|| true 同理。症状消失了,病因却上线了。
所以我们定下的规则是:系统可以修复环境,但不能修复代码,也不能隐藏结果。
编译错误、类型错误、断言、panic 和真正的测试失败,会通过几十种语言和框架的特征签名被识别出来,绝不会重试,绝不会修改。它们直接失败并大声报错。测试套件存在的意义就在于此。
唯一例外是不稳定测试重试,但它基于证据而非乐观预期。只有当我们的分析系统已经标记该工作流在重试时通过,才会重新运行该测试。只跑一次,不修改任何东西。没有这个证据,失败的测试就保持红色。
我喜欢的一条相关规则:如果一个反复出现的失败近期根本原因大多是上游 5xx 和限流,我们就直接压制这条告警,而不是报告给你。别人的故障不是你代码库的问题,也不应该变成你的额外工作。
三层结构,优先处理最便宜的。基于退出码的确定性规则。然后是对一百多种已知故障形态的特征匹配。只有对匹配不到任何规则的失败,或者首次修复未能保持的失败,才会动用 agent——它读取日志尾部和解出的文件,形成假设,从规则所使用的同一组经过审核的 action 中选择修复方案。它不能运行任意命令,也不能触碰你的源码。
大多数失败永远不会到达第三层。这对延迟和成本很重要,但更重要的是可审计性:确定性规则可以被审查,而凌晨 3 点模型的推理不能。
引起内部最多争论的规则:当 agent 不自信时,它什么都不做,让原始失败保持原样。一个在不知道的时候猜测的工具比没有工具更糟糕,因为你既不能信任绿色也不能信任红色。
这份语料库给我们的启示
将我们的检测和修复管道回放那 156,808 次失败:
1,300+ 次运行自动变绿。瞬时失败就地修复并重试,零人工干预。
~700 个修复 PR 被打开。针对缺失系统库、setup 缺口和超时设置过低等问题的永久修复。
~每季度节省 $100K,建模估算的。这个数字是模型算的,不是实测的。它假设一个中等市场团队使用付费运行器,并把工程师中断时间和计算资源都算进去了。把它当作一个参考区间,而不是确切数字。
记录的两点说明。这是针对公开工作流历史的回顾性审计,而不是在某个生产组织上的实时部署。另外自动变绿的统计受限于我们跑的时候检测器的覆盖范围,而不是理论上可修复的上限。这两个数字都应该会上升。
一次失败最终会到哪里

四种终结状态,每次失败恰好落入其中之一。在运行期间被修复。或者一个带有确定性编辑的 pull request,标记为 verified-at-runtime 或 proposed。或者通过 MCP 交给你自己的 coding agent,同时带上根本原因、精确的失败文件和完整未截断的日志(密钥已剥离)。或者给出解释——带上精确的失败 job、step 和文件——这仍然是比面对四万行日志好得多的早晨。
我需要反驳的一点
你应该对供应商发布结论为其产品类别不可或缺的调研报告保持警惕。合理。所以这里有一个不卖任何东西的版本:
去看看你上个月的失败运行记录。手动把它们分成"真实缺陷"和"环境垮了"两类。我赌第二堆比你想象的大,而且团队花在点击重跑按钮上的总人工时间,是一个你们看到会很不爽的数字。
不管你用我们、用重试 action、还是用五十行 bash 来修复,修就是了。让高级工程师当重跑按钮是一件真正愚蠢的事。
如果你想要这个工具:Latchkey 运行的是内置了这些功能的托管运行器,改一行 YAML 就能切换。我们还开源了 CI Doctor,一个 MIT 协议的 agent skill,做的是本地化诊断,免费,不用注册账号。