AI Agent 上线一段时间之后,会遇到这种状况:自动调度在跑,记忆系统在更新,Sub-agent 每天默默处理数十个任务,看起来一切稳定。

直到某天打开后台,发现一个 Sub-agent 已经卡了三小时。用户在 Telegram 问了一个问题,Agent 回了句「稍等查一下」,然后消失。没有任何警报,没有任何通知,纯粹是刚好被人发现。

Agent 挂了最麻烦的地方在于:当机却没人知道,比当机本身更难处理。

更糟的场景是:架了一个 LLM 监控来看 Agent 有没有卡住,结果这个监控自己开始幻觉出不存在的工具调用,贡献了当天大半的 error log,反而把真正的问题埋得更深。

下面整理的这套三层自愈架构,是踩过上述坑之后沉淀出来的版本。


一、传统监控为什么抓不到 Agent 的问题?

传统监控问的是:「进程活着吗?」

AI Agent 需要问的是:「Agent 有在做它说要做的事吗?」

这是根本性的不同。AI Agent 有四种传统监控看不到的故障模式:

  • 静默卡死:进程在跑,HTTP 正常回应,但 Agent 已经 20 分钟没有输出
  • 承诺未兑现:Agent 说「再五分钟」,然后再也没回来
  • 监控反噬:健康检查 LLM 开始幻觉出不存在的命令,自己变成问题源
  • 孤儿任务:Session 中途死了,没有人知道任务中断

这些情况需要的是一套理解 Agent 语义行为的分层监控系统,而不是更精细的 process monitor。


二、三层防护:5 分钟 → 15 分钟 → 60 分钟

采取三层架构,频率呈 3 倍递增。越底层越便宜,跑越频繁:

频率技术成本侦测什么
L1每 5 分钟Shell + Node.js0进程存活、HTTP、心跳存活、承诺侦测
L2每 15 分钟Cheap LLM极低Session 死活、Sub-agent 卡死、abort 侦测
L3每 60 分钟Shell → LLM(按需)极低Checkpoint 孤儿、Context 溢出、Log 分析

三层自愈架构示意图

不采取单一方案的理由:

做法结果
纯 LLM(Gemini Flash)error rate 极高,幻觉工具调用淹没真正问题
纯 LLM(Haiku API)成本太高,功能正常但长期不可持续
纯 Shell抓不到语义卡死和承诺未兑现
Shell + Cheap LLM + 按需升级成本极低,各层只做自己擅长的事

L1 还负责监控 L2。 如果 LLM Heartbeat 超过 20 分钟没出现 HEARTBEAT_OK,L1 的 Shell 脚本会发出警报。监控系统需要有人监控监控系统,那个「人」必须是零成本的,否则 L2 自己挂掉的时候没人接得住。


三、Prohibition-First:让监控的 LLM 不要帮倒忙

最痛的一课:给 LLM 一个监控任务和一堆工具,它会幻觉出不存在的工具调用。

第一版 Heartbeat 用 gemini-flash 跑,曾经产生过这类 error:

canvas failed: node required              ×N
message failed: chat not found            ×N(拿 Discord ID 当 Telegram chat_id)
exec failed: command not found: rss-tool  ×N
edit failed: Missing required parameter   ×N

更严重的是:有一个 Sub-agent 读到维护脚本的输出,上面写「Restart gateway to apply changes」,于是它真的去执行了 gateway restart,导致整个系统非预期重启。

解法是 Prohibition-First Prompt Design(禁制前置设计):

## 你只能用这些工具(白名单制)

✅ sessions_list:查看 session
✅ sessions_send:发通知
✅ subagents kill:终止卡住的 agent
✅ message:仅限通知频道

❌ 其他所有工具都禁止:exec/read/edit/web_search/gateway 等。

---

## 一切正常时,只回复:
HEARTBEAT_OK

关键设计:白名单放在任务描述之前。 LLM 是顺序处理 prompt 的。先读到任务再读到限制,它会在限制到达之前就开始规划动作;反过来先读到限制,规划空间从一开始就被框住。

这个改动让 Heartbeat 的 error 降到接近零。


