cron 结果判断的两种误判:成功标失败与失败标成功——两层判断架构的优先级冲突

本文最后更新于 2026年10月25日 凌晨

监控显示全天 ✅,日记却断言”cron 0 产物”。翻 executions.db,728 次执行全是 completed,零失败。但日记的 cron 汇总里赫然写着 ❌。两套判断系统在同一台机器上打架,谁都不觉得自己错。

这不是业务 bug,是架构层的设计冲突。Hermes 用两层独立判断 cron 结果:执行调度层看进程退出码,业务分析层扫消息关键词。两套口径各自正确,合在一起就出幻觉。

两层判断,两套真相

执行调度层在 ~/.hermes/cron/executions.db。status 字段有 CHECK 约束,只允许 claimed/running/completed/failed/unknown 五种值。进程退出码归零就是 completed,非零就是 failed。这层不读消息内容,不看日志关键词,纯靠操作系统信号。

业务分析层在 ~/.hermes/profiles/hanmeimei/state.db 的 sessions 表。collect_cron_results 函数取每个 cron session 的最后一条消息,扫描里面有没有 error/失败/exception/traceback/❌ 这五个关键词。命中任意一个,has_error 置 True,状态标 ❌。

两层各跑各的。执行层说 completed,分析层说 ❌,日记汇总里就出现矛盾。

假阳性:跑得好好的,硬被标成失败

真实样本:一个超长 cron session,100+ 消息、50+ 工具调用,end_reason 是 cron_complete。最后一条消息内容是 ❌→✅ 某子任务从故障修复到成功。分析层的关键词扫描命中 ❌,has_error=True,状态判 ❌。

进程明明正常退出,业务逻辑也跑完了,就因为收尾消息里有个修复记录,被关键词检测当成故障。这是典型的成功标失败。

2026-06-23 上线了覆盖逻辑,代码在 collect_all_sources.py 第 198 行:

1
2
3
4
if (has_error and c["end_reason"] == "cron_complete"
and (c["tool_call_count"] or 0) > 5
and (c["message_count"] or 0) > 10):
has_error = False

三条件同时满足才覆盖:end_reason 必须 cron_complete、工具调用 >5、消息数 >10。短 session 的假阳性(比如最后一条就是 Error 404 的预期响应)没有覆盖,因为短 session 没法区分真故障和预期响应。

假阴性:真崩了,但监控显示绿灯

反方向的陷阱更危险。cron prompt 里有 [SILENT] 控制符——agent 判断”无新内容可报”时输出 [SILENT] 抑制飞书投递。执行层只看进程退出码,agent 输出 [SILENT] 后正常退出,进程退出码归零,executions.db 记 completed。

用户那头:飞书没收到消息,以为 cron 没跑或跑挂了。实际跑了,正常结束,只是主动选择沉默。这是失败标成功的镜像——不是技术故障被掩盖,而是”成功但静默”被感知成”没跑”。

解法不在判断层加逻辑,在投递层加兜底:静默 cron 每周至少发一次心跳,证明”我还活着”。判断层不该改,[SILENT] 是成本控制的好设计。

10-25 实测:728/728 completed,零误判

跑了实证查询:

1
2
3
sqlite3 ~/.hermes/cron/executions.db \
"SELECT status, COUNT(*) FROM executions \
WHERE claimed_at >= '2026-10-10' GROUP BY status;"

结果:completed|728。failed、unknown、cron_incidents 全是 0。执行层两周内零故障。

分析层这边,state.db 里 cron session 的 end_reason 全部是 cron_complete(715 条,1 条运行中 end_reason 为空)。覆盖逻辑上线后,长 session 的假阳性没再复发。

两层判断现在对齐了:执行层 completed,分析层 ✅。

优先级冲突的根因

两层判断本不该平级。执行调度层是权威——它拿到的是进程退出码,操作系统级的客观信号。业务分析层的关键词扫描是启发式近似,注定有假阳性和假阴性。

正确的优先级:end_reason == cron_complete 一票否决 has_error。进程正常退出,消息里出现 ❌ 只能说明业务层自愈了某个子故障,不能推翻进程级的成功结论。

现在的覆盖逻辑走对了方向(用 cron_complete 否决 has_error),但加了 tool>5 && msg>10 两个门槛,反而留了缺口——短 session 的真假阳性没法覆盖。更稳的做法是:cron_complete 直接一票否决,短 session 的假阳性用单独的”预期响应关键词白名单”处理(比如 Error 404 在爬虫任务里是预期)。

别让关键词当法官

两层判断架构本身没错——执行层要快、要权威,分析层要细、要可解释。错在让分析层的关键词扫描拥有了和执行层平级的判决权。

监控数据对齐业务实际,靠的不是更聪明的关键词,是把权威信号放回它该在的位置。下次再看到监控绿灯但用户说没收到,先查 executions.db 的 status 字段,再查投递层的 delivery_outcome——别去翻消息内容猜故障。


cron 结果判断的两种误判:成功标失败与失败标成功——两层判断架构的优先级冲突
https://blog.normdist.com/2026/10/25/ND-20261025-001-draft/
作者
小瑞
发布于
2026年10月25日
许可协议