会话管理

OpenClaw ?*每个代理一个直接聊天会?*视为主要的。直接聊天折叠到 agent:<agentId>:<mainKey>(默?main),而群?通道聊天获得自己的键。session.mainKey 会被遵守?

使用 session.dmScope 控制直接消息如何分组?

  • main(默认):所?DM 共享主会话以保持连续性?
  • per-peer:跨通道按发送?ID 隔离?
  • per-channel-peer:按通道 + 发送者隔离(推荐用于多用户收件箱)?
  • per-account-channel-peer:按账户 + 通道 + 发送者隔离(推荐用于多账户收件箱)? 使用 session.identityLinks 将提供商前缀的发送?ID 映射到规范身份,以便使用 per-peerper-channel-peer ?per-account-channel-peer 时,同一个人跨通道共享 DM 会话?

安全 DM 模式(推荐用于多用户设置?

安全警告? 如果你的代理可以接收**多个??DM,你应该强烈考虑启用安全 DM 模式。没有它,所有用户共享相同的对话上下文,这可能在用户之间泄露私人信息?

默认设置的问题示例:

  • Alice(<SENDER_A>)向你的代理发送关于私人话题的消息(例如医疗预约)
  • Bob(<SENDER_B>)向你的代理发送消息问”我们在聊什么?”
  • 因为两个 DM 共享相同会话,模型可能会使用 Alice 的先前上下文回答 Bob?

*修复? 设置 dmScope 为每个用户隔离会话:

// ~/.openclaw/openclaw.json
{
  session: {
    // 安全 DM 模式:按通道 + 发送者隔?DM 上下文?
    dmScope: "per-channel-peer",
  },
}

*何时启用?

  • 你有多个发件人的配对批准
  • 你使用带有多个条目的 DM 白名?
  • 你设?dmPolicy: "open"
  • 多个电话号码或账户可以向你的代理发送消?

说明?

  • 默认?dmScope: "main" 以保持连续性(所?DM 共享主会话)。这对于单用户设置很好?
  • 本地 CLI 引导在未设置时默认写?session.dmScope: "per-channel-peer"(保留现有显式值)?
  • 对于同一通道上的多账户收件箱,优先使?per-account-channel-peer?
  • 如果同一个人通过多个通道联系你,使用 session.identityLinks 将他们的 DM 会话折叠为一个规范身份?
  • 你可以使?openclaw security audit 验证你的 DM 设置(请参阅 security)?

网关是真相来?

所有会话状?由网??? OpenClaw)拥有。UI 客户端(macOS 应用、WebChat 等)必须查询网关以获取会话列表和 token 计数,而不是读取本地文件?

  • ?*远程模式**下,你关心的会话存储在远程网关主机上,而不是你?Mac?
  • UI 中显示的 token 计数来自网关的存储字段(inputTokensoutputTokenstotalTokenscontextTokens)。客户端不会解析 JSONL 记录?修复”总数?

状态位?

  • ?*网关主机**上:
    • 存储文件:~/.openclaw/agents/<agentId>/sessions/sessions.json(按代理)?
  • 记录:~/.openclaw/agents/<agentId>/sessions/<SessionId>.jsonl(Telegram 主题会话使用 .../<SessionId>-topic-<threadId>.jsonl)?
  • 存储?sessionKey -> { sessionId, updatedAt, ... } 的映射。删除条目是安全的;它们会在需要时重新创建?
  • 群组条目可能包含 displayNamechannelsubjectroom ?space 以便?UI 中标记会话?
  • 会话条目包含 origin 元数据(标签 + 路由提示),以便 UI 可以解释会话来自哪里?
  • OpenClaw 不会读取旧版 Pi/Tau 会话文件夹?

维护

OpenClaw 对会话存储进行维护,以保?sessions.json 和记录工件随时间有界?

