Session 管理

OpenClaw 将每个 Agent 的一个直接聊天 Session 作为主 Session。直接聊天会合并到 agent:<agentId>:<mainKey>(默认是 main),而群组/频道聊天会有自己的 key。系统会遵循 session.mainKey 的设置。

使用 session.dmScope 来控制私信的分组方式:

  • main(默认):所有 DM 共享主 Session,保持对话连续性。
  • per-peer:按发送者 ID 隔离,跨 Channel 独立。
  • per-channel-peer:按 Channel + 发送者隔离(推荐用于多用户收件箱)。
  • per-account-channel-peer:按账号 + Channel + 发送者隔离(推荐用于多账号收件箱)。 使用 session.identityLinks 可以将带提供商前缀的对等方 ID 映射到统一身份,这样同一个人在使用 per-peerper-channel-peerper-account-channel-peer 时,可以跨 Channel 共享同一个 DM Session。

Gateway 是唯一数据源

所有 Session 状态都由 Gateway 拥有(即”主” OpenClaw)。UI 客户端(macOS 应用、WebChat 等)必须向 Gateway 查询 Session 列表和 Token 计数,而不是读取本地文件。

  • 远程模式下,你关心的 Session 存储位于远程 Gateway 主机上,而不是你的 Mac 上。
  • UI 中显示的 Token 计数来自 Gateway 存储字段(inputTokensoutputTokenstotalTokenscontextTokens)。客户端不会解析 JSONL 记录来”修正”总数。

状态存储位置

  • Gateway 主机上:
    • 存储文件:~/.openclaw/agents/<agentId>/sessions/sessions.json(每个 Agent 一个)。
  • 记录文件:~/.openclaw/agents/<agentId>/sessions/<SessionId>.jsonl(Telegram 话题 Session 使用 .../<SessionId>-topic-<threadId>.jsonl)。
  • 存储是一个映射 sessionKey -> { sessionId, updatedAt, ... }。删除条目是安全的,它们会按需重新创建。
  • 群组条目可能包含 displayNamechannelsubjectroomspace,用于在 UI 中标记 Session。
  • Session 条目包含 origin 元数据(标签 + 路由提示),这样 UI 可以解释 Session 的来源。
  • OpenClaw 不会读取旧版 Pi/Tau Session 文件夹。

Session 修剪

OpenClaw 默认会在调用 LLM 之前从内存上下文中修剪旧的工具结果。 这不会重写 JSONL 历史记录。详见 /concepts/session-pruning

Compaction 前的内存刷新

当 Session 接近自动 Compaction 时,OpenClaw 可以运行一个静默内存刷新轮次,提醒模型将持久化笔记写入磁盘。这只在 Workspace 可写时运行。详见 MemoryCompaction

传输方式 → Session key 的映射

  • 直接聊天遵循 session.dmScope(默认是 main)。
    • mainagent:<agentId>:<mainKey>(跨设备/Channel 保持连续性)。
      • 多个电话号码和 Channel 可以映射到同一个 Agent 主 key,它们作为进入同一对话的传输方式。
    • per-peeragent:<agentId>:dm:<peerId>
    • per-channel-peeragent:<agentId>:<channel>:dm:<peerId>
    • per-account-channel-peeragent:<agentId>:<channel>:<accountId>:dm:<peerId>(accountId 默认是 default)。
    • 如果 session.identityLinks 匹配到带提供商前缀的对等方 ID(例如 telegram:123),统一 key 会替换 <peerId>,这样同一个人可以跨 Channel 共享 Session。
  • 群组聊天隔离状态:agent:<agentId>:<channel>:group:<id>(房间/频道使用 agent:<agentId>:<channel>:channel:<id>)。
    • Telegram 论坛话题会在群组 ID 后追加 :topic:<threadId> 来隔离。
    • 旧版 group:<id> key 仍然可以识别,用于迁移。
  • 入站上下文可能仍使用 group:<id>;Channel 会从 Provider 推断并规范化为 agent:<agentId>:<channel>:group:<id> 形式。
  • 其他来源:
    • Cron 任务:cron:<job.id>
    • Webhook:hook:<uuid>(除非由 hook 明确设置)
    • Node 运行:node-<nodeId>