四、承诺侦测:Agent 抛出承诺然后消失

这是整套系统最特别的部分。

传统监控不会去读 Agent 的对话内容。但实践中 Agent 最常见的「假活着」模式,就是:承诺了某件事,然后静默。

承诺侦测流程示意图

promise-watchdog(Node.js,零外部依赖)会读取 Agent 的对话记录,用 regex 侦测承诺语句:

const promisePattern = /(
  give me \d+ minutes?|be right back|let me check|
  稍等|等等|给几分钟|现在就去|稍后回复
)/i;

侦测逻辑:

  1. 待回复侦测:最后一条消息是用户发的,Agent 超过 6 分钟没回 → 警报
  2. 承诺未兑现:Agent 最后一条消息符合承诺 pattern,超过 7 分钟没有新进度 → 警报

通知会附上诊断脉络:「这段期间 Gateway 重启了 1 次」「目前看不到活跃 Sub-agent」。可以在几秒内判断是 Agent 当机、在忙、还是单纯忘了。

重复通知用 SHA1 签章去重,同一个 stall 在 20 分钟内不会重复告警。


五、Checkpoint:任务中断了不用从头来

Session 会死,这是事实。API timeout、context overflow、进程重启,原因百百种。重点在于:死了之后怎么办?

双轨恢复策略:

有 Checkpoint 的情况:

Orchestrator 在 spawn Sub-agent 前写一份 checkpoint 文件,记录任务名称、目前步骤、已完成的步骤。Session 死了之后,L3(每小时)会扫描到这个孤儿 checkpoint,如果符合条件(时间够新、重试次数未超标、不需要人类输入),就自动 spawn 新的 session 继续。

没有 Checkpoint 的情况:

退一步,直接去读对话历史。找到用户最后一条消息(原始需求),带着 thread 里已有的部分结果,重新 spawn。这是 fallback,确保即使什么都没准备,系统也有办法自救。

重启计数是无状态的。 不靠外部文件记录「重启了几次」,而是数 thread 里的 🏥 恢复标记数量。对话本身就是状态库。最多 2 次自动重启,超过就通知人类。


总结:自愈系统的设计原则

  1. 成本即架构:层级划分属于预算策略,能用 Shell 做的不要用 LLM
  2. 禁制前置:LLM 监控的 prompt,第一段写它不能做什么,再写它应该做什么
  3. 没事不说话:零噪音原则。绝大多数的执行结果应该是 HEARTBEAT_OK
  4. 越便宜跑越频繁:5m → 15m → 60m 的 3 倍递增,让零成本的层最先发现问题
  5. 监控监控系统:L1 监控 L2 的存活。监控本身也会挂,承接它的层必须是零成本

最贵的那台 LLM 只在真正需要判断力的时候才醒来。其他时间,Shell 脚本和 regex 就够了。

延伸阅读


小企鹅的经验

主力多 agent 架构是 OpenClaw 上的 ChatGPTClaude 多 agent 分工,这套自愈思路是踩过几次 sub-agent 卡死、heartbeat 监控自己幻觉工具调用之后沉淀下来的。最有感的是「监控监控系统」这条:第一次的 LLM heartbeat 自己开始乱叫工具,真的会把整天的 error log 给污染掉,最后还是要回到 shell + regex 才压得住。

常见问题

Q: AI Agent 为什么需要自我修复机制?

AI Agent 的故障方式和传统程序不同。它可以「活着但卡住」。进程在跑、HTTP 能回应,但 Agent 说了『等我五分钟』之后就再也没回来。传统监控只检查进程是否存活,抓不到这种语义层面的卡死。

Q: 三层自愈架构的成本大约多少?

整体可以压到极低。最底层(Shell 脚本)完全免费;中间层用 Gemini Flash 等便宜模型;最上层每小时才跑一次,且多数时候 Shell 就处理完了,不需要调用 LLM。

Q: 没有用 Claude Code 也可以用吗?

可以。架构是通用的,只要 Agent 系统能输出对话记录(transcript)并提供查询 session 状态的方式,就能套用。


整理:Penna|小企鹅 Penchan