今天在使用 OpenClaw 的过程中遇到一个现象:正在进行的会话突然“失忆”了,助手对不久前做过的操作完全不知情。经过日志追溯和源码分析,弄清了整个链路。记录如下。
现象
一次完整的会话链涉及了 3 个关联的 session:
| 时间 | Session | 内容 | 结果 |
|---|---|---|---|
| 20:25 | 639ede17 | Agent宕机诊断 → 发布博客 → iNove主题排版调整 | 被 abort |
| 20:32 | 6520c2c3 | 继续iNove主题调整,找CSS文件 | 被 abort |
| 20:38 | 7f1f9d61 | 当前会话,全新开始,对之前一无所知 | 正常运行 |
两个前序 session 在 abort 后被 reset,新 session 从零开始,对过去 13 分钟内的所有操作完全没有记忆。
直接原因:手抖多打了一个 0
在 openclaw.json 的模型配置中,deepseek-v4-flash 的 maxTokens 值被设为了 3840000:
{
"id": "deepseek-v4-flash",
"name": "deepseek-v4-flash",
"contextWindow": 1000000,
"maxTokens": 3840000, ← 多了一个 0
"compat": { "supportsTools": false }
}
这个值应该是 393216(火山引擎 deepseek-v4 实际支持的 max_completion_tokens 上限),但配置时多输入了一个 0,变成了 384 万。OpenClaw 的计算逻辑是:
max_completion_tokens = min(maxTokens, contextWindow - 已用token数)
当上下文膨胀后,contextWindow - 已用token数 约 90 万,min(3840000, 90万) 取了 90 万,远超火山引擎 393216 的上限,直接 400 拒绝:
400 The parameter `max_completion_tokens` specified in the request are not valid: expected a value <= 393216, but got 966158 instead.
也就是说,整条调用链 OpenClaw → handai(newapi) → 火山引擎 本身没有兼容性问题,根因就是配置错误。
链式崩溃经过
第 1 轮:连番 400 错误 + 大量重试
用户在 webchat 上发送了 Agent 宕机诊断请求,模型连续返回 400 format error。每次 failover(handai→deepseek→alibaba)都持续失败——因为模型提供商本身没问题,是请求参数超标了。用户因看不到响应,在 webchat 上反复发送同一条消息 30 多次,每一次都触发新的 400 错误,形成恶性循环。
最终用户手动 abort 当前请求,但 abort 的回收超时了:
embedded abort settle timed out: timeoutMs=2000
同时 session 文件已被修改,触发保护机制:
session file changed while embedded prompt lock was released
结果:session 639ede17 被标记为 .reset.,视为被接管放弃。
第 2 轮:CSS 文件查找也 abort
新 session 启动后,用户继续讨论 iNove 主题的 CSS 调整。助手在 /var/www/、/www/ 等路径远程查找 CSS 文件,全部失败(路径不存在)。用户再次 abort,同样的 chat.abort + settle timeout → session 6520c2c3 也被 reset。
OpenClaw 的 Session Reset 机制
从日志可以看出 OpenClaw 在这类场景下的行为:
- 模型 400 错误 → 触发 failover(handai → alibaba → deepseek),但 failover 同样失败(请求参数依然超标)
- 用户手动 abort → 调用
chat.abortWebSocket 接口 - Abort settle timeout(2秒) → 如果在 2 秒内 abort 不能被嵌入层清理完成,触发 session takeover
- Session takeover → 旧的 session 文件被重命名为
.reset.,新请求从新 session 开始 - 失忆 → 新 session 没有历史消息,模型看到的是一块空白画布
如何重建记忆
当前这个 session 恢复“记忆”的方式并非真正记住了,而是通过以下步骤从文件系统中重新构建了上下文:
- 搜 session 文件:用
grep -rl "retailsolution|xmlrpc"在所有 session 文件中找到相关记录 - 读 trajectory 日志:从 reset 过的 session 的
.trajectory.jsonl中提取助手回复文本和用户消息 - 找残留证据:
/tmp/publish_xmlrpc.py等脚本还躺在文件系统上 - 翻日志确认崩溃原因:
/tmp/openclaw/openclaw-YYYY-MM-DD.log中的 failover 和 abort 记录
这种方式的问题很明显:它依赖助手在运行时主动去“考古”,且只能找到轨迹日志中残留的信息,并非系统级的内存持久化。
启示
这次事件揭示了几个值得注意的问题:
- 配置错误是最隐蔽的故障源:多输入一个 0,就能让整套系统反复崩溃。模型配置中的
maxTokens要与最终提供商的实际支持上限对齐,不能写理论最大值。 - Abort 回收的可靠窗口:2 秒的 settle timeout 在复杂场景下偏短,一旦超时就导致整个 session 上下文丢失。这是 OpenClaw 的一个设计缺陷(Issue #98156)。
- 长期记忆 vs 会话记忆:OpenClaw 的 memory 系统保存的是长期语义记忆,但会话级的工作记忆在 session reset 后彻底消失。即使这次是配置错误触发的,其他场景(网络波动、模型超时等)同样会触发 session takeover,导致同样的失忆问题。