生命周期

  • 重置策略:Session 会被重复使用直到过期,过期检查在下一条入站消息时进行。
  • 每日重置:默认是 Gateway 主机本地时间凌晨 4:00。当 Session 的最后更新时间早于最近一次每日重置时间时,Session 就过期了。
  • 空闲重置(可选):idleMinutes 添加一个滑动空闲窗口。当同时配置每日重置和空闲重置时,哪个先过期就用哪个来强制创建新 Session。
  • 旧版仅空闲模式:如果你设置了 session.idleMinutes 但没有任何 session.reset/resetByType 配置,OpenClaw 会保持仅空闲模式以保持向后兼容。
  • 按类型覆盖(可选):resetByType 让你可以为 dmgroupthread Session 覆盖策略(thread = Slack/Discord 线程、Telegram 话题、连接器提供的 Matrix 线程)。
  • 按 Channel 覆盖(可选):resetByChannel 为某个 Channel 覆盖重置策略(应用于该 Channel 的所有 Session 类型,优先级高于 reset/resetByType)。
  • 重置触发器:精确的 /new/reset(加上 resetTriggers 中的任何额外触发器)会启动一个新的 Session ID,并将消息的剩余部分传递下去。/new <model> 接受模型别名、provider/model 或提供商名称(模糊匹配)来设置新 Session 的模型。如果单独发送 /new/reset,OpenClaw 会运行一个简短的”你好”问候轮次来确认重置。
  • 手动重置:从存储中删除特定 key 或删除 JSONL 记录文件;下一条消息会重新创建它们。
  • 隔离的 Cron 任务总是为每次运行生成一个新的 sessionId(不会空闲重用)。

发送策略(可选)

阻止特定 Session 类型的消息发送,无需列出单个 ID。

{
  session: {
    sendPolicy: {
      rules: [
        { action: "deny", match: { channel: "discord", chatType: "group" } },
        { action: "deny", match: { keyPrefix: "cron:" } },
      ],
      default: "allow",
    },
  },
}

运行时覆盖(仅所有者):

  • /send on → 允许此 Session 发送
  • /send off → 拒绝此 Session 发送
  • /send inherit → 清除覆盖并使用配置规则 将这些作为独立消息发送以便注册。

配置(可选重命名示例)

// ~/.openclaw/openclaw.json
{
  session: {
    scope: "per-sender", // 保持群组 key 独立
    dmScope: "main", // DM 连续性(共享收件箱设置为 per-channel-peer/per-account-channel-peer)
    identityLinks: {
      alice: ["telegram:123456789", "discord:987654321012345678"],
    },
    reset: {
      // 默认值:mode=daily, atHour=4(Gateway 主机本地时间)。
      // 如果你也设置了 idleMinutes,哪个先过期就用哪个。
      mode: "daily",
      atHour: 4,
      idleMinutes: 120,
    },
    resetByType: {
      thread: { mode: "daily", atHour: 4 },
      dm: { mode: "idle", idleMinutes: 240 },
      group: { mode: "idle", idleMinutes: 120 },
    },
    resetByChannel: {
      discord: { mode: "idle", idleMinutes: 10080 },
    },
    resetTriggers: ["/new", "/reset"],
    store: "~/.openclaw/agents/{agentId}/sessions/sessions.json",
    mainKey: "main",
  },
}

检查

  • openclaw status — 显示存储路径和最近的 Session。
  • openclaw sessions --json — 导出所有条目(用 --active <minutes> 过滤)。
  • openclaw gateway call sessions.list --params '{}' — 从运行中的 Gateway 获取 Session(使用 --url/--token 访问远程 Gateway)。
  • 在聊天中发送 /status 作为独立消息,查看 Agent 是否可达、Session 上下文使用了多少、当前的 thinking/verbose 开关状态,以及你的 WhatsApp web 凭证最后刷新时间(帮助发现需要重新链接的情况)。
  • 发送 /context list/context detail 查看系统 Prompt 和注入的 Workspace 文件中有什么(以及最大的上下文贡献者)。
  • 发送 /stop 作为独立消息来中止当前运行,清除该 Session 的排队后续任务,并停止从它派生的任何子 Agent 运行(回复会包含停止的数量)。
  • 发送 /compact(可选指令)作为独立消息来总结旧的上下文并释放窗口空间。详见 /concepts/compaction
  • 可以直接打开 JSONL 记录文件来查看完整的轮次。

提示

  • 保持主 key 专用于 1:1 对话;让群组保持自己的 key。
  • 自动清理时,删除单个 key 而不是整个存储,以保留其他地方的上下文。

Session 来源元数据

每个 Session 条目会记录它的来源(尽力而为)在 origin 中:

  • label:人类可读标签(从对话标签 + 群组主题/频道解析)
  • provider:规范化的 Channel ID(包括扩展)
  • from/to:来自入站信封的原始路由 ID
  • accountId:提供商账号 ID(多账号时)
  • threadId:线程/话题 ID(当 Channel 支持时) 来源字段会为私信、频道和群组填充。如果连接器只更新发送路由(例如,保持 DM 主 Session 活跃),它仍应提供入站上下文,这样 Session 可以保留其解释元数据。扩展可以通过在入站上下文中发送 ConversationLabelGroupSubjectGroupChannelGroupSpaceSenderName,并调用 recordSessionMetaFromInbound(或将相同上下文传递给 updateLastRoute)来实现。