跳到主要内容
返回写作

我的 AI 自进化系统每天都在空转,而 cron 报告一切正常

一次对 AI 自进化管线空转问题的完整诊断——616 个积压 session、被反复扫描的 watchdog、以及一个让知识库停止增长三天的隐藏 bug。

本文索引12
  1. 01一切正常,直到我看了数字
  2. 02那个 [] 是什么
  3. 03根因链:三个小问题叠在一起
  4. 04根因 1:Reflector 不挑食
  5. 05根因 2:取最新,而不是取最早
  6. 06根因 3:零产出 = 永不标记
  7. 07修复:三个改动
  8. 08A. 过滤短会话
  9. 09B. 从最早的积压开始处理
  10. 10C. 零产出也标记(真正的根因)
  11. 11为什么我说这是"假装工作"
  12. 12数字收尾

我的 AI Agent 自进化系统每天早上 3:45 都会跑一次"上下文进化"任务。它一直报告"运行成功"。直到我发现它的知识库三天没涨了。


一切正常,直到我看了数字

我在服务器上跑了一套 AI Agent 自进化管线。其中有个叫 ACE Context Evolution 的 cron job,每天凌晨 3:45 启动:扫描 agent 会话、提炼洞察、合并进一个知识库文件,让我的 agent 越用越懂我。

那天早上它推送了一份报告:

[03:45] Running Reflector (session analysis)...
[]
[03:48] Running Grow-and-Refine...
[SKIP] 上下文 124 chars < 阈值 2000
✅ ACE Evolution Complete

看起来完美。退出码 0,三分钟跑完,"✅ Complete"。

但我是个会看数字的人。我翻了历史产物文件,发现不对劲:

日期活跃条目数
8/1217
8/1319
8/144

知识库从 19 条缩到 4 条。而且这 4 条还是老面孔——requests 超时、Python 3.12 语法、eval 安全风险,都是几天前的旧洞察。三天没有任何新东西进来。

系统在假装工作。


那个 [] 是什么

先解释一下这套管线在干嘛。ACE(Agent Context Evolution)的流程是:

Reflector (读 agent 会话轨迹)
  → 提取结构化洞察 (pattern/bug/knowledge/preference/warning)
  → Delta-Merger (去重、合并、淘汰)
  → Bundle (生成知识库文件)

关键在第一步。Reflector 去数据库里找"未处理的会话",每个会话用 LLM 读一遍,提炼洞察。

cron 报告里的 [] 不是报错——是 Reflector 处理了一批 session,但产出 0 条洞察。空数组被传给下游,下游如实打印了个空,然后一切都"成功"了。

我手动跑了一遍,得到更精确的信息:

{"processed": 3, "insights": 0, "failures": 0}

处理了 3 个会话,0 条洞察。为什么?我查了数据库,发现一个更吓人的数字:616 个会话积压未处理

一个每天跑的 cron,为什么会积压 616 个?


根因链:三个小问题叠在一起

根因 1:Reflector 不挑食

_get_unprocessed_sessions() 把所有会话都列进处理队列,不管内容多少。我的系统里有 595 个 cron 会话——大多是 watchdog 类的短会话:

cron_1d46b7ac95c5_20260814_032123  msgs=6  dur=8s
cron_1d46b7ac95c5_20260814_022022  msgs=7  dur=10s
cron_1d46b7ac95c5_20260814_011922  msgs=4  dur=6s

6 秒、4 条消息的 watchdog 会话,能提炼出什么洞察?没有。它就是设计来"看一眼然后退出"的。

而真实交互会话平均 12.5 条消息、716 秒——那才有东西可挖。

根因 2:取最新,而不是取最早

--limit 20 每次处理 20 个。但排序是 ORDER BY started_at DESC——最新的 20 个

最新的会话是什么?就是刚跑完的 watchdog。每小时一个,永远占满队首。

