会话管理
OpenClaw 为每个 agent 维护一个主直聊会话。直聊会收拢到 agent:<agentId>:<mainKey>(默认 main),群聊/频道聊天则各有独立的 key。session.mainKey 配置会被尊重。
通过 session.dmScope 控制私信的分组方式:
main(默认):所有私信共享主会话,保持对话连贯。per-peer:按发送者 ID 跨渠道隔离。per-channel-peer:按渠道 + 发送者隔离(推荐多用户收件箱场景)。per-account-channel-peer:按账号 + 渠道 + 发送者隔离(推荐多账号收件箱场景)。 使用session.identityLinks可以把带有服务商前缀的 peer ID 映射到统一身份,这样在per-peer、per-channel-peer或per-account-channel-peer模式下,同一个人跨渠道也能共享同一个私信会话。
安全私信模式(多用户场景建议开启)
安全警告: 如果你的 agent 会收到多人的私信,强烈建议启用安全私信模式。不启用的话,所有用户共享同一个对话上下文,可能导致用户之间的信息泄露。
默认设置下的问题示例:
- Alice(
<SENDER_A>)给你的 agent 发了一条涉及隐私的消息(比如一个医疗预约) - Bob(
<SENDER_B>)问你的 agent:“我们刚才聊的是什么?” - 因为两人的私信共享同一个会话,模型可能会用 Alice 的上下文来回答 Bob
解决方案: 设置 dmScope 来隔离每个用户的会话:
// ~/.openclaw/openclaw.json
{
session: {
// 安全私信模式:按渠道 + 发送者隔离私信上下文。
dmScope: "per-channel-peer",
},
}
何时需要启用:
- 你对多个发送者做了配对审批
- 你的 DM 允许列表有多条记录
- 你设置了
dmPolicy: "open" - 多个手机号或账号可以给你的 agent 发消息
注意事项:
- 默认是
dmScope: "main"以保持连贯性(所有私信共享主会话)。单用户场景下没问题。 - 本地 CLI 引导流程在未设置时默认写入
session.dmScope: "per-channel-peer"(已有显式值会被保留)。 - 同一渠道的多账号收件箱,建议用
per-account-channel-peer。 - 如果同一个人通过不同渠道联系你,可以用
session.identityLinks把他们的私信会话归并到一个统一身份。 - 你可以通过
openclaw security audit验证当前的私信设置(参见 security)。
Gateway 是权威数据源
所有会话状态都由 Gateway 持有(即”主” OpenClaw)。UI 客户端(macOS 应用、WebChat 等)必须向 Gateway 查询会话列表和 token 统计,而不是读取本地文件。
- 在远程模式下,你关心的会话存储在远程 Gateway 主机上,不在你的 Mac 上。
- UI 中显示的 token 数来自 Gateway 存储字段(
inputTokens、outputTokens、totalTokens、contextTokens)。客户端不会通过解析 JSONL 对话记录来”修正”总数。
状态存放位置
- Gateway 主机上:
- 存储文件:
~/.openclaw/agents/<agentId>/sessions/sessions.json(按 agent)。
- 存储文件:
- 对话记录:
~/.openclaw/agents/<agentId>/sessions/<SessionId>.jsonl(Telegram topic 会话使用.../<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>归档 - 当
sessions.json超过rotateBytes时进行轮转 - 如果设置了
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。
传输层 → 会话 key 的映射
- 直聊遵循
session.dmScope(默认main)。main:agent:<agentId>:<mainKey>(跨设备/渠道保持连贯)。- 多个手机号和渠道可以映射到同一个 agent main key,它们只是同一个对话的不同传输通道。
per-peer:agent:<agentId>:direct:<peerId>。per-channel-peer:agent:<agentId>:<channel>:direct:<peerId>。per-account-channel-peer:agent:<agentId>:<channel>:<accountId>:direct:<peerId>(accountId 默认为default)。- 如果
session.identityLinks匹配到带服务商前缀的 peer ID(比如telegram:123),统一 key 会替换<peerId>,使同一个人跨渠道共享会话。
- 群聊隔离状态:
agent:<agentId>:<channel>:group:<id>(频道/房间使用agent:<agentId>:<channel>:channel:<id>)。- Telegram 论坛话题在群组 ID 后追加
:topic:<threadId>来隔离。 - 旧版
group:<id>key 仍兼容迁移。
- Telegram 论坛话题在群组 ID 后追加
- 入站上下文可能仍使用
group:<id>;渠道信息从Provider推断,并标准化为agent:<agentId>:<channel>:group:<id>格式。 - 其他来源:
- 定时任务:
cron:<job.id> - Webhook:
hook:<uuid>(除非 hook 显式指定) - 节点运行:
node-<nodeId>
- 定时任务:
生命周期
- 重置策略:会话持续复用直到过期,过期判定在下一条入站消息到达时进行。
- 每日重置:默认 Gateway 主机本地时间凌晨 4:00。如果会话最后更新时间早于最近一次每日重置时间点,则视为过期。
- 空闲重置(可选):
idleMinutes添加一个滑动的空闲窗口。同时配置每日和空闲重置时,先到期的那个触发新会话。 - 旧版纯空闲模式:如果你设置了
session.idleMinutes但没有配置session.reset/resetByType,OpenClaw 保持纯空闲模式以向后兼容。 - 按类型覆盖(可选):
resetByType可以为direct、group和thread会话分别设置重置策略(thread = Slack/Discord 线程、Telegram topic、连接器提供的 Matrix 线程)。 - 按渠道覆盖(可选):
resetByChannel为某个渠道覆盖重置策略(适用于该渠道的所有会话类型,优先级高于reset/resetByType)。 - 重置触发词:精确匹配
/new或/reset(以及resetTriggers中的额外配置)会创建新的 session ID,消息剩余部分会继续传递。/new <model>接受模型别名、provider/model或服务商名称(模糊匹配)来设置新会话的模型。单独发送/new或/reset时,OpenClaw 会运行一轮简短的问候来确认重置。 - 手动重置:从存储中删除特定 key 或删除 JSONL 对话记录;下条消息会重新创建它们。
- 隔离的定时任务每次运行都会生成新的
sessionId(不复用空闲会话)。
发送策略(可选)
按会话类型阻止投递,无需列出具体 ID。
{
session: {
sendPolicy: {
rules: [
{ action: "deny", match: { channel: "discord", chatType: "group" } },
{ action: "deny", match: { keyPrefix: "cron:" } },
// 匹配原始会话 key(包含 `agent:<id>:` 前缀)。
{ action: "deny", match: { rawKeyPrefix: "agent:main:discord:" } },
],
default: "allow",
},
},
}
运行时覆盖(仅所有者):
/send on→ 允许当前会话发送/send off→ 拒绝当前会话发送/send inherit→ 清除覆盖,使用配置规则 作为独立消息发送以使其生效。
配置(可选的重命名示例)
// ~/.openclaw/openclaw.json
{
session: {
scope: "per-sender", // 群组 key 保持独立
dmScope: "main", // 私信连贯性(共享收件箱用 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 },
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 '{}'— 从运行中的 Gateway 获取会话(远程访问使用--url/--token)。- 在聊天中发送
/status作为独立消息,可以查看 agent 是否可达、会话上下文使用情况、当前思考/快速/详细切换状态,以及 WhatsApp web 凭据的上次刷新时间(便于发现需要重新链接的情况)。 - 发送
/context list或/context detail查看系统 prompt 和注入的工作空间文件(以及最大的上下文贡献者)。 - 发送
/stop(或独立的中止短语如stop、stop action、stop run、stop openclaw)中止当前运行、清除该会话的排队 followup,并停止由它创建的所有子 agent 运行(回复中包含已停止的数量)。 - 发送
/compact(可选指令)作为独立消息,总结较早的上下文并释放窗口空间。参见 /concepts/compaction。 - JSONL 对话记录可以直接打开查看完整的对话轮次。
使用建议
- 把主 key 专门用于 1:1 对话;群聊让它们保持各自的 key。
- 自动化清理时,删除单个 key 而不是整个存储,以保留其他上下文。
会话来源元数据
每个会话条目在 origin 中记录了它的来源(尽力而为):
label:可读标签(由对话标签 + 群组主题/频道生成)provider:标准化的渠道 ID(含扩展)from/to:入站信封中的原始路由 IDaccountId:服务商账号 ID(多账号场景)threadId:线程/话题 ID(渠道支持时提供) 来源字段对私信、频道和群组都会填充。如果连接器只更新投递路由(比如为了保持 DM 主会话的活跃),它仍应提供入站上下文以保留会话的解释性元数据。扩展可以通过在入站上下文中发送ConversationLabel、GroupSubject、GroupChannel、GroupSpace和SenderName,并调用recordSessionMetaFromInbound(或将相同上下文传给updateLastRoute)来实现。