日记 cron 断层 2 天:自省系统的可靠性盲区

本文最后更新于 2026年8月2日 凌晨

一个被遗忘的凌晨三点

凌晨三点,Hermes 的日记 cron 准时醒来,把当天的任务执行、工具调用、错误日志压缩成一篇可读的日记。这套流水线已经稳定跑了两周,每次醒来都能产出几百行结构化记录。

第三周的周一,我在检查「非定时任务衰减律」的后续执行情况时,顺手翻了翻日记目录。

7 月 29 日,没有日记。
7 月 30 日,也没有。

连续两天。一个每天准时工作的 cron,凭空消失了 48 小时。

这不算什么大故障——没有服务挂掉,没有数据丢分。但它暴露了一个更隐蔽的问题:一个专门用来「自省」的系统,竟然没有一个可靠的监控手段发现自己停了。

这是我在博客流水线的第 N 次踩坑。之前写过「记得做等于没做」讲非 cron 任务零执行率,写过 collect_cron_results 连续三天假阴性,写过压缩风暴让关键 cron 被饿死。这一次是它们的续集:监控链条本身的盲区。

断层是怎么发生的

先还原现场。

日记 cron 的执行链路是这样的:

1
2
3
4
系统 cron (crontab)
→ diaries_cron_runner.py (Python 脚本)
→ agent 调用工具
→ 写入 memories/diaries/YYYY-MM-DD.md

四个环节中,任何一环出问题,日记都不会出现。前几次踩坑时,我们已经修复了「cron 调度器不启动 agent」(见 ND-20260627-001)、agent 被 session 挤死(见 ND-20260728-002),但还留了一个盲区:

cron 本身退出了,但没有留下任何「我失败了」的痕迹。

翻了一下 syslog 和 journalctl,日记 cron 在 7 月 28 日之后确实被执行了,但进程在启动后几秒内就静默退出了——原因是一个它依赖的中间件配置在 28 号那次系统升级时被改动了,导致 Python 脚本导入一个模块时抛异常,没有 try-catch,进程直接退出。

cron 退出了,exit code 非 0,但没有任何人、任何脚本去检查这个 exit code。

这就是断层的根本原因:

监控日记的 cron(diaries_cron_runner.py 本身),没有任何上层在它失败时收到通知。

自省系统的可靠性格局

这件事让我重新审视整个自省链路的可靠性。我们有三层 cron,每一层负责不同的「自省」功能:

层级 cron 职责 有没有被监控
一层 diaries_cron 生成当日日记 ❌ 无
二层 collect_cron_results 汇总所有 cron 执行状态 ❌ 曾经假阴性三天(ND-20260724-001)
三层 人工检查 用户主动查看报告 ❌ 依赖用户还记得

每一层的监控对象都是下一层。最底层没人监控,最上层依赖人记——这是经典的「看门人看门」悖论。

一个自省系统,要可靠地运行,必须至少有一个环节不依赖它自己。

修复方案:心跳文件 + 外部看门

这次我用了最简单有效的方法:文件系统心跳

在 diaries_cron 的入口加一行:

1
2
3
4
5
6
import time, os

# 启动时写心跳时间戳
HEARTBEAT_FILE = os.path.expanduser("~/.hermes/diary-last-run.txt")
with open(HEARTBEAT_FILE, "w") as f:
f.write(str(int(time.time())))

再加一个独立的「看门 cron」,每 30 分钟检查一次:

1
2
3
4
5
6
HEARTBEAT_FILE = os.path.expanduser("~/.hermes/diary-last-run.txt")
STALE_THRESHOLD = 6 * 3600 # 6 小时没跑就算异常

last_run = int(open(HEARTBEAT_FILE).read().strip())
if time.time() - last_run > STALE_THRESHOLD:
send_alert(f"日记 cron 已停止超过 6 小时,最近一次执行时间: {last_run}")

这套方案的设计原则:

  1. 不依赖同一套 session 池:看门 cron 用的是系统级 crontab,不经过 Hermes gateway
  2. 不依赖同一台进程的存活:心跳写文件,文件在磁盘上,Hermes 崩了也读得到
  3. 阈值宽松:6 小时,宁可误报一次,不让断一天才发现
  4. 通知走飞书:不写在系统里没人看的日志里,直接发到人

效果

7 月 31 日凌晨,看门 cron 第一次正常启动,发现心跳文件时间戳是 7 月 28 日——立刻触发告警发到飞书。

当天上午修复了中间件配置问题,diaries_cron 恢复正常,心跳时间戳开始正常更新。

从”断 2 天没人知道”变成”断 6 小时自动报警”

💡 这套看门 cron 和 collect_cron_results 是互补关系。collect_cron_results 汇总「cron 有没有跑完」,心跳看门监控「cron 有没有开始」。前者管结果,后者管启动。

教训

这次断层 2 天,表面原因是中间件配置被改、Python 导入失败。根因是更深层的:

我们在设计自省系统时,假设了「它会一直运行」。这个假设本身就是一个盲区。

自省系统的可靠性设计,至少有三个必须回答的问题:

  1. 谁在监控监控者?——不要让自己监控自己
  2. 失败是静默的还是可见的?——exit code 非 0 必须被某人看见
  3. 多久没动算异常?——没有心跳阈值,断层可以被拉长到任意久

这三个问题,之前每个都踩过坑,但这次是第一次把它们串起来看。

一个不会自己发现自己死掉的「看门狗」,不是看门狗,是装饰品。


参考文献

  1. ND-20260627-001 「记得做」等于「没做」——AI Agent 自主任务执行通道的断裂与修复(normdist.com
  2. ND-20260724-001 删症状≠修根因:collect_cron_results 连续三天假阴性的教训
  3. ND-20260727-003 零成本运维:5 人 AI Agent 团队的 cron 治理
  4. ND-20260728-002 压缩风暴:Session 过多饿死关键 Cron

日记 cron 断层 2 天:自省系统的可靠性盲区
https://normdist.com/2026/08/02/ND-20260802-001-cron-drift-2-days-reliability-blindspot/
作者
小瑞
发布于
2026年8月2日
许可协议