OpenClaw 网关重启后会话还在不在?哪些状态能自动恢复、哪些直接消失

2026-08-17

改完配置要重启网关,手里那个 Agent 正跑到一半——大多数人这时会犹豫一下:重启完它是接着干,还是从头再来一遍,或者干脆当没发生过?

这个犹豫有道理。Agent 的回合不是纯计算,中间可能已经发过消息、动过文件。重放一次和丢掉一次,是两种不同的事故。

OpenClaw 官方文档有一页专门讲这件事(Restart recovery),口径很明确:重启不会丢 Agent 状态,恢复机制默认常开,通常不需要人工介入。但”通常”之外还有一串条件——什么算被中断、怎么标记、续跑几次之后放弃、放弃之后你要做什么。这篇就把这几层按文档拆开讲。

本文只依据 OpenClaw 官方文档 docs/gateway/restart-recovery.md 的内容整理,核对日 2026-08-17。我们没有部署运行过 OpenClaw,文中不涉及任何实测数字与界面描述,机制以官方文档为准。

一、重启之后哪些东西还在

文档给了一张状态存活表,这是判断一切的地基,译为中文如下:

状态存储位置跨重启的行为
对话历史每个 Agent 各自的 SQLite 数据库不受影响,会话从已存的 transcript 继续
被中断的主会话回合每 Agent SQLite 的会话行与 transcript启动后数秒内自动恢复或对账
子代理(subagent)运行SQLite(共享状态库)开机重建注册表,被中断的运行恢复
后台任务SQLite(共享状态库)开机对账,孤儿运行被恢复或标记为丢失
待发送的出站投递SQLite 投递队列重启后排空,未送达的回复重试
定时(cron)作业SQLite cron 存储计划保留,调度器开机重新装载
重启续跑SQLite restart sentinel向请求重启的那个会话派发一次性后续回合
网关终端 PTY进程内存随旧进程结束,终端会话不恢复

读这张表有个抓手:凡是落在 SQLite 的都能活,凡是只在进程内存里的都活不了。终端 PTY 是唯一明确写死”不恢复”的一类。

待投递的记录在重启后排空或重试。文档补了一句关键的:失败的记录会丢弃自己的载荷,只有可复用或崩溃语义不明确的持有方,才保留一份最小的、有界或永久的回执,用来阻止重复投递。这是整页的设计基调——宁可留一条回执占地方,也不冒重复发一次的风险

二、主动重启先排空,多数情况根本没中断

很多人担心的”回合被腰斩”,在正常路径下其实不太会发生。文档写明:一次被请求的重启(openclaw gateway restart、需要重启的配置变更、或者网关更新)不会立刻杀掉在途工作。网关会先停止接收新工作,然后等待活跃的 Agent 回合与后台任务跑完,等待有一个排空预算,默认 5 分钟

所以绝大多数重启什么都没打断。真正会被中止的只有两类——排空预算内跑不完的,以及被强制重启或崩溃打断的。而且在中止之前,每个受影响的会话都会先被打上恢复标记。

三、“没跑完”是怎么被认出来的

三个互补机制负责标记未完成的回合,落在三个时间点。

回合准入时。 对既有主会话上的普通文本回合,网关会在一个 SQLite 事务里完成三件事:追加用户消息、把会话标记为 running、记录它的恢复投递声明——这三件事发生在模型调用和 before_agent_reply 钩子执行之前。命令、附件、单回合覆盖、待投递、之前的中止提示、插件持有的会话、带执行钩子的回合,各自走专门的准入路径。

装了 before_agent_reply 钩子的话,准入阶段还会多记一层阶段状态,用来区分”已经完成的静默结果”和”副作用窗口不明确”。先前钩子结果不明确的,会以受限的 restart-safe 工具集恢复,而不是把不受限的副作用重放一遍。

关停时。 重启排空过程中,每个还有活跃运行的会话,都会在运行被中止前,在会话存储里盖上恢复标记。

启动时。 网关扫描会话存储,找出仍然自称 running、但在新进程里没有活着的持有方的会话。这一条专门兜住硬崩溃和被 kill 的场景——那时根本没有关停代码跑过。同时还会清理陈旧的 transcript 锁文件。

三个时机叠起来,覆盖了”优雅重启""强杀""崩溃”三种退出方式。要理解这些标记落在会话的哪个层面,可以对照会话模型:主会话、附着与状态一起看。

四、自动续跑:合成系统消息、三次预算、墓碑

