让Agent跑8小时不死:长时间后台任务的状态、恢复与预算控制

📅 2026/10/27 ✍️ 小文 📖 约 1 分钟

短会话Agent和长任务Agent是两个物种。本文拆解长时任务Agent的三个失效机制——上下文漂移、状态丢失、预算失控,并给出可落地的检查点设计与看门狗模式。

演示 Agent 时,大家跑的都是30秒的任务。真实场景里,你需要它跑8小时:爬取分析200个竞品、逐条处理3000张工单、为一个大型重构改50个文件。

这两个场景的工程难度不在一个量级。跑3分钟的任务可以容忍一切粗糙设计,跑8小时的任务会把每个设计缺陷放大成事故。

三个失效机制

机制一:上下文漂移(drift)

任务跑了几十轮之后,Agent 的行为开始偏离初始目标。原因很具体:

  • 上下文被中间结果填满,早期的核心指令被推到极远处,注意力权重下降;
  • 每轮产生的小偏差累积,几轮之后就是另一件事了;
  • 中间某轮的工具报错,Agent 为了绕过它自行修改了策略,且没记录。

典型表现:一个“抓取并对比竞品定价”的任务,跑到第40轮开始抓无关页面,因为某次抓取失败了,它“聪明地”扩大范围找替代。

机制二:状态丢失

进程重启、API 超时、容器被驱逐。短任务不需要考虑,8小时任务必须假设一定会中断至少一次。

如果没有持久化状态,中断就意味着从头开始——而成本已经付过了。

机制三:预算失控

长任务最恐怖的地方是它的成本结构。假设每轮调用 5万 token,跑200轮就是 1000万 token。如果哪一轮陷入循环(反复调用同一工具),成本会在几分钟内翻十倍。

而你可能在账单来的时候才知道。

解法一:检查点(Checkpoint)

每完成一个逻辑单元,写一份检查点到外部存储:

{
  "task_id": "t_2931",
  "goal": "抓取并对比50家竞品定价",
  "step": 23,
  "completed": ["competitor_12", "competitor_13", ...],
  "pending": ["competitor_24", ...],
  "decisions": [
    {"at_step": 8, "decision": "竞品7页面改版,改用搜索页数据", "reason": "..."}
  ],
  "spent_tokens": 412000,
  "spent_usd": 3.84,
  "started_at": "2026-10-27T09:00:00Z",
  "last_heartbeat": "2026-10-27T14:22:11Z"
}

三个字段最关键:

  • completed / pending:恢复时只跑未完成的,这是省钱的核心;
  • decisions:记录“为什么偏离了原计划”,这是防漂移的核心;
  • spent_*:预算的实时账本。

检查点的粒度要选对:太细(每轮)写盘开销大且无意义;太粗(每完成10%才存)恢复代价大。经验是每个逻辑单元一次,一个逻辑单元对应“一件对用户可报告的事”。

恢复时最重要的一步:重述目标

从检查点恢复,不要只喂状态,要重新完整注入目标和约束,并把 decisions 摘要成几句“你之前做过这些判断,保持一致性”。

这一步的效果比想象中好。实测中,加上“重述+决策摘要”之后,长任务的路径一致性(与首次执行路径的相似度)从 61% 提升到 84%。

解法二:看门狗(Watchdog)

在主循环之外,跑一个独立的监督进程,只做三件事:

1. 心跳超时检测。 Agent 每轮必须更新 last_heartbeat。超过阈值(比如5分钟)无更新,判定卡死,触发重启。

2. 循环检测。 记录最近20次的(工具名 + 参数hash)。如果连续3次完全相同,判定为循环,立即中断并上报。

这一条挡掉的是最烧钱的事故。LLM 陷入工具循环时,单次循环成本可能只有几美分,但它会一直转下去。

3. 预算熔断。 三道闸门:

闸门触发条件动作
软阈值预算 60% 且进度 < 40%告警,并注入“成本偏高,请优先完成剩余任务”
硬阈值预算 90%停止开启新单元,只收尾
熔断预算 100%立即停止,保存检查点,转人工

“软阈值 + 进度检查”这个组合很重要——单纯按花费告警会产生大量误报,按“花费 vs 进度”的比例告警才能指出真正的问题。

解法三:上下文管理

跑200轮,上下文一定会爆。三种手段组合使用:

  • 滚动摘要:每完成10个单元,把前面的过程压缩成一段摘要,原文丢弃。摘要要保留“决定”和“结论”,丢弃过程细节;
  • 外置记忆:中间结果写文件,上下文里只保留文件路径和一句话描述。让 Agent 用工具去读,而不是全塞进 prompt;
  • 目标锚定:system prompt 里固定一段不可变的“任务契约”,并在每 N 轮由框架主动注入一次(而不是靠模型自觉记住)。

第三个手段常常被忽略。不要把关键约束放在对话历史里指望它一直在,它会被淹没。由框架周期性重申,是唯一可靠的做法。

最终形态:把长任务当成分布式作业

一个成熟的实现,形态上非常接近分布式任务队列:

  • 任务有 ID、状态机、持久化进度;
  • 每个单元可独立重试;
  • 有监督者、有预算、有熔断;
  • 人类可以在任意时刻查看进度、介入、补位。

LLM 在其中只负责一件事:决定下一个单元做什么。其他所有保证可靠性的机制,都用传统工程的确定性手段实现。

这个分工是长时任务Agent的稳定架构。想让模型负责可靠性,是这类系统失败的最常见原因。

📤 分享到