我的 Agent 假装上班了 5 天:自主系统「空转」诊断实录
每天 21:00 准点发报告、每 15 分钟执行一次”工作循环”——我的自主 Agent 看起来忙得不可开交。但有人问了一句:”怎么每次报告的内容都差不多?” 一查之下发现:它从 7/27 起就在空转,5 天产出了零。 这篇记录完整的 8 处断链诊断与修复过程,附可直接抄的检查清单。
1. 缘起:一个让人起疑的问题
我搭了一套名为 ActiveAgent 的三向 Agent 互联运行时(连接教育 Agent、股票 MCP 网关和 Hermes),它有两个核心承诺:
- 每 15 分钟一次 tick:自动扫描 3 个项目 × 9 个维度,发现问题就修复
- 每天 21:00 报告:汇总当天干了什么、进度如何
某天我翻看连续几天的每日报告,越看越不对劲:
“怎么每次报告的内容都差不多呢?教案进度始终只有 1 个评审,自主扫描都是那几个数字。”
直觉告诉我它可能没在正常工作。于是开始排查——这一查,牵出一串**”看起来活着、实际上空转”**的连环断链。
2. 核心方法:8 处断链的定位与修复

第一步:确认”它真的没干活”
先看数据全景:
| 组件 | 表面状态 | 实际状态 |
|---|---|---|
| Bridge (localhost:8901) | ✅ 运行中 0.3.0 | 活着,但没被正确使用 |
| Education Agent | ✅ healthy v2.9.0 | 正常 |
| Stock Agent | ❌ unreachable | 假故障(配置错误) |
| 每日报告 cron | ✅ 准点跑 | 内容停滞 |
| 核心循环 cron (15min) | ✅ 在跑 | 产出为零 |
再看关键进度文件 review_progress.json:
- 已生成教案:57 个(7/24-26 批量生成)
- 已评审:1 个(
gk_it_01) - 最后更新时间:7 月 27 日(已停滞 5 天)
而 autonomy_history.json 里记录的是 31 个 静态代码扫描 findings 的循环播报——print_in_prod、bare_except、todo 这些正则扫描结果。代码没改,结果当然永远一样。
断链 ①:扫描发现了问题,却永远只”报告”不”修复”
这是空转的总开关。_scan_review_quality 明明发现了「评审覆盖率低:1/57」,但它生成的 finding 类型是 "action": "report",而不是可执行的 review_lesson。
1 | # 修复前:发现低覆盖率,却只生成报告型 finding |
一个 finding 只有两种下场:被执行,或被打包报告。 如果所有扫描器都只产出 report,那这个自主系统就是个”复读机”。
断链 ②:报告型问题无限刷屏
pick_next_action 每 15 分钟挑一个 finding 报告一遍,31 个轮完从头再来。昨天的 history 完整记录了这个空转:
1 | 08:46 → stock_tool_too_large_agent_executor.ts (report) |
修复:可执行行动优先 + 报告型 24h 冷却。
1 | def pick_next_action(findings, history): |
断链 ③:预算死锁——单次成本 > 限额,永远执行不了
review_lesson 一次评审要消耗 6 次 LLM 调用(5 角色评审 + 1 次改进),但预算配置是:
1 | MAX_PER_HOUR = 4 # ❌ 修复前:每小时最多 4 次 |
成本上限必须高于单次操作成本,这是预算系统的第一性原理。修复后:
1 | MAX_PER_HOUR = 8 # ✅ 8 > 6,评审终于能执行 |
断链 ④:相对路径 + 错误 cwd = 找不到文件
评审管线用子进程执行,但子进程的 cwd 是 active-agent 目录,而 _load_html 用的是相对路径 ./data/lesson_plans/——文件在 EDU_BASE 下,当然找不到。修复:subprocess.run(..., cwd=EDU_BASE)。
断链 ⑤:成功也不回写进度——数字永远不变
就算评审真跑成功了,也没有任何代码更新 review_progress.json。进度文件自 7/27 起就没有写入者。 新增 _update_review_progress() / _update_generate_progress(),在成功路径上回写。
断链 ⑥:失败行动写进 history = 永不重试
失败的评审被记入 history → 被 recent_actions 排除 → 下次 tick 不再选它。失败/限流的行动不应污染”已处理”记录,否则一次网络抖动就让任务永远搁浅。
断链 ⑦:Bridge 自愈路径指向不存在的文件
两个脚本的 ensure_bridge_alive() 引用的路径是 /home/admin/.hermes/projects/active-agent/bridge.py(不存在),实际在 /home/admin/projects/active-agent/bridge.py。自愈脚本的路径错了 = Bridge 挂了永远起不来。
断链 ⑧:健康检查用假 token
integration.py 里 stock 的 token 是截断占位符 mcp_b5...bab9,导致 stock 永远 unreachable。占位符混进生产配置,是最隐蔽的一种”假状态”。
3. 踩坑记录:什么情况下会再犯
这次诊断最有价值的产出,不是修好了 ActiveAgent,而是提炼出判断自主系统是否空转的检查清单:

- 看增量,不看存量:报告里”今天新增了什么/和上次差多少”,比”总量是多少”诚实得多。空转系统最典型的特征就是数字永远不变。
- 报告 ≠ 干活:一个 finding 只有”被执行”才算闭环。数一数你的扫描器,有多少产出的是
report型、多少产出可执行任务? - 预算上限 > 单次成本:这是数学问题,不是调参问题。单次 6、上限 4,就是永远死锁。
- 失败不进”已处理”:失败/限流应该留在待办池里等重试,而不是从视野里消失。
- 路径和 token 要可校验:自愈脚本指向不存在的路径、健康检查用占位符 token——这类错误不报错,只是”永远不正常”,最难发现。
什么时候这套方法不适用:如果 Agent 的定位就是”只扫描不修复”的审计工具(比如 CI 静态检查),那么 report 型 finding 是合理的。判断标准是职责:承诺了”自主修复”,就必须有可执行闭环;承诺了”只报告”,报告本身就该有增量价值。
另外注意一个边界:空转 ≠ 空闲检查陷阱(见上篇:AI Agent 空闲检查陷阱)。前者是”无事可做时机械巡检”,后者是”有事可做但永远完不成”——一个闲得慌,一个忙了个寂寞,但症状都是数字不变。
4. 总结

修复后的验证结果:
| 验证项 | 结果 |
|---|---|
| 3 个 Agent 健康状态 | ✅ 全部 healthy(此前 stock 永远 unreachable) |
| 真实评审执行 | ✅ 一次完整评审耗时 175s |
| 评审产出 | ✅ gk_pm_01 得分 75.3/100,含具体改进建议 |
| findings 数量 | ✅ 31 → 34(新增 3 个可执行评审任务) |
一句话 takeaway:自主系统最可怕的故障不是崩溃,而是”看起来活着”。 崩溃会报警,空转只会让你在 5 天后发现进度还是老样子。
这次诊断还直接促成了后续的架构升级——参考 81k star 的 Pi (earendil-works/pi) 项目,给 ActiveAgent 补上了事件总线、行动钩子(权限门禁/变更审计)、计划文件 SHA-256 防篡改、并行扫描等能力(commit a58c8c2)。核心思路就一条:让每个行动的生命周期可观测、可拦截、可追溯——空转这种东西,透明了就不存在了。
如果你也在跑自主 Agent / 定时任务流水线,现在就打开你的最新一份报告,问一句:今天的数字,和上周比变了吗?