启动后数秒,网关把每个被标记的会话重新派发一次,带上一条合成的系统消息,告诉 Agent:你上一个回合被重启打断了,请从现有 transcript 继续。

如果最终回复其实已经生成、只是没送出去,那段文本会一并塞进去,让 Agent 直接投递而不是重做。

重试的额度分两层,别混了:

额度特点
启动对账重试瞬时失败最多重试 3 次指数退避
主会话续跑预算每个被中断周期 3 次「计费」派发跨网关重启保留

“计费”这个说法来自文档本身:派发前先扣一次;网关在受理前明确拒绝请求,就退回这一次;派发之后结果不确定,就保留扣除——目的是避免把工作重放一遍。另外,已经有前台工作持有该会话时,自动恢复会一直让路,直到那份工作了结。

预算耗尽之后,会话会被打上墓碑(tombstone),而不是无限循环重试。 这时要人工介入:检查失败的会话,用 /new/reset 起一个替代会话。openclaw doctor --fix 能修掉与墓碑冲突的陈旧 aborted 标志,但它不会重新启用那个恢复周期——别指望 doctor 把额度还给你。doctor 输出怎么对号入座,见doctor 体检输出怎么读

去重这块做了两道保险。第一道,每次重试复用同一个持久派发标识符,所以一次语义不明的连接失败没法把同一个恢复启动两次。

第二道是给”只用消息工具回复”的回合准备的:终态的同会话发送抵达渠道之前,网关会在确切的会话与来源回合上记录一条未解决的投递意图,供应商确认成功就落成持久的已投递回执,确认失败就清掉。恢复时补完一条回执不需要重跑工具;而如果崩溃导致供应商结果未知,恢复会以 restart-safe 工具集继续,让模型去检查并报告这个不确定性,而不是把外部副作用重放。

还有一条硬规则:只有持久的渠道入口声明才能恢复消息动作授权。一个只用消息工具、又无法重建渠道授权的回合会被直接墓碑化,终态通知引导用户用 /new/reset 开替代会话。

恢复之前,网关还会先给 transcript 的尾部分类,据此决定续跑时用哪一档工具限制:已经流式输出的部分文本留在 transcript 里,续跑从它下面那条消息接着走;而悬空的工具调用会从下一次给供应商的载荷里剔除,并限制到 restart-safe 工具,除非它经审计确认可安全重放。

五、子代理与后台任务归谁管

这两类不走主会话恢复那条路。子代理运行持久化在共享 SQLite 状态库里,开机重建注册表,被中断的子代理会话带着原始任务上下文恢复。两个安全阀:

  • 中断超过 2 小时的运行会被终结而不是恢复,免得通宵宕机的网关醒来后把过期活儿全刨出来重做;
  • 反复恢复失败的会话被标记为 wedged 并墓碑化,防止恢复逻辑自己死循环。

后台任务注册表同样由 SQLite 支撑,开机对账一次,之后按周期对账:已完成运行记下的持久结果会被回收,持有进程消失了的运行则在宽限期之后标记为 lost,而不是一直挂着。

六、Agent 自己要求的重启

还有一类:重启是 Agent 自己触发的——应用配置变更、更新网关,或者显式请求重启。

这时进程退出前会往 SQLite 写一个 restart sentinel。开机后网关把结果回帖到最初那个聊天里,并派发一次性的续跑回合,让 Agent 接着刚才的位置往下做,渠道和会话线程都保持原样

实现细节文档也交代了:以 SQLite 的类型化列为准,payload_json 只是重放/调试用的影子,运行时读写和清除都走 SQLite,没有文件回退。存储切换期间,启动时和 Doctor 里会跑一个有界的状态迁移,把旧进程更新后留下的、通过校验的 restart-sentinel.json 保住。

七、崩溃循环断路器:网关活着,渠道却不上线

这是整页里最值得运维记住的一条,因为症状很容易被误判。5 分钟内 3 次非正常启动会触发断路器,下一次启动时抑制自动启动的旁路服务,避免一个正在崩的网关把自己放大。断路器触发之后,控制面照常启动,但渠道插件(以及其它自动启动的旁路服务)起不来,直到运维手动覆盖抑制,或者整个窗口干净地排空。

所以”渠道断了”这个症状,很可能不是网关死了,而是自动启动被抑制了。日志形如:

