游乐游手机版
首页/AI教程/文章详情

自主Agent可观察性工程实践:如何知道Agent挂了?

时间:2026-06-05 16:44
三个月前,有支AI Agent在凌晨2点挂了。 它的任务听起来挺简单:每天抓数据、生成报告、再推给下游系统。但挂了就是真挂了——没有报错、没有告警,下游系统只是安安静静地停止接收新数据。直到第二天早上有人问“昨天的报告呢”,才发现出事了。 当时所谓的“监控”是什么?就是每过一小时手动跑一遍 ps a

三个月前,有支AI Agent在凌晨2点挂了。

Agent 挂了我怎么知道?自主 Agent 的可观察性工程实践

它的任务听起来挺简单:每天抓数据、生成报告、再推给下游系统。但挂了就是真挂了——没有报错、没有告警,下游系统只是安安静静地停止接收新数据。直到第二天早上有人问“昨天的报告呢”,才发现出事了。

当时所谓的“监控”是什么?就是每过一小时手动跑一遍 ps aux | grep agent

回过头来看,这其实是犯了一个根本性的错误:错把“进程活着”当成了“Agent正常运行”。

为什么Agent的可观察性比普通服务更难

普通服务要是挂了,看HTTP返回的5xx状态码就一目了然。但Agent的行为模式要复杂得多:

  • 它可以「活着但卡住」——进程还在,但LLM调用被限速重试卡了三个小时,半点进度都没推进
  • 它可以「活着但走错路」——任务确实在执行,但每一步都在做错误决策,直到资源耗尽了才崩溃
  • 它可以「静默失败」——工具调用返回了空数组,Agent判断“没有数据”,然后正常退出,可实际上是查询条件写错了

传统的“进程是否存活”检测,对这三种情况几乎全部失效。要真正掌握Agent的健康状态,需要的是语义级别的健康检测。

第一层:心跳 ≠ 进程探活

拿OpenClaw跑Agent的实践来说,它自带heartbeat机制,但最初方向就配错了:

// 错误配置:只检测进程{"heartbeat": {"interval": "30m","check": "process"}}

进程活着真不等于Agent在干活。正确做法是让Agent主动写入心跳时间戳:

// agent/main.js — 每完成一个工作单元就更新async function processTask(task) { await updateHeartbeat({ task_id: task.id, step: 'started', timestamp: Date.now() }); const result = await llm.call(task.prompt); await updateHeartbeat({ task_id: task.id, step: 'llm_done', tokens_used: result.usage.total_tokens, timestamp: Date.now() }); // ... 后续步骤 }

然后需要一个独立的watchdog进程来检查心跳是否超时:

// watchdog.js async function checkHeartbeat() { const lastBeat = await db.get('agent:heartbeat:last'); const age = Date.now() - lastBeat.timestamp; if (age > 10 * 60 * 1000) { // 10 分钟没心跳 await alert.send(`Agent 疑似卡死,上次心跳 ${Math.round(age/60000)} 分钟前,步骤:${lastBeat.step}`); } }

这里的关键点是:watchdog必须是独立的进程,不能和Agent挤在同一个进程里——不然Agent一崩,watchdog也跟着没了。

第二层:状态快照与检查点

Agent执行到一半挂了,这种情况最难处理:重启后不知道跑到哪一步了,从头跑可能会重复操作,不跑又会导致数据丢失。

现在的做法是在每个“不可逆操作”前都写入检查点:

