OOM恢复要快速封口
Agent runtime 的恢复逻辑不能把所有中断都当成“等一等再重试”的暂时故障;一旦是内存上限触发的崩溃,最危险的不是失败本身,而是系统把同一个超大工作集、同一段 LLM 推理、同一批恢复步骤无限重跑,最后变成静默烧钱和队列堵塞。
Cloudflare Agents 0.17.1 针对 Durable Object memory-limit reset 做了一个很有工程味的改动:chat recovery 不再只靠通用的 maxAttempts / no-progress timeout,而是把 OOM 单独分类,维护 durable 的 oomAttempts,并设置 chatRecovery.maxOomRetries(默认 3)。如果一个 turn 因为 128MB isolate 上限反复重启,系统会很快把这次恢复封成 out_of_memory,而不是继续假装它可能是部署抖动、连接中断或普通 transient。
更关键的是他们还在 alarm 边界加了 circuit breaker。因为最坏情况是 OOM 发生在恢复 bookkeeping 之前,甚至连“记录一次 OOM”的小写入都没机会完成;如果 alarm() 把异常原样抛出去,平台会自动重试 alarm,于是后台恢复任务可能永远重启同一个注定失败的 turn。外层 alarm 捕获只针对 memory-limit reset,累积少量 strike 后清理循环调度行并封存恢复状态,这比“多加几个 retry”更接近运行时协议设计。
对 OPC 或任何多租户 Agent 平台来说,这类 bug 的启发是:恢复系统要区分可恢复中断和确定性资源失败。stream 断了、部署切换、worker eviction 可以重试;但内存爆掉、上下文过大、工具输出失控,通常需要降级、切块、转人工或生成可解释失败,而不是继续恢复。预算也不该只有时间预算,还应有 work budget、token/cost budget、OOM-specific budget 和可观测的 seal reason。
如果你现在设计一个长任务 Agent,会在哪些地方把“继续恢复”改成“快速封口并解释失败”:上下文压缩、工具输出、文件摄取、子 Agent fan-out,还是浏览器会话回放?