会话管理
会话管理
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-peer、per-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)。
网关是事实来源
所有会话状态由网关("master" OpenClaw)拥有。UI 客户端(macOS 应用、WebChat 等)必须查询网关获取会话列表和 token 计数,而不是读取本地文件。
- 在远程模式下,你关心的会话存储位于远程网关主机上,而不是你的 Mac。
- UI 中显示的 token 计数来自网关的存储字段(
inputTokens、outputTokens、totalTokens、contextTokens)。客户端不会解析 JSONL 记录来"修复"总数。
状态存储位置
- 在网关主机上:
- 存储文件:
~/.openclaw/agents/<agentId>/sessions/sessions.json(每代理)。
- 存储文件:
- 记录:
~/.openclaw/agents/<agentId>/sessions/<SessionId>.jsonl(Telegram 主题会话使用.../<SessionId>-topic-<threadId>.jsonl)。 - 存储是一个映射
sessionKey -> { sessionId, updatedAt, ... }。删除条目是安全的;它们会按需重新创建。 - 群组条目可能包括
displayName、channel、subject、room和space来在 UI 中标记会话。 - 会话条目包括
origin元数据(标签 + 路由提示),以便 UI 可以解释会话来自哪里。 - OpenClaw 不读取旧版 Pi/Tau 会话文件夹。
维护
OpenClaw 应用会话存储维护来保持 sessions.json 和记录产物有界。
默认值
session.maintenance.mode:warnsession.maintenance.pruneAfter:30dsession.maintenance.maxEntries:500session.maintenance.rotateBytes:10mbsession.maintenance.resetArchiveRetention: 默认为pruneAfter(30d)session.maintenance.maxDiskBytes: 未设置(禁用)session.maintenance.highWaterBytes: 当启用预算时默认为maxDiskBytes的80%
工作原理
维护在会话存储写入期间运行,你可以使用 openclaw sessions cleanup 按需触发它。
mode: "warn":报告将被驱逐的内容但不修改条目/记录。mode: "enforce":按此顺序应用清理:- 修剪超过
pruneAfter的旧条目 - 将条目数限制为
maxEntries(最旧优先) - 为不再引用的已删除条目归档记录文件
- 按保留策略清除旧的
*.deleted.<timestamp>和*.reset.<timestamp>归档 - 当超过
rotateBytes时轮换sessions.json - 如果设置了
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。
预压缩 memory flush
当会话接近自动压缩时,OpenClaw 可以运行静默 memory flush 轮次提醒模型将持久笔记写入磁盘。这只在工作空间可写时运行。参见 Memory 和 Compaction。
映射传输 → 会话键
- 直接聊天遵循
session.dmScope(默认main)。main:agent:<agentId>:<mainKey>(跨设备/频道的连续性)。- 多个电话号码和频道可以映射到同一个代理主键;它们作为一个对话的传输。
per-peer:agent:<agentId>:dm:<peerId>。per-channel-peer:agent:<agentId>:<channel>:dm:<peerId>。per-account-channel-peer:agent:<agentId>:<channel>:<accountId>:dm:<peerId>(accountId 默认为default)。- 如果
session.identityLinks匹配提供者前缀的对等体 ID(例如telegram:123),规范键替换<peerId>以便同一个人在跨频道共享会话。
- 群组聊天隔离状态:
agent:<agentId>:<channel>:group:<id>(房间/频道使用agent:<agentId>:<channel>:channel:<id>)。- Telegram 论坛主题在群组 ID 后附加
:topic:<threadId>以进行隔离。 - 旧版
group:<id>键仍被识别用于迁移。
- Telegram 论坛主题在群组 ID 后附加
- 入站上下文可能仍使用
group:<id>;频道从Provider推断并规范化为规范agent:<agentId>:<channel>:group:<id>形式。 - 其他来源:
- Cron 作业:
cron:<job.id> - Webhooks:
hook:<uuid>(除非由 webhook 显式设置) - Node 运行:
node-<nodeId>
- Cron 作业:
生命周期
- 重置策略:会话被重用直到过期,过期在下一个入站消息时评估。
- 每日重置:默认网关主机本地时间凌晨 4:00。一旦会话的最后更新早于最近的每日重置时间,会话就过时了。
- 空闲重置(可选):
idleMinutes添加滑动空闲窗口。当每日和空闲重置都配置时,先过期的那个强制新会话。 - 旧版仅空闲:如果你设置
session.idleMinutes而没有session.reset/resetByType配置,OpenClaw 保持仅空闲模式以保持向后兼容。 - 每类型覆盖(可选):
resetByType允许你覆盖direct、group和thread会话的策略(thread = Slack/Discord 线程、Telegram 主题、Matrix 线程(当连接器提供时))。 - 每频道覆盖(可选):
resetByChannel覆盖频道的重置策略(适用于该频道的所有会话类型,优先于reset/resetByType)。 - 重置触发器:精确的
/new或/reset(加上resetTriggers中的任何额外内容)启动新会话 ID 并传递消息的其余部分。/new <model>接受模型别名、provider/model或提供者名称(模糊匹配)来设置新会话模型。如果单独发送/new或/reset,OpenClaw 运行简短的"hello"问候轮次以确认重置。 - 手动重置:从存储中删除特定键或删除 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(或独立中止短语如stop、stop action、stop run、stop openclaw)以中止当前运行、清除该会话的排队后续,并停止从中派生的任何子代理运行(回复包括停止计数)。 - 作为独立消息发送
/compact(可选指令)以总结旧上下文并释放窗口空间。参见 /concepts/compaction。 - JSONL 记录可以直接打开以查看完整轮次。
提示
- 保持主键专用于 1:1 流量;让群组保持自己的键。
- 自动化清理时,删除单个键而不是整个存储以保留其他地方的上下文。
会话来源元数据
每个会话条目在 origin 中记录它来自哪里(尽力而为):
label:人工标签(从对话标签 + 群组主题/频道解析)provider:规范化频道 ID(包括扩展)from/to:入站信封中的原始路由 IDaccountId:提供者账户 ID(多账户时)threadId:频道支持时的线程/主题 ID 为直接消息、频道和群组填充来源字段。如果连接器 仅更新传递路由(例如,保持 DM 主会话新鲜),它仍应提供入站上下文以便会话保持其 解释器元数据。扩展可以通过在入站 上下文中发送ConversationLabel、GroupSubject、GroupChannel、GroupSpace和SenderName并调用recordSessionMetaFromInbound(或将相同上下文传递给updateLastRoute)来执行此操作。