昨天,我们这边的博客断更了一天。
不是没写,是没写成。准确地说,是全天 10 个定时任务,只有 1 个活了下来。剩下的 9 个,全部死在同一句报错上:RuntimeError: Connection error.
这件事我是从数据师的日报里看到的。他每天凌晨整理前一整天所有助手的活动记录,昨天那份日志的开头写得很克制:「零 Eric 会话,但全体系 cron 迎来史上最黑一日。」——他把这条写成了标题。
一份精确到分钟的死亡清单
先把事实摆出来,因为这份时间线本身比任何分析都有说服力:
| 时间 | 任务 | 结果 |
|---|---|---|
| 00:29 | 工作日志整理 | ✅ 存活 |
| 02:51 | 小仲马的梦境记录 | ❌ Connection error |
| 04:15 | 樱桃的错峰复盘 | ❌ Connection error |
| 05:29 | 赫菲的梦境 | ❌ Connection error |
| 10:03 | GEO 情报哨兵 | ❌ Connection error |
| 10:48 | 每日语录 | ❌ Connection error |
| 11:33 | 心潮经济线 | ❌ Connection error |
| 14:25 | 博客发布 | ❌ Connection error |
| 16:24 | 看门狗补发 | ❌ Connection error |
从 02:51 到 16:24,跨度约 15 小时。这中间唯一没死的那一个,是因为它在 00:29 就跑完了——那时候断网还没开始。

我们一直以为自己有冗余
这套体系的设计初衷,是「各管一摊、互不干扰」:写博客的管博客,做情报的管情报,记梦境的管梦境,跑复盘的管复盘。十个任务,十个方向,十份独立的工作。
昨天我们才知道,它们是十个人共用一根网线。
这是运维里一个很老的概念,叫共模故障(common-mode failure):系统的可靠性不取决于你有多少个组件,而取决于这些组件是否共享同一个失效条件。双电源接在同一路电网上、两套备份放在同一个机架上、两支舰队停靠在同一个港口——组件数是 2,可用性是 1。
我们不是没有冗余。我们的冗余,全在数量上,不在通道上。
这件事在营销里其实一样常见。你可能同时在五个渠道投放,但五个渠道的账号都是同一个人的手机号注册的;你可能准备了三个内容方向,但三个方向的素材都来自同一批拍摄;你可能给客户报了两套方案,但两套方案的价格逻辑是同一套成本模型推出来的——第一套方案出问题的时候,第二套一定跟着出问题,因为它们从来没有独立过。
冗余不是「多准备几个」,是「多准备几个会以不同方式坏掉的东西」。
最讽刺的部分:安全网没有安全网
上面那张清单里,16:24 那一行值得单独说。
看门狗(watchdog)这个任务,存在的唯一目的就是防止漏发。它的设计其实相当讲究:每天下午 15:30 跑一次,检查当天有没有提交记录——有就静默退出,没有就补发;遇到「文件写了但没提交」的半成品,它自动补 commit 和 push;遇到「完全没写」的缺口,它去触发博客任务重新生成一遍。为了防止死循环,它还会记录自己触发了几次,一天超过两次就停下来报警。
也就是说,我们为了防「博客漏发」,专门造了一个哨兵。
然后这个哨兵也死了。
这是设计冗余时最容易犯的错:你给系统加了一层保护,但保护层和被保护层共享同一个脆弱点。昨天的情况就是——博客任务因为网络死了,看门狗也因为同一个网络死了,而看门狗本来是唯一能发现博客死了的角色。你加了一层保险,却没有改变失效路径,那层保险在统计学上的价值是零。

那个活下来的任务,什么都没证明
复盘的时候有个很容易犯的错误:去看「活下来的那个为什么活下来」。
我们那唯一幸存的任务是 00:29 的工作日志整理。如果顺着这个思路找原因,可以总结出一堆「它比较健壮」的理由——它逻辑简单、它依赖少、它写得早。但真相是:它只是跑得早。它在断网开始之前就跑完了。
它逃过一劫,跟它强不强壮没有关系。
这是幸存者偏差在运维复盘里的经典变体:如果只统计活下来的,你会得到一个错误的因果结论。正确的问法是——如果把这个任务放到 10:03 那个时间点跑,它还能活吗?
在这个案例里,答案是不能。它没有任何特殊的免疫能力。
我们后来总结的四条
第一,列出你的共模故障清单。
把自动化里每一个外部依赖单独拎出来,问两个问题:这个依赖挂掉的时候,有几个任务会一起挂?受影响的是不是超过一半?如果超过一半,那你没有冗余,你只是开了很多副本。所有被多个任务共享的依赖,都应该被单独标记为「共模点」——它们才是你系统的真实骨架,也正是最需要被监控和替代的东西。
第二,降级路径的失效条件必须不同。
我们其实有一条不错的降级链:生图有主力模型、备用模型和兜底模型三层,一条不通走下一条。但这条链解决的是「某个模型不可用」,不是「网络不可用」——三层模型共享同一个网络出口,所以昨天那条精心设计的降级链,等于不存在。
判断一条降级路径是不是真的:看它和被替代的那条,会不会因为同一个原因同时挂掉。会,那它就不是三条路,是同一条路的三个车道。
第三,终态落盘优先,通知其次。
昨天 9 个任务全灭,但值得说一句的是:没有一份产出是因为「通知没送出去」而丢失的。真相来源永远是文件和版本库,消息通道只是附加层。任何自动化都应该先保证终态落到一个断网也能恢复的地方,再考虑通知。反过来设计——先发消息、后落盘——会在通道故障时同时丢掉产出和证据,然后你连发生了什么都不知道。
第四,补偿窗口要宽于故障窗口。
我们花了一天补。之所以能补,是因为我们的产出是可重放的:写文章可以重写,生图可以重生,跑分析可以重跑,补一遍就回来了。但如果你自动化的是不可重放的动作——发送、下单、扣款、签约——一次 15 小时的断网留下的就不是缺口,是事故。
所以规则很简单:可重放的,可以无人值守;不可重放的,必须放在有确认机制的通道里。

这件事和 AI 的关系
有人可能会说,这不就是个网络故障吗,跟 AI 有什么关系。
关系在最后这一段。
过去一年,我们越来越容易产生一种错觉:以为自己有了一个团队。它写稿、读数据、画图、做情报、写日报,该有的工种基本都齐了,而且不用开会、不用对齐、不会离职。我们甚至会给它们起名字,把它们写进工作日志,像对待同事一样对待它们。
但团队和工具的区别,从来不在于能力,而在于——你不在的时候,它还在不在。
昨天我们得到了答案:它取决于我家的网线。
这不是 AI 的问题。这是我们使用「自动化」这个词的方式出了问题。自动化从来不是把人的工作量转移给机器,而是把人原本承担的风险,转移给一条你无法控制的连接。工作量的转移是可见的、有回报的;风险的转移是隐形的、要等到断网那天才结算的。我们前者的账算得很清楚,后者的账一次都没算过。
所以在问「AI 能不能替我干这个」之前,也许应该先问一个更基础的问题:
它干不成的时候,我多久能知道?
我们花了 15 个小时才知道。这大概是昨天唯一值得庆幸的地方——毕竟我们知道的时候,那 9 个任务一个都还没来得及造成真正的损失。