class AgentCheckpoint { constructor(runId, storage) { this.runId = runId; this.storage = storage; // Redis / 本地 SQLite 均可 } async sa ve(step, state) { await this.storage.set(`checkpoint:${this.runId}:${step}`, {step, state, sa ved_at: Date.now()}); console.log(`[checkpoint] sa ved step=${step}`); } async load(step) { return this.storage.get(`checkpoint:${this.runId}:${step}`); } async hasCompleted(step) { const cp = await this.load(step); return cp !== null; } } // 使用 async function runPipeline(runId) { const cp = new AgentCheckpoint(runId, redis); // 步骤 1:拉数据(幂等,可重跑) let rawData; if (await cp.hasCompleted('fetch')) { rawData = (await cp.load('fetch')).state.data; console.log('[resume] skipping fetch, loaded from checkpoint'); } else { rawData = await fetchData(); await cp.sa ve('fetch', { data: rawData }); } // 步骤 2:LLM 处理(有成本,不可随意重跑) let analysis; if (await cp.hasCompleted('analyze')) { analysis = (await cp.load('analyze')).state.result; } else { analysis = await llm.analyze(rawData); await cp.sa ve('analyze', { result: analysis }); } // 步骤 3:写入下游(只跑一次) if (!await cp.hasCompleted('push')) { await pushToDownstream(analysis); await cp.sa ve('push', { pushed_at: Date.now() }); } }

这段代码的核心价值在于:重启后能从上次成功的步骤继续执行,既不会重复调用LLM,也不会重复向下游写入数据。

第三层:语义健康检查

心跳功能只告诉你Agent还在跑,但回答不了“跑得对不对”这个问题。为此,每5分钟跑一次“语义探针”会很有帮助:

async function semanticHealthCheck(agent) { // 发一个有已知答案的探针问题 const PROBE = { input: "2+2等于多少?", expected_pattern: /4/ }; const start = Date.now(); const result = await agent.run(PROBE.input, { timeout: 30_000 }); const latency = Date.now() - start; const metrics = { latency_ms: latency, responded: result !== null, correct: PROBE.expected_pattern.test(result?.output || ''), timestamp: Date.now() }; await metrics.record('agent.health', metrics); if (!metrics.correct) { await alert.critical(`语义健康检查失败:探针问题回答异常,latency=${latency}ms`); } if (latency > 20_000) { await alert.warn(`Agent 响应过慢:${latency}ms`); } return metrics; }

在生产环境中,探针问题可以设计得更复杂——比如“处理一条测试数据,然后验证输出格式是否正确”。核心思路清晰:有输入、有期望输出、机器能自动判断对错。

第四层:故障恢复自动化

前三层解决的是“发现问题”,那发现之后呢?

过去的流程是:收到告警 → 手动SSH → 查日志 → 重启。凌晨3点碰到这种情况,基本不现实。

现在把恢复动作直接写成代码:

class AgentSupervisor { constructor(agentFactory, options = {}) { this.agentFactory = agentFactory; this.maxRestarts = options.maxRestarts ?? 3; this.restartWindow = options.restartWindow ?? 3600_000; // 1h 内最多 N 次 this.restartHistory = []; this.agent = null; } async start(task) { this.agent = await this.agentFactory(); try { return await this.agent.run(task); } catch (err) { return this.handleFailure(err, task); } } async handleFailure(err, task) { const now = Date.now(); this.restartHistory = this.restartHistory.filter(t => now - t < this.restartWindow); if (this.restartHistory.length >= this.maxRestarts) { // 超过重启次数上限,人工介入 await alert.critical(`Agent 在 ${this.restartWindow/60000} 分钟内重启了 ${this.maxRestarts} 次,停止自动恢复,等待人工处理`, { error: err.message, last_checkpoint: await this.getLastCheckpoint() }); throw err; } this.restartHistory.push(now); const delay = Math.min(1000 * 2 ** this.restartHistory.length, 60_000); await alert.warn(`Agent 崩溃,${delay/1000}s 后自动重启(第 ${this.restartHistory.length} 次)`, { error: err.message }); await sleep(delay); // 重启并从检查点恢复 this.agent = await this.agentFactory(); return this.agent.resumeFrom(task, await this.getLastCheckpoint()); } }

一定要设硬上限。自动恢复功能很实用,但无限重启只会掩盖真正的bug,还会白白烧钱——毕竟每次LLM调用都有成本。

现在的监控架构

踩了这三个月的坑,最终打磨出来的Agent监控框架是这样的:

┌─────────────────────────────────────────┐
│               Agent 主进程                │
│  ┌─────────┐  ┌──────────┐  ┌────────┐   │
│  │ 心跳写入 │  │ 检查点存储│  │ 指标上报│   │
│  └────┬────┘  └────┬─────┘  └───┬────┘   │
└───────┼─────────────┼────────────┼───────┘
        │             │            │
        ▼             ▼            ▼
  ┌─────────┐  ┌─────────┐  ┌─────────┐
  │  Redis  │  │ SQLite  │  │ InfluxDB│
  └────┬────┘  └─────────┘  └────┬────┘
       │                         │
       ▼                         ▼
  ┌─────────┐            ┌─────────┐
  │Watchdog │            │ Grafana │
  │(独立进程)│            │(告警规则)│
  └────┬────┘            └────┬────┘
       │                      │
       └──────────┬────────────┘
                  ▼
          ┌──────────┐
          │ 告警通知  │
          │(TG/邮件)  │
          └──────────┘

四层加在一起,从“凌晨挂了到第二天早上才发现”变成了“5分钟内自动告警、30分钟内自动恢复或人工接管”。

踩坑总结

  1. 别用进程活着当健康指标——要用语义心跳
  2. watchdog必须独立于Agent进程——否则Agent一崩,什么都等于零
  3. 每个不可逆操作前都要存检查点——幂等重跑的成本比重来一遍低太多
  4. 自动恢复必须设上限——无限重启等于无限烧钱,还会把真实问题彻底掩盖
  5. 语义探针比日志更早发现问题——日志记录的是“发生了什么”,而探针检测的是“能不能正常工作”

如果你的Agent目前也只有“进程监控”这一层,上面的代码可以直接参考使用。

来源:https://juejin.cn/post/7644466893395689506
上一篇FastMCP实战:30行Python打造AI数据库查询工具 下一篇手把手教你编写自己的Agent技能教程
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
Claude Code 必知的14个高效工作流,让你的开发效率提升300%
AI教程 · 2026-07-09

Claude Code 必知的14个高效工作流,让你的开发效率提升300%

Claude Code 常用工作流 先分享几项核心判断:Claude Code 真正强大的地方,并非仅仅在于它能编写代码——而是它让“编码”这件事本身变得更加高效且可控。你大概率遇到过这类场景:接手一个陌生项目,花了一整天才能理清架构;线上出现报错,翻遍日志也找不到根本原因;想要重构遗留代码,又担心

阿里云通义AIGC平台完全指南:设计师AI生产力革命
AI教程 · 2026-07-09

阿里云通义AIGC平台完全指南:设计师AI生产力革命

一、写在前面:为什么设计师需要关注AIGC? 如果你还在手动一张一张制作海报、反复修改客户口中“感觉不对”的配色方案、为电商详情页准备几十张不同场景的产品图——那么你一定经历过这些痛点: 创意瓶颈:脑海中有画面,但手绘无法呈现 重复劳动:调整尺寸、更换背景、批量生成变体消耗了大量时间精力 成本焦虑:

零基础毕设代码二次开发:3文件定位法及Vue/Java修改对照表
AI教程 · 2026-07-09

零基础毕设代码二次开发:3文件定位法及Vue/Java修改对照表

每年一到毕业季,计算机专业的同学总会陷入一个共同的怪圈:从 GitHub 上扒下来一套代码,或者用 AI 生成一个项目,看起来挺完整的,可导师一句“加个筛选条件”或“换个页面颜色”,瞬间就懵了——不敢改,不会改,怕改崩。是不是很熟悉? 一、为什么AI生成的毕设代码你 "不敢改 "? 1 1 毕业生的三大

反向海淘订单系统:状态机与分布式事务实战设计
AI教程 · 2026-07-09

反向海淘订单系统:状态机与分布式事务实战设计

先分享一个反直觉的结论:反向海淘订单管理的真正挑战,往往不在于业务逻辑本身,而在于状态流转。一个订单的生命周期拉长到跨国运输,中间涉及的环节多、系统多、参与者多,状态稍有错乱就可能引发连锁事故。Taocarts团队在实践中踩了不少坑,最终沉淀下来的这套状态机与分布式事务方案,成功解决了这一复杂难题。

AI并未抢走程序员饭碗而是更新了编程菜单
AI教程 · 2026-07-09

AI并未抢走程序员饭碗而是更新了编程菜单

AI并未大规模替代程序员,而是改变了职业结构。重复性编码岗位需求下降35%至15%,而AIAgent开发等岗位需求激增187%。开发者焦虑从“被替代”转向“跟不上变化”,60%程序员已使用AI辅助编程。人的核心价值转向架构设计、技术决策和审查AI生成代码,AI技能带来16%薪资溢价。