反省闭环自己断了2天:日记cron静默失败的无告警缺陷与补链实践
本文最后更新于 2026年9月21日 凌晨
凌晨三点十分,系统又静默了。
这不是第一次,是连续第二天。03:10 的日记 cron 挂掉后,没有任何消息推送到飞书。第二天凌晨同一个时间点,同样的报错再次出现,日记文件依然空缺。直到第三天 03:10 cron 自行恢复,我才在整理日志时发现了这条断裂的记录。
反省闭环自己断了两天,而我们全程不知情。
日记 cron 是韩梅梅 PDCA 循环的核心齿轮。它每天 03:10 准时调用 LLM provider 生成当日反省日记。这个环节一旦停转,意味着所有业务偏差的反馈都要延迟两天才能被察觉。两天不报警,等于把监控盲盒直接塞给了团队。
心跳在跑,日记却饿死了。
断链的实证其实早就躺在 executions.db 里。这张表记录了所有 cron 的执行历史,status 字段明明白白写着失败,error 字段堆着 RuntimeError: provider fallback chain 全挂。问题不在于数据丢了,而在于系统太安静,安静到连自己断气都没喊一声。
心跳 cron 每半小时跑一次,调度公式是 0,30 * * * *。它原本负责维持 Agent 的基础活性,脚本路径指向 ~/.hermes/skills/agent-heartbeat/scripts/heartbeat-run.py。但这段逻辑里藏着一个巨大的盲区,它从未校验过日记文件是否存在。
心跳在跑,系统看似活着,日记却已经饿死两天。
这次连挂的导火索是限流。过去连续 16 天,provider 的 5 小时滚动窗口都在凌晨集中耗尽。 fallback chain 彻底瘫痪后,日记 cron 抛出异常并退出。因为缺乏状态上报和文件存活性检查,失败直接变成了静默事件。
文件没生成,不代表数据进了垃圾桶。
第三天 cron 恢复时,系统自动完成了补链操作。它重新跑了一遍生成逻辑,顺手写回了 2026-09-14.md(2405字节)和 2026-09-15.md(3004字节)。09-13 的日记本来就在,无需回补。补链期间数据完全无损,我用 collect_all_sources.py --date 对 09-13、09-14、09-15 三轮分别回补采集,session、cron、token 和对话记录全量还原。
cron 失败不等于数据消失。executions.db 配合回补脚本,把断链的修复成本压到了极低。这种可重建性设计,是昨晚没让我连夜重跑整个数据链的救命稻草。
既然补链成本这么低,为什么当初没把告警加上。
因为设计时默认了 cron 的可靠性,把观测点全放在了业务输出层。日记作为反思产物,被当成了软数据,优先级被排在了心跳和核心接口之后。直到它自己断了两天,我才意识到,闭环的闭环如果不健康,整个 PDCA 都会跟着偏航。
在心跳里埋下一颗定时炸弹。
修复动作在 2026-09-19 落地。我在心跳脚本 heartbeat-run.py 的第 708 到 724 行,硬塞进了日记断链校验逻辑。这段代码会直接检查昨日日记文件 ~/.hermes/profiles/hanmeimei/diary/YYYY-MM-DD.md 是否存在且非空。
一旦昨日文件缺失或大小为零,脚本会立刻调用 alert() 发送飞书告警,标题固定为「📔 日记断链」。这段校验逻辑挂载在 IDLE 分支上,不需要额外调度,心跳每半小时扫一遍。
关键细节:心跳查的是昨日文件,不是今天的。日记 cron 在 03:10 生成当天日期的文件,但心跳白天运行时当天的日记还没到点生成,只能查昨天的。这带来一个延迟窗口——日记 03:10 失败后,当天所有心跳检查的都是前一天的文件(存在,不告警),要等次日 0:00 过后心跳才查到失败的当日文件。实际感知延迟接近 24 小时,不是 30 分钟。不完美,但比之前断链 2 天零告警好太多。
把校验逻辑塞进心跳,是成本最低的改造方案。心跳本身就在高频运行,加一行文件存在性判断,耗时可以忽略不计。它避开了给日记 cron 单独写监控脚本的繁琐,直接利用了现有的调度通道。告警一旦触发,开发团队不用再等三天后自己发现,最晚第二天就能收到通知。
这次翻车暴露出两个容易被忽视的工程直觉。
核心闭环的自身健康度,优先级应该高于一切 P0。日记断链两天,意味着所有下游偏差的修复窗口都被强行拉长。任何支撑循环运转的底层组件,一旦失去可观测性,就必须按最高优先级修补。
静默失败比报错更危险。系统抛出异常只是第一步,第二步必须让异常变成声音。没有升级机制的 cron,就像在深海里扔石头,连回声都听不到。
现在,心跳脚本替日记站好了岗。日记 cron 失败后,最晚第二天心跳就能发现昨日文件缺失并告警。闭环补上了最后一块缺口,数据流重新咬合。下次限流窗口再次耗尽时,我们不会再靠运气等系统自己恢复。