channel autostart suppressed by crash-loop breaker; refusing automatic
start for <channel>… Start a channel manually with: openclaw gateway call
channels.start --params '{"channel":"<id>"}'

文档给了一套运维恢复 SOP,按顺序是:

  1. 确认网关进程还在(openclaw gateway status,或看 LaunchAgent / systemd 单元);

  2. 查渠道状态 openclaw channels status(必要时加 --probe),找网关健康但账号处于 stopped / not connected 的;

  3. 先修非正常启动的根因(坏配置、插件启动时崩、缺密钥),再把渠道拉起来;

  4. 抑制期间手动起一个渠道:

    openclaw gateway call channels.start --params '{"channel":"<id>"}'
    # 可选:{"channel":"<id>","accountId":"<account>"}
    

    channels.start手动覆盖,它不会替其它渠道解除断路器。

  5. 或者让健康的网关跑着,等非正常启动窗口排空。同一个进程会打日志说断路器已恢复,并启动被推迟的已配置渠道,不需要再重启一次网关。窗口时间加一个健康监控周期之后这条消息还没出现,就去看日志、跑 openclaw doctor

网关起不来或渠道连不上的整体排查思路,配合网关起不来:锁文件、端口与后台进程一起排。

八、主机休眠与进程冻结

不只是重启,休眠也算一种打断。

主机从睡眠中唤醒、虚拟机恢复、或者进程在长时间停顿后继续时,网关会在大约 30 秒内检测到这次冻结。它会重启渠道连接,刷新缓存的健康与在线状态,免得客户端干等陈旧的 socket 或快照过期。

macOS 应用和 Linux 伴随程序会跟本地网关配合:主机睡眠前先准备一份短期挂起租约,唤醒后恢复。远程网关不会因为应用所在主机睡眠而被挂起。 通过 gateway.suspend.* 做的主动挂起,则会让恢复推迟到控制方把网关恢复为止。

九、可观测性:查恢复到底发生没发生

出问题时你需要证据,文档给了两处抓手:

  • 指标:恢复活动通过 Prometheus 导出,指标名是 openclaw_session_recovery_totalopenclaw_session_recovery_age_seconds
  • 日志:恢复决策记在 main-session-restart-recoverysubagent-interrupted-resume 两个子系统下。

另外,自动投递的回复会在渠道投递之前跑正常的 reply_payload_sending 钩子,并带上恢复出来的上下文。

十、什么时候不适用,以及还有哪些没解决

文档明确排除的几类,不走主会话恢复

  • 已经有别的持有方在管的会话——子代理会话(走子代理恢复)、cron 会话(调度器按计划重跑)、ACP 托管的会话(由连接的 IDE 或客户端负责恢复);
  • 从未被准入的工作:排空窗口里到达的消息直接以重启错误拒绝;
  • 网关终端 PTY,包括运维和 Agent 各自持有的终端,它们是进程本地的,网关一重启就结束;
  • 独立的嵌入式回合没法接管一个还挂着重启恢复的主会话,因为它们不共享网关的生命周期持有方——要么把回合走网关跑,要么在网关那边用 /new/reset 重置。

再说这套机制解决不了的:

它保证的是”不丢、不乱重放”,不是”一定跑完”。 三次续跑预算耗尽就是墓碑,剩下的活得你自己判断。

它也不替你判断副作用。 副作用不明确的场景一律降到 restart-safe 工具,把”到底完成了什么、还剩什么”交回给模型判断并报告。所以恢复之后你可能会收到一句”我不确定刚才那步做完没有”——这不是故障,是设计。

断路器会掩盖症状。 一旦触发,表现出来的是渠道断连而不是网关挂掉。把 openclaw gateway statusopenclaw channels status 一起看,是区分二者最快的办法;健康检查本身还有 health 与 heartbeat 两套口径,区别见health 与 heartbeat 两套健康检查的区别

至于恢复在真实负载下的耗时、排空预算是否可调、墓碑会话保留多久,官方文档未说明,别照着别处的经验去猜。

延伸阅读


本文依据 OpenClaw 官方仓库(github.com/openclaw/openclawdocs/ 下的官方文档整理,核对日 2026-08-17。 我们没有安装或运行过 OpenClaw,因此不涉及界面外观、操作手感与实测耗时的任何描述; 文中的默认值、命令与配置项均为文档口径,不构成对实际运行结果的保证。 该项目迭代很快,请以仓库最新内容为准。接入即时通讯平台前,请自行确认所在平台的规则与合规要求。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。