会话管理与压缩(深度解析)
本文解释 OpenClaw 端到端如何管理会话:- 会话路由(入站消息如何映射到
sessionKey) - 会话存储(
sessions.json)及其跟踪内容 - 转录持久化(
*.jsonl)及结构 - 转录清理(运行前的 provider 特定修正)
- 上下文限制(上下文窗口 vs 追踪 token)
- 压缩(手动 + 自动压缩)及压缩前工作挂钩点
- 静默清理(例如不应产生用户可见输出的记忆写入)
事实来源:Gateway
OpenClaw 围绕单一 Gateway 进程 设计,它拥有会话状态。- UI(macOS app、web Control UI、TUI)应通过 Gateway 查询会话列表与 token 计数。
- 远程模式下,会话文件在远程主机;“查看本地 Mac 文件”不会反映 Gateway 实际使用的内容。
两层持久化
OpenClaw 使用两层持久化会话:-
会话存储(
sessions.json)- 键/值映射:
sessionKey -> SessionEntry - 体积小、可变、可安全编辑(或删除条目)
- 跟踪会话元数据(当前 session id、最近活动、开关、token 计数等)
- 键/值映射:
-
转录(
<sessionId>.jsonl)- 追加式转录,树结构(条目含
id+parentId) - 存储实际对话 + 工具调用 + 压缩摘要
- 用于重建后续回合的模型上下文
- 追加式转录,树结构(条目含
磁盘位置
每个 agent 在 Gateway 主机上:- 存储:
~/.openclaw/agents/<agentId>/sessions/sessions.json - 转录:
~/.openclaw/agents/<agentId>/sessions/<sessionId>.jsonl- Telegram topic 会话:
.../<sessionId>-topic-<threadId>.jsonl
- Telegram topic 会话:
src/config/sessions.ts 解析这些路径。
会话键(sessionKey)
sessionKey 标识你处于哪个“会话桶”(路由 + 隔离)。
常见模式:
- 主/直聊(按 agent):
agent:<agentId>:<mainKey>(默认main) - 群聊:
agent:<agentId>:<channel>:group:<id> - 房间/频道(Discord/Slack):
agent:<agentId>:<channel>:channel:<id>或...:room:<id> - Cron:
cron:<job.id> - Webhook:
hook:<uuid>(除非被覆盖)
会话 id(sessionId)
每个 sessionKey 指向当前 sessionId(继续对话的转录文件)。
经验规则:
- Reset(
/new、/reset)会为该sessionKey创建新的sessionId。 - 日重置(默认 Gateway 主机本地时间凌晨 4:00)会在重置边界后的下一条消息创建新的
sessionId。 - 空闲过期(
session.reset.idleMinutes或旧的session.idleMinutes)会在空闲窗口后收到消息时创建新sessionId。若同时设置日重置与空闲过期,以先到者为准。
src/auto-reply/reply/session.ts 的 initSessionState()。
会话存储 schema(sessions.json)
存储值类型为 src/config/sessions.ts 中的 SessionEntry。
关键字段(非穷举):
sessionId:当前转录 id(若未设置sessionFile,文件名由此派生)updatedAt:最近活动时间戳sessionFile:可选的转录路径覆盖chatType:direct | group | room(用于 UI 与发送策略)provider、subject、room、space、displayName:用于群/频道标签的元数据- 开关:
thinkingLevel、verboseLevel、reasoningLevel、elevatedLevelsendPolicy(按会话覆盖)
- 模型选择:
providerOverride、modelOverride、authProfileOverride
- Token 计数(尽力而为 / 依赖 provider):
inputTokens、outputTokens、totalTokens、contextTokens
compactionCount:该 session key 完成自动压缩的次数memoryFlushAt:上次压缩前记忆写入的时间戳memoryFlushCompactionCount:上次写入发生时的 compaction 计数
转录结构(*.jsonl)
转录由 @mariozechner/pi-coding-agent 的 SessionManager 管理。
文件为 JSONL:
- 第一行:会话头(
type: "session",包含id、cwd、timestamp、可选parentSession) - 之后:带
id+parentId的会话条目(树结构)
message:用户/助手/toolResult 消息custom_message:扩展注入的消息,会 进入模型上下文(可在 UI 中隐藏)custom:扩展状态,不会 进入模型上下文compaction:持久化压缩摘要,包含firstKeptEntryId与tokensBeforebranch_summary:在树分支导航时持久化的摘要
SessionManager 读写它们。
上下文窗口 vs 追踪 token
有两个概念需要区分:- 模型上下文窗口:每个模型的硬上限(模型可见 token 数)
- 会话存储计数器:写入
sessions.json的滚动统计(用于 /status 与 dashboard)
- 上下文窗口来自模型目录(可在配置中覆盖)。
- 存储中的
contextTokens是运行时估算/报告值,不应视为严格保证。
压缩:它是什么
压缩会将较早对话摘要为转录中的compaction 条目,并保留近期消息。
压缩后,后续回合可见:
- 压缩摘要
firstKeptEntryId之后的消息
自动压缩何时发生(Pi runtime)
在内置 Pi agent 中,自动压缩在两种情况下触发:- 溢出恢复:模型返回上下文溢出错误 → 压缩 → 重试。
- 阈值维护:成功回合后,当:
contextTokens > contextWindow - reserveTokens
其中:
contextWindow为模型上下文窗口reserveTokens为提示 + 下一次模型输出预留的头部空间
压缩设置(reserveTokens、keepRecentTokens)
Pi 的压缩设置位于 Pi settings:
- 若
compaction.reserveTokens < reserveTokensFloor,OpenClaw 会提高它。 - 默认下限为
20000tokens。 - 设置
agents.defaults.compaction.reserveTokensFloor: 0可禁用该下限。 - 若已高于下限,OpenClaw 不会修改。
src/agents/pi-settings.ts 中的 ensurePiCompactionReserveTokens()
(由 src/agents/pi-embedded-runner.ts 调用)。
用户可见界面
你可以通过以下方式观察压缩与会话状态:/status(任意聊天会话)openclaw status(CLI)openclaw sessions/sessions --json- 详细模式:
🧹 Auto-compaction complete+ compaction 计数
静默清理(NO_REPLY)
OpenClaw 支持“静默”回合,用于不应让用户看到中间输出的后台任务。
约定:
- 助手输出以
NO_REPLY开头,表示“不要向用户发送回复”。 - OpenClaw 在发送层会剥离/抑制该内容。
2026.1.10 起,OpenClaw 也会在 草稿/typing 流 中抑制以 NO_REPLY 开头的部分输出,避免静默操作在回合中泄漏。
压缩前“记忆写入”(已实现)
目标:在自动压缩发生前运行一个静默的代理回合,将持久状态写入磁盘 (例如 agent 工作区的memory/YYYY-MM-DD.md),避免压缩抹掉关键上下文。
OpenClaw 使用 阈值前写入 策略:
- 监控会话上下文用量。
- 当超过“软阈值”(低于 Pi 的压缩阈值)时,运行静默的“现在写入记忆”指令。
- 使用
NO_REPLY,让用户不可见。
agents.defaults.compaction.memoryFlush):
enabled(默认:true)softThresholdTokens(默认:4000)prompt(写入回合的用户消息)systemPrompt(写入回合追加的系统提示)
- 默认 prompt/system prompt 中包含
NO_REPLY提示,以抑制发送。 - 每个压缩周期只运行一次写入(在
sessions.json中跟踪)。 - 仅对内置 Pi 会话运行(CLI backend 会跳过)。
- 当会话工作区只读时会跳过(
workspaceAccess: "ro"或"none")。 - 工作区文件布局与写入模式参见 Memory。
session_before_compact 钩子,但 OpenClaw 目前的写入逻辑在 Gateway 侧。
故障排查清单
- session key 错误?从 /concepts/session 入手,并在
/status中确认sessionKey。 - 存储 vs 转录不一致?确认 Gateway 主机与
openclaw status中的存储路径。 - 压缩过于频繁?检查:
- 模型上下文窗口(是否过小)
- 压缩设置(
reserveTokens过高会导致提前压缩) - 工具结果膨胀:启用/调整会话修剪
- 静默回合泄漏?确认回复以
NO_REPLY(精确 token)开头,且版本包含流式抑制修复。