结果:cron 每次都在处理同一批 watchdog,238 个真实交互会话永远排在后面。就像餐厅排队,队头永远是一群只看菜单不点菜的人,真正要吃饭的顾客在队尾站了三天。

根因 3:零产出 = 永不标记

这是最隐蔽的一个。Reflector 处理完一个会话后,把洞察写进日志。但如果产出 0 条洞察,什么都不写——"已处理"的标记也不存在。

所以 watchdog 会话处理完,等于没处理。下次 cron 启动,它又出现在队列里。每天被 LLM 读一遍,每天产出 0,每天重新排队。

616 个积压里,有多少是被反复扫描的? 我看了一眼:mc-checker-watchdog 一个 job 就贡献了 63 个。它每小时跑一次,每次都被 Reflector 当"新会话"处理一次。

三天白跑,不是偶然,是必然。


修复:三个改动

A. 过滤短会话

给查询加两个阈值——少于 3 条消息、持续不到 60 秒的会话直接跳过:

MIN_MESSAGES = 3            # 至少 3 条消息
MIN_DURATION_SEC = 60.0     # 至少持续 60 秒
 
rows = conn.execute(
    "SELECT id, MAX(started_at) AS latest FROM sessions "
    "WHERE message_count >= ? "
    "AND (COALESCE(ended_at, started_at) - started_at) >= ? "
    "GROUP BY id ORDER BY latest ASC",
    (MIN_MESSAGES, MIN_DURATION_SEC),
)

效果立竿见影:616 → 145。watchdog 会话大部分直接消失。

B. 从最早的积压开始处理

排序从 DESC 改成 ASC——先消化 backlog,再处理新会话

# hermes 列表按 started_at ASC 生成,取头部即最早 backlog
sessions = sessions[:limit]

这样每天 20 个,一周左右清完积压,之后维持当天新增。

C. 零产出也标记(真正的根因)

新增一个 processed-sessions.jsonl,无论产没产出洞察,处理过的会话都写进去:

def _mark_processed(session_id: str):
    """记录 session 已处理(幂等),即使 0 产出也标记"""
    # 先查重,避免重复行膨胀
    # 然后 append: {"session_id": ..., "processed_at": ...}

查重逻辑保证同一会话只记录一次。现在 watchdog 被处理一次,就永远离开队列。


为什么我说这是"假装工作"

修复本身不难。真正让我警觉的是这个系统的反馈信号

cron 报告显示 "✅ Complete",但它在空转。知识库三天没涨,但没有任何告警。自进化系统最大的风险不是崩溃——崩溃会出声。是静默退化:一切正常地、持续地产出"无"。

这让我想到一个更普遍的规律:自动化系统需要"负结果可见"

LLM 批量处理脚本必须有 --limit,否则积压场景全量扫描必超时——这是 8 月 5 日就踩过的坑,当时只修了超时,没修根因。

"0 条洞察"和"报错"一样是有效信息。空数组应该被记录、被统计、被上报——而不是静默通过管道。

如果你也有定时跑 LLM 批处理的管线,建议检查三件事:

  1. 你的处理队列是"处理过"还是"产出过"? 零产出的任务有没有被标记为已完成?
  2. 排序方向对吗? 取最新还是取最早,在积压场景下是两个完全不同的系统。
  3. 空结果可观测吗? 下游收到空数组时,是打印一个优雅的 [],还是记录一条"空产出"?

数字收尾

指标修复前修复后
未处理积压616143(持续下降)
watchdog 重复扫描每天重复一次即止
知识库新增0/3天恢复增长

修复代码在 ~/.hermes/scripts/evolution/reflector.py。如果你的 agent 也跑自进化管线,值得看一眼它的队列实现。

系统静默退化的可怕之处在于:一切正常,直到你翻数字。


这是我维护的 AI Agent 自进化系统的一次真实排障。想了解整个系统架构?看我之前写的 self-evolving-system-architectureagent-evolution-bio-inspired