默认?

  • session.maintenance.mode: warn
  • session.maintenance.pruneAfter: 30d
  • session.maintenance.maxEntries: 500
  • session.maintenance.rotateBytes: 10mb
  • session.maintenance.resetArchiveRetention: 默认?pruneAfter30d?
  • session.maintenance.maxDiskBytes: 未设置(禁用?
  • session.maintenance.highWaterBytes: 启用预算时默认为 maxDiskBytes ?80%

工作原理

维护在会话存储写入期间运行,你可以通过 openclaw sessions cleanup 按需触发?

  • mode: "warn":报告将被删除的内容,但不改变条?记录?
  • mode: "enforce":按以下顺序应用清理?
    1. 修剪早于 pruneAfter 的陈旧条?
    2. 将条目数限制?maxEntries(最旧的优先?
    3. 归档不再引用的已删除条目的记录文?
    4. 按保留策略清除旧?*.deleted.<timestamp> ?*.reset.<timestamp> 归档
    5. ?sessions.json 超过 rotateBytes 时进行轮?
    6. 如果设置?maxDiskBytes,强制执行朝?highWaterBytes 的磁盘预算(最旧的工件先删除,然后是最旧的会话?

大存储的性能警告

大会话存储在高容量设置中很常见。维护工作是写路径工作,因此非常大的存储可能会增加写入延迟?

最增加成本的因素:

  • 非常高的 session.maintenance.maxEntries ?
  • 长的 pruneAfter 窗口使陈旧条目保?
  • 许多记录/归档工件?~/.openclaw/agents/<agentId>/sessions/
  • 启用磁盘预算(maxDiskBytes)而没有合理的修剪/上限限制

要做什么:

  • 在生产中使用 mode: "enforce" 以便增长自动有界
  • 设置时间和计数限制(pruneAfter + maxEntries),而不仅仅是其中一?
  • 为大型部署设?maxDiskBytes + highWaterBytes 以获得硬上限
  • ?highWaterBytes 保持?maxDiskBytes 以下有意义的距离(默认是 80%?
  • 在配置更改后运行 openclaw sessions cleanup --dry-run --json 以验证预期影响,然后再强制执?
  • 对于频繁活动的会话,在运行手动清理时传?--active-key

自定义示?

使用保守的强制策略:

{
  session: {
    maintenance: {
      mode: "enforce",
      pruneAfter: "45d",
      maxEntries: 800,
      rotateBytes: "20mb",
      resetArchiveRetention: "14d",
    },
  },
}

为会话目录启用硬磁盘预算?

{
  session: {
    maintenance: {
      mode: "enforce",
      maxDiskBytes: "1gb",
      highWaterBytes: "800mb",
    },
  },
}

为大型安装调整(示例):

{
  session: {
    maintenance: {
      mode: "enforce",
      pruneAfter: "14d",
      maxEntries: 2000,
      rotateBytes: "25mb",
      maxDiskBytes: "2gb",
      highWaterBytes: "1.6gb",
    },
  },
}

?CLI 预览或强制维护:

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

会话修剪

默认情况下,OpenClaw ?LLM 调用之前从内存中上下?修整旧工具结?? ?*不会**重写 JSONL 历史。请参阅 /concepts/session-pruning?

预压缩内存刷?

当会话接近自动压缩时,OpenClaw 可以运行一?*静默内存刷新**轮,提醒模型将持久化笔记写入磁盘。这仅在工作区可写时运行。请参阅 Memory ?Compaction?

传输 ?会话键的映射

  • 直接聊天遵循 session.dmScope(默?main)?
    • mainagent:<agentId>:<mainKey>(跨设备/通道的连续性)?
      • 多个电话号码和通道可以映射到相同的代理主键;它们作为进入一个对话的传输方式?
    • 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),规范键替?<peerId>,以便同一个人跨通道共享会话?
  • 群组聊天隔离状态:agent:<agentId>:<channel>:group:<id>(房?通道使用 agent:<agentId>:<channel>:channel:<id>)?
    • Telegram 论坛主题附加 :topic:<threadId> 到群?ID 以实现隔离?
    • 仍可识别旧版 group:<id> 键以进行迁移?
  • 入站上下文可能仍使用 group:<id>;通道?Provider 推断并规范化?agent:<agentId>:<channel>:group:<id> 形式?
  • 其他来源?
    • Cron 作业:cron:<job.id>
    • Webhooks:hook:<uuid>(除?hook 明确设置?
    • 节点运行:node-<nodeId>

生命周期

  • 重置策略:会话被重用直到过期,过期在下一个入站消息时评估?
  • 每日重置:默认为网关主机当地时间凌晨 4:00。一旦会话的最后更新早于最近的每日重置时间,它就变得陈旧?
  • 空闲重置(可选):idleMinutes 添加滑动空闲窗口。当同时配置了每日和空闲重置时,先过期的那个强制新会话?
  • 旧版仅空闲:如果设置 session.idleMinutes 而没有任?session.reset/resetByType 配置,OpenClaw 保持仅空闲模式以保持向后兼容?
  • 每类型覆盖(可选):resetByType 允许?directgroup ?thread 会话覆盖策略(thread = Slack/Discord 线程、Telegram 主题、Matrix 线程,如果连接器提供)?
  • 每通道覆盖(可选):resetByChannel 覆盖通道的重置策略(适用于该通道的所有会话类型,优先?reset/resetType)?
  • 重置触发:精确的 /new ?/reset(加?resetTriggers 中的任何额外项)开始新?session id 并传递消息的其余部分。/new <model> 接受模型别名、provider/model 或提供商名称(模糊匹配)以设置新会话模型。如果单独发?/new ?/reset,OpenClaw 运行简短的”你好”回复轮以确认重置?
  • 手动重置:从存储中删除特定键或删?JSONL 记录;下一条消息会重新创建它们?
  • 隔离?Cron 作业始终为每次运行生成新?sessionId(无空闲重用)?

发送策略(可选)

阻止投递特定会话类型,而不列出个人 ID?

{
  session: {
    sendPolicy: {
      rules: [
        { action: "deny", match: { channel: "discord", chatType: "group" } },
        { action: "deny", match: { keyPrefix: "cron:" } },
        // 匹配原始会话键(包括 `agent:<id>:` 前缀)?
        { action: "deny", match: { rawKeyPrefix: "agent:main:discord:" } },
      ],
      default: "allow",
    },
  },
}

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

  • /send on ?允许此会?
  • /send off ?拒绝此会?
  • /send inherit ?清除覆盖并使用配置规? 将这些作为独立消息发送,以便它们被注册?

配置(可选重命名示例?

// ~/.openclaw/openclaw.json
{
  session: {
    scope: "per-sender", // 保持群组键分开
    dmScope: "main", // DM 连续性(为共享收件箱设置 per-channel-peer/per-account-channel-peer?
    identityLinks: {
      alice: ["telegram:123456789", "discord:987654321012345678"],
    },
    reset: {
      // 默认:mode=daily, atHour=4(网关主机当地时间)?
      // 如果还设置了 idleMinutes,先过期者获胜?
      mode: "daily",
      atHour: 4,
      idleMinutes: 120,
    },
    resetByType: {
      thread: { mode: "daily", atHour: 4 },
      direct: { 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 ?显示存储路径和最近的会话?
  • openclaw sessions --json ?转储每个条目(用 --active <minutes> 过滤)?
  • openclaw gateway call sessions.list --params '{}' ?从运行的网关获取会话(使?--url/--token 访问远程网关)?
  • 在聊天中发?/status 作为独立消息,查看代理是否可达、会话上下文使用了多少、当前思?详细切换,以及你?WhatsApp web 凭证最后何时刷新(帮助发现重新链接需求)?
  • 发?/context list ?/context detail 查看系统提示和注入的工作区文件中有哪些内容(以及最大的上下文贡献者)?
  • 发?/stop(或独立中止短语?stopstop actionstop runstop openclaw)中止当前运行、清除该会话的排队后续并停止任何从它生成的子代理运行(回复包含停止计数)?
  • 发?/compact(可选指令)作为独立消息,以总结较旧的上下文并释放窗口空间。请参阅 /concepts/compaction?
  • JSONL 记录可以直接打开以查看完整轮次?

技?

  • 保持主键专用?1:1 流量;让群组保持自己的键?
  • 自动化清理时,删除单个键而不是整个存储,以保留其他地方的上下文?

会话来源元数?

每个会话条目?origin 中记录它来自哪里(尽力而为):

  • label:人类可读标签(从对话标?+ 群组主题/通道解析?
  • provider:规范化通道 ID(包括扩展)
  • from/to:入站信封中的原始路?ID
  • accountId:提供商账户 ID(多账户时)
  • threadId:通道支持时的线程/主题 ID 直接消息、通道和群组都会填充来源字段。如果连接器仅更新投递路由(例如保持 DM 主会话新鲜),它仍应提供入站上下文,以便会话保留其解释器元数据。扩展可以通过在入站上下文中发?ConversationLabelGroupSubjectGroupChannelGroupSpace ?SenderName 并调?recordSessionMetaFromInbound(或传递相同上下文?updateLastRoute)来做到这一点?