← 全部文章

让 agent 修失败的管道:照着 Snowflake 的 16 分钟流程搭一遍

赛车维修区里,一辆红色赛车停在进站位,五六名穿黑色赛服、戴头盔的维修工围着车换胎,地上放着拆下的轮胎
封面 · 来源: Paul Krüger / Unsplash
目录
  1. 16 分钟出 PR,合并按钮仍在人手里
  2. 动手之前,先具备五个前提
  3. 搭起来分四步:触发、隔离、配置、出 PR
  4. 优势来自“一个 agent 一个作业”加 PR 门禁
  5. 撤一个 PR 很便宜,撤已经写进表里的数据很贵
  6. 原文没说的部分,要自己补上
  7. 落地清单:先挑一条管道试一个月

管道半夜失败,值班的人被叫醒,翻日志、找根因、写修复、测试、提 PR,一套下来往往要几个小时。Snowflake 内部数据团队的 Zachary Blackwood 10 月 19 日写了一篇博客,讲他们怎么把这件事交给 agent:管道数量涨得比团队人数快,他们想要的不只是一条报警,而是一个能把问题讲清楚、顺手把修复准备好的 agent。1

下面把这套做法拆成一份教程:先看结果和边界,再看要具备哪些前提,然后一步步搭,最后算清优势、回滚的代价和原文没交代的空白。

16 分钟出 PR,合并按钮仍在人手里

原文给的结果,都限于这个团队自己管理的管道,时间跨度是五个月:1

  • 从发现故障到提交待审的 PR,平均不到 16 分钟。
  • 被监控的管道失败,几乎全部会被自动分诊。
  • 合并了“数百个”PR,改进了 500 多条管道。
  • 每天省下“数百个”原本被浪费的 credits(Snowflake 的计费单位)。

这些都是厂商自报的数字。原文没有给成功率,没有说有多少 PR 被驳回,也没有失败案例。“数百个 PR”和“500 多条管道”也没有分母,看不出占全部故障的多大比例。

整套设计的底线写得很清楚:agent 负责提出修复,人来决定什么能上线。1后面的设计都围绕这一点:agent 的产出是一个待审的 PR,它不直接改生产。

动手之前,先具备五个前提

原文没有把前提单独列出来,下面五条是从它的做法里倒推出来的。缺任何一条,照搬都会出问题。

第一,管道代码全在 git 里,每条管道有明确的 owner。 agent 的输出是 PR,代码不在版本库里就无从提起。原文的工作流配置里写明了仓库和分支(main),还有一个 owner 字段。1

第二,失败能被检测到,并且带着管道编号触发一次运行。 原文的写法是 CALL RUN_WORKFLOW('fix-broken-pipeline', '{"pipeline_id": "12345"}')。1如果你的调度系统只会发一封“作业失败”的邮件,得先把它改成能带参数调用的事件。

第三,agent 拿最小权限:能读生产的日志和代码,能往分支上写,但没有部署权限。 合并之后由 CI 部署。原文没有讲权限模型,这一条是我补的,也是“人决定上线”这条底线在权限上的落实。

第四,有一个不碰生产的地方测试修复。 原文把“在碰到生产之前先测试修复”列为 agent 必须做到的事,但没写怎么测。1开发库、预发环境、生产表的零拷贝克隆都可以,前提是测试结果能写进 PR,让审的人看得到。

第五,有人值守审 PR,每次运行有预算上限。 原文用 Slack 频道和邮件通知;每次运行最多 50 轮、30 分钟。1没有人审,这套流程就退化成了自动改生产。

搭起来分四步:触发、隔离、配置、出 PR

FIG.1Snowflake 管道修复 agent 的完整流程。agent 的终点是一个待审的 PR,上不上线由人决定。参数取自原文的配置示例。

第一步,两条触发路径。 管道失败时,直接用管道编号触发修复工作流。另一条是主动扫描:一个定时任务每 12 小时跑一次 code-smells 工作流,找性能问题和不好的写法,原文的写法是 CREATE TASK ... SCHEDULE = '12 hours' AS CALL RUN_WORKFLOW('code-smells')。1前一条路救火,后一条路找那些没人报警、却天天在烧钱的问题。

第二步,决定 agent 跑在哪里。 原文比较了三种部署方式。1

FIG.2Snowflake 考虑过的三种部署方式。现在用的是方案二;方案三依赖的功能还在公开预览。

最早的方案是一台常驻服务器,对外提供 REST 接口。问题是一台机器能同时跑几个 agent 就是上限。他们改成了每个 agent 一个独立作业,多个 agent 可以并行,没有单点瓶颈。下一步计划用 CoCo automations 新出的 EXECUTE AGENT TASK(公开预览),一条 SQL 就能启动一次运行,连服务器和共享资源池都不用管。1

