Skip to content

Session 管理与会话压缩(深度解析)

本文档端到端说明 OpenClaw 如何管理会话:

  • Session 路由(入站消息如何映射到 sessionKey
  • Session 存储sessions.json)及其追踪内容
  • Transcript 持久化*.jsonl)及其结构
  • Transcript 清理(运行前针对特定提供商的修正)
  • 上下文限制(上下文窗口 vs 已追踪 token 数)
  • 压缩机制(手动 + 自动压缩),以及如何在压缩前挂载预处理工作
  • 静默后台任务(如不应产生用户可见输出的内存写入)

如需先了解高层概览,请参阅:


权威数据源:Gateway

OpenClaw 围绕单一 Gateway 进程设计,由它持有 session 状态。

  • UI 端(macOS 应用、Web 控制台、TUI)应通过 Gateway 查询 session 列表和 token 计数。
  • 在远程模式下,session 文件存在于远程主机上;"看本地 Mac 文件"无法反映 Gateway 实际使用的状态。

两层持久化

OpenClaw 通过两层结构持久化 session:

  1. Session 存储(sessions.json

    • 键值映射:sessionKey -> SessionEntry
    • 体积小、可变更,可以安全编辑(或删除条目)
    • 追踪 session 元数据(当前 session id、最后活动时间、开关状态、token 计数器等)
  2. Transcript(<sessionId>.jsonl

    • 仅追加的 transcript,具有树形结构(条目包含 id + parentId
    • 存储实际对话 + 工具调用 + 压缩摘要
    • 用于重建未来轮次的模型上下文

磁盘存储位置

每个 Agent,存于 Gateway 主机:

  • 存储:~/.openclaw/agents/<agentId>/sessions/sessions.json
  • Transcripts:~/.openclaw/agents/<agentId>/sessions/<sessionId>.jsonl
    • Telegram 话题 session:.../<sessionId>-topic-<threadId>.jsonl

OpenClaw 通过 src/config/sessions.ts 解析上述路径。


存储维护与磁盘控制

Session 持久化具有自动维护控制(session.maintenance),覆盖 sessions.json 和 transcript 文件:

  • modewarn(默认)或 enforce
  • pruneAfter:旧条目年龄阈值(默认 30d
  • maxEntriessessions.json 条目上限(默认 500
  • rotateBytessessions.json 过大时触发轮换(默认 10mb
  • resetArchiveRetention*.reset.<timestamp> transcript 归档文件的保留期(默认与 pruneAfter 相同;false 禁用清理)
  • maxDiskBytes:可选的 sessions 目录总容量上限
  • highWaterBytes:清理后的目标用量(默认为 maxDiskBytes80%

磁盘预算清理的执行顺序(mode: "enforce"):

  1. 首先删除最旧的归档或孤立 transcript 文件。
  2. 若仍超出目标,驱逐最旧的 session 条目及其 transcript 文件。
  3. 持续清理直到用量降至 highWaterBytes 以下。

mode: "warn" 下,OpenClaw 会报告可能的驱逐操作,但不修改存储/文件。

手动触发维护:

bash
openclaw sessions cleanup --dry-run
openclaw sessions cleanup --enforce

Cron Session 与运行日志

独立 cron 运行也会创建 session 条目/transcripts,并拥有专属保留控制:

  • cron.sessionRetention(默认 24h):从 session 存储中清理旧的独立 cron 运行 session(false 禁用)
  • cron.runLog.maxBytes + cron.runLog.keepLines:清理 ~/.openclaw/cron/runs/<jobId>.jsonl 文件(默认:2_000_000 字节和 2000 行)

Session 键(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>(除非被覆盖)

权威规则文档见 /openclaw/concepts/session


Session ID(sessionId

每个 sessionKey 指向一个当前的 sessionId(继续对话的 transcript 文件)。

基本规则:

  • 重置/new/reset):为该 sessionKey 创建新的 sessionId
  • 每日重置(默认 Gateway 主机本地时间凌晨 4:00):在重置边界后收到下一条消息时创建新的 sessionId
  • 空闲过期session.reset.idleMinutes 或旧版 session.idleMinutes):在空闲窗口后收到消息时创建新的 sessionId。若每日重置和空闲过期同时配置,先到期者优先。
  • 线程父分叉保护session.parentForkMaxTokens,默认 100000):当父 session 已经太大时,跳过父 transcript 的分叉;新线程从空白开始。设置为 0 可禁用。

实现细节:决策发生在 src/auto-reply/reply/session.tsinitSessionState() 中。


Session 存储 Schema(sessions.json

存储的值类型为 src/config/sessions.ts 中的 SessionEntry

关键字段(不完整):

  • sessionId:当前 transcript id(文件名由此派生,除非设置了 sessionFile
  • updatedAt:最后活动时间戳
  • sessionFile:可选的显式 transcript 路径覆盖
  • chatTypedirect | group | room(帮助 UI 和发送策略)
  • providersubjectroomspacedisplayName:群组/频道标签元数据
  • 开关:
    • thinkingLevelverboseLevelreasoningLevelelevatedLevel
    • sendPolicy(每 session 覆盖)
  • 模型选择:
    • providerOverridemodelOverrideauthProfileOverride
  • Token 计数器(尽力估算 / 取决于提供商):
    • inputTokensoutputTokenstotalTokenscontextTokens
  • compactionCount:该 session key 完成自动压缩的次数
  • memoryFlushAt:上次 pre-compaction 内存刷新的时间戳
  • memoryFlushCompactionCount:上次刷新时的压缩计数

存储可以安全编辑,但 Gateway 是最终权威:它可能在 session 运行时重写或重新填充条目。


Transcript 结构(*.jsonl

Transcripts 由 @mariozechner/pi-coding-agentSessionManager 管理。

文件为 JSONL 格式:

  • 第一行:session 头部(type: "session",包含 idcwdtimestamp,可选 parentSession
  • 之后:包含 id + parentId 的 session 条目(树形结构)

主要条目类型:

  • message:用户/助手/工具结果消息
  • custom_message:扩展注入的消息,_会_进入模型上下文(可对 UI 隐藏)
  • custom:不进入模型上下文的扩展状态
  • compaction:持久化的压缩摘要,包含 firstKeptEntryIdtokensBefore
  • branch_summary:导航树形分支时持久化的摘要

OpenClaw 刻意修正 transcript;Gateway 使用 SessionManager 读写 transcript。


上下文窗口 vs 已追踪 Token 数

两个概念需要区分:

  1. 模型上下文窗口:每个模型的硬性上限(模型可见的 token 数)
  2. Session 存储计数器:写入 sessions.json 的滚动统计(用于 /status 和仪表板)

调整限制时:

  • 上下文窗口来自模型目录(可通过配置覆盖)。
  • 存储中的 contextTokens 是运行时估算/报告值;不要将其视为严格保证。

更多内容见 token-use 参考


压缩机制:是什么

压缩将较旧的对话汇总为 transcript 中一个持久化的 compaction 条目,并保留最近的消息完整。

压缩后,后续轮次将看到:

  • 压缩摘要
  • firstKeptEntryId 之后的消息

压缩是持久化的(与 session 修剪不同)。参见 /openclaw/concepts/session-pruning


自动压缩触发时机(Pi 运行时)

在嵌入式 Pi Agent 中,自动压缩在两种情况下触发:

  1. 溢出恢复:模型返回上下文溢出错误 → 压缩 → 重试。
  2. 阈值维护:成功的轮次结束后,当:

contextTokens > contextWindow - reserveTokens

其中:

  • contextWindow 是模型的上下文窗口大小
  • reserveTokens 是为提示词 + 下一次模型输出预留的余量

这些是 Pi 运行时的语义(OpenClaw 消费事件,但 Pi 决定何时压缩)。


压缩配置(reserveTokenskeepRecentTokens

Pi 的压缩配置存在于 Pi 设置中:

json5
{
  compaction: {
    enabled: true,
    reserveTokens: 16384,
    keepRecentTokens: 20000,
  },
}

OpenClaw 还对嵌入式运行强制设置安全下限:

  • 如果 compaction.reserveTokens < reserveTokensFloor,OpenClaw 会将其提升。
  • 默认下限为 20000 token。
  • 设置 agents.defaults.compaction.reserveTokensFloor: 0 可禁用下限。
  • 如果已经更高,OpenClaw 不做修改。

原因:为多轮"后台任务"(如内存写入)保留足够的余量,避免在压缩迫在眉睫前无法完成。

小提示:这个下限机制正是让你的龙虾在高强度对话中依然能从容写内存的保障。

实现:src/agents/pi-settings.ts 中的 ensurePiCompactionReserveTokens() (从 src/agents/pi-embedded-runner.ts 调用)。


用户可见的入口

你可以通过以下方式观察压缩和 session 状态:

  • /status(在任意聊天 session 中)
  • openclaw status(CLI)
  • openclaw sessions / sessions --json
  • 详细模式:🧹 Auto-compaction complete + 压缩计数

静默后台任务(NO_REPLY

OpenClaw 支持用于后台任务的"静默"轮次,用户不应看到中间输出。

约定:

  • 助手以 NO_REPLY 开头输出,表示"不要向用户发送回复"。
  • OpenClaw 在传递层过滤/抑制此内容。

2026.1.10 起,当部分块以 NO_REPLY 开头时,OpenClaw 还会抑制草稿/输入中流式传输,防止静默操作在轮次中间泄漏部分输出。


Pre-Compaction "内存刷新"(已实现)

目标:在自动压缩发生前,运行一个静默的 agentic 轮次,将持久化状态写入磁盘(例如,Agent 工作区中的 memory/YYYY-MM-DD.md),防止压缩抹去关键上下文。

OpenClaw 采用预阈值刷新方式:

  1. 监控 session 上下文使用量。
  2. 当使用量越过"软阈值"(低于 Pi 的压缩阈值)时,向 Agent 发出静默的"立即写入内存"指令。
  3. 使用 NO_REPLY,用户看不到任何输出。

配置(agents.defaults.compaction.memoryFlush):

  • enabled(默认:true
  • softThresholdTokens(默认:4000
  • prompt(刷新轮次的用户消息)
  • systemPrompt(刷新轮次追加的额外系统提示词)

注意事项:

  • 默认的 prompt/systemPrompt 包含 NO_REPLY 提示以抑制传递。
  • 每次压缩周期只刷新一次(在 sessions.json 中追踪)。
  • 刷新仅在嵌入式 Pi session 中运行(CLI 后端跳过)。
  • 当 session 工作区为只读时跳过刷新(workspaceAccess: "ro""none")。
  • 工作区文件布局和写入模式见 Memory

Pi 还在扩展 API 中暴露了 session_before_compact 钩子,但 OpenClaw 的刷新逻辑目前在 Gateway 侧实现。


故障排查检查清单

  • Session 键错误?从 /openclaw/concepts/session 入手,通过 /status 确认 sessionKey
  • 存储与 transcript 不匹配?通过 openclaw status 确认 Gateway 主机和存储路径。
  • 压缩过于频繁?检查:
    • 模型上下文窗口(是否太小)
    • 压缩配置(reserveTokens 相对于模型窗口过高会导致更早压缩)
    • 工具结果膨胀:启用/调整 session 修剪
  • 静默轮次泄漏?确认回复确实以 NO_REPLY(精确 token)开头,且你使用的版本已包含流式传输抑制修复。