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

目录
管道半夜失败,值班的人被叫醒,翻日志、找根因、写修复、测试、提 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
第一步,两条触发路径。 管道失败时,直接用管道编号触发修复工作流。另一条是主动扫描:一个定时任务每 12 小时跑一次 code-smells 工作流,找性能问题和不好的写法,原文的写法是 CREATE TASK ... SCHEDULE = '12 hours' AS CALL RUN_WORKFLOW('code-smells')。1前一条路救火,后一条路找那些没人报警、却天天在烧钱的问题。
第二步,决定 agent 跑在哪里。 原文比较了三种部署方式。1
最早的方案是一台常驻服务器,对外提供 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 部署。“修”和“撤”走的是同一条路,这样每一次撤回也有记录。
代价取决于修复已经走了多远。
- 只改了代码,还没跑。 合并撤回 PR 就结束了,几乎没有成本。
- 已经跑过,改了表。 要把表恢复到修复之前的样子。Snowflake 的 Time Travel 可以查询、克隆过去某个时间点的表,按时间戳、偏移量或某条语句的 ID 定位,比如用
BEFORE(STATEMENT => ...)拿到某条语句执行之前的数据。2但它有保留期:所有账户默认 1 天;Standard 版最多就是 1 天;Enterprise 版及以上,永久表可以设到 90 天。保留期一过,数据进入 Fail-safe,文档的例子是 7 天,这段时间只有 Snowflake 能恢复,你自己查不了、也克隆不了。保留期越长,存储费越高。2 - 下游已经用了错数据。 报表发出去了,模型训练过了,别的团队的表也跟着算错了。这时候回滚要连下游一起重算,还要向用数据的人解释。代价最大,而且很难提前估。
把代价压下来,有三个做法:
- PR 模板强制写两项:这次修复会改哪些表,出了问题怎么撤。
- 修复里有回填(重写历史数据)时,先记下回填语句的 query ID,出问题时可以用
BEFORE(STATEMENT => ...)回到它执行之前。 - 关键表把 Time Travel 保留期调长,并把多出来的存储费算进预算。
这和我在 裁判模型那篇里的判断一致:判断落在网关里,错一次只影响一次调用;一旦写进表里,就成了要重写、要解释的历史。
原文没说的部分,要自己补上
原文没给成功率、驳回率,没讲测试方法和权限模型,也没讲失败案例。可以借一项外部研究来校准预期。
2026 年 1 月的一项研究分析了 8,106 个由 AI 编码 agent 提交的修复类 PR,整体合并率 65.0%,26.1% 被关闭没合并,8.9% 还开着。4按 agent 分,合并率差别很大:
作者人工看了 326 个没合并的 PR:22.1% 是问题被别的 PR 解决了,18.1% 是测试没过,15.3% 是修得不对或不完整。4这些项目不是数据管道,数字不能直接套用,但方向很清楚:相当一部分 agent 修复过不了测试,或者没修到根上。人审这道门不是走形式。
落地清单:先挑一条管道试一个月
- 按前面的五个前提逐项打勾,缺哪项先补哪项。
- 先挑一条失败最频繁、影响面最小的管道试点。
- 第一个月只开“诊断加出 PR”,不开主动扫描,看驳回率和每个 PR 的审查时间。
- 每个 PR 记下四个数:从故障到出 PR 的时间、用了几轮、是否合并、合并后有没有撤回。
- 一个月后,用自己的这四个数,替换掉文中的厂商数字,再决定要不要扩大范围。
16 分钟出 PR 是一个好的起点。这套方案真正值得学的地方,在于它把 agent 的终点设在一个可以审、可以撤的 PR 上。
参考
- The Path to Autonomous Data Engineering: Fixing Data Pipeline Failures with AI, Snowflake Blog
- Understanding & using Time Travel, Snowflake Documentation
- Agentic Pipeline Fixer, Orchestra Docs
- Why Are AI Agent Involved Pull Requests (Fix-Related) Remain Unmerged? An Empirical Study, arXiv
评论
匿名评论审核后显示,用 GitHub、X、Facebook 登录后直接显示。邮箱不公开,只用于回复通知。
评论暂时加载不出来,稍后再试。