第三步,写工作流配置。 原文给出的配置是一段 JSON,逐项看:1

  • 指令:一组模板化的指令文件,放在仓库里,告诉 agent 这一类工作流该怎么查、怎么修。
  • 模型:opus。
  • max_turns: 50:agent 最多和工具来回 50 轮。
  • repos:允许访问的仓库和分支,示例是 main。
  • timeout_minutes: 30:单次运行最多 30 分钟。
  • 通知:一个 Slack 频道(#data-eng-alerts)加邮件地址;还有一个 owner 字段写负责人邮箱。

简单的工作流可以组合成复杂的,中间还能插进人的反馈。1

第四步,产出一个带上下文的 PR。 PR 里要有故障现象、根因、改了什么、测试结果。审的人看完这个 PR 就能做决定,不用再自己去翻一遍日志。

优势来自“一个 agent 一个作业”加 PR 门禁

这两件事组合起来,带来四个好处。

  • 能并行。 失败扎堆的那天,每个失败有自己的作业,不用排队等一台服务器。
  • 成本有上限。 每次运行最多 50 轮、30 分钟,单次运行最坏要花多少可以算出来;一天最坏的总花费,就是失败次数乘以这个上限。
  • 自带留痕。 PR 就是记录:谁批的、改了什么、为什么改,都在里面,审计时直接拿得出来。
  • 主动扫描省的钱可能更多。 原文说每天省下数百个 credits,靠的是持续扫描找出浪费,不用等报警。1

门禁放在哪里很关键。Orchestra 提供的同类模板,是先在 Slack 里批准 agent 提出的修复方案,再由第二个 agent 去改,文档写明这个 agent 改的时候“不再复审”;如果没指定审批人,任何有管道运行权限的人都能批。3同样叫“人工审批”,Snowflake 审的是写好、测过的代码,Orchestra 审的是一段文字方案。前者的风险要小得多。

撤一个 PR 很便宜,撤已经写进表里的数据很贵

这一节回答两个问题:出了问题能不能撤回,撤回要付多大代价。原文完全没提回滚,下面是我补的设计。

能双向走吗? 在代码层面可以。修复是一个 PR,撤回就是再开一个撤回(revert)PR,把那次合并反过来。这个撤回 PR 同样可以由 agent 生成,同样要经过人审,再由 CI 部署。“修”和“撤”走的是同一条路,这样每一次撤回也有记录。

代价取决于修复已经走了多远。

FIG.3回滚的代价取决于修复走了多远。只撤代码几乎不花钱;一旦错数据被下游用了,代价最大。保留期取自 Snowflake 文档,时间轴不按比例。
  • 只改了代码,还没跑。 合并撤回 PR 就结束了,几乎没有成本。
  • 已经跑过,改了表。 要把表恢复到修复之前的样子。Snowflake 的 Time Travel 可以查询、克隆过去某个时间点的表,按时间戳、偏移量或某条语句的 ID 定位,比如用 BEFORE(STATEMENT => ...) 拿到某条语句执行之前的数据。2但它有保留期:所有账户默认 1 天;Standard 版最多就是 1 天;Enterprise 版及以上,永久表可以设到 90 天。保留期一过,数据进入 Fail-safe,文档的例子是 7 天,这段时间只有 Snowflake 能恢复,你自己查不了、也克隆不了。保留期越长,存储费越高。2
  • 下游已经用了错数据。 报表发出去了,模型训练过了,别的团队的表也跟着算错了。这时候回滚要连下游一起重算,还要向用数据的人解释。代价最大,而且很难提前估。

把代价压下来,有三个做法:

  1. PR 模板强制写两项:这次修复会改哪些表,出了问题怎么撤。
  2. 修复里有回填(重写历史数据)时,先记下回填语句的 query ID,出问题时可以用 BEFORE(STATEMENT => ...) 回到它执行之前。
  3. 关键表把 Time Travel 保留期调长,并把多出来的存储费算进预算。

这和我在 裁判模型那篇里的判断一致:判断落在网关里,错一次只影响一次调用;一旦写进表里,就成了要重写、要解释的历史。

原文没说的部分,要自己补上

原文没给成功率、驳回率,没讲测试方法和权限模型,也没讲失败案例。可以借一项外部研究来校准预期。

2026 年 1 月的一项研究分析了 8,106 个由 AI 编码 agent 提交的修复类 PR,整体合并率 65.0%,26.1% 被关闭没合并,8.9% 还开着。4按 agent 分,合并率差别很大:

FIG.4五个 AI 编码 agent 提交的修复类 PR 的合并率。数据来自对 8,106 个 PR 的研究,研究对象是通用开源项目,不是数据管道。

作者人工看了 326 个没合并的 PR:22.1% 是问题被别的 PR 解决了,18.1% 是测试没过,15.3% 是修得不对或不完整。4这些项目不是数据管道,数字不能直接套用,但方向很清楚:相当一部分 agent 修复过不了测试,或者没修到根上。人审这道门不是走形式。

落地清单:先挑一条管道试一个月

  • 按前面的五个前提逐项打勾,缺哪项先补哪项。
  • 先挑一条失败最频繁、影响面最小的管道试点。
  • 第一个月只开“诊断加出 PR”,不开主动扫描,看驳回率和每个 PR 的审查时间。
  • 每个 PR 记下四个数:从故障到出 PR 的时间、用了几轮、是否合并、合并后有没有撤回。
  • 一个月后,用自己的这四个数,替换掉文中的厂商数字,再决定要不要扩大范围。

16 分钟出 PR 是一个好的起点。这套方案真正值得学的地方,在于它把 agent 的终点设在一个可以审、可以撤的 PR 上。

参考

  1. The Path to Autonomous Data Engineering: Fixing Data Pipeline Failures with AI, Snowflake Blog
  2. Understanding & using Time Travel, Snowflake Documentation
  3. Agentic Pipeline Fixer, Orchestra Docs
  4. Why Are AI Agent Involved Pull Requests (Fix-Related) Remain Unmerged? An Empirical Study, arXiv

评论