生成模型:Claude Opus 4.6 (anthropic/claude-opus-4-6) Token 消耗:输入 ~250,000 tokens,输出 ~18,000 tokens(本章合计)
会话(Session)是 OpenClaw 中对话上下文的容器。每一次与 AI 的交互都发生在某个会话中,会话保存了对话历史、配置覆盖、元数据等信息。本节将分析会话模型的核心设计。
5.1.1 会话键(Session Key)的结构
在 OpenClaw 中,每个会话由一个会话键(Session Key)唯一标识。会话键的标准格式为:
agent:<agentId>:<rest>
其中:
agent 是固定前缀,标识这是一个 Agent 会话键
<agentId> 是 Agent 的标识符(如 main、coding-assistant)
<rest> 是会话的具体标识部分,根据消息类型和配置不同而变化
src/sessions/session-key-utils.ts 中的 parseAgentSessionKey() 函数负责解析这个结构:
// src/sessions/session-key-utils.ts
export type ParsedAgentSessionKey = {
agentId: string;
rest: string;
};
export function parseAgentSessionKey(
sessionKey: string | undefined | null,
): ParsedAgentSessionKey | null {
const raw = (sessionKey ?? "").trim();
if (!raw) return null;
const parts = raw.split(":").filter(Boolean);
if (parts.length < 3) return null; // 至少需要 3 段
if (parts[0] !== "agent") return null; // 必须以 "agent" 开头
const agentId = parts[1]?.trim();
const rest = parts.slice(2).join(":"); // 剩余部分可能包含冒号
if (!agentId || !rest) return null;
return { agentId, rest };
}
以下是几种典型的会话键:
agent:main:telegram:group:12345
默认 Agent 在 Telegram 群组 12345 的会话
agent:main:slack:dm:U123:thread:T456
agent:coding:subagent:task-1
agent:main:cron:daily-report:run:uuid
Agent ID 在存储前会经过严格的规范化处理:
这个设计保证了会话键是路径安全(Path-Safe)和Shell 安全(Shell-Friendly)的——因为会话键最终会被用作文件路径的一部分。
除了标准的 agent:*:* 格式,还有两个特殊的会话键:
global — 全局会话,所有 Agent 共享
辅助函数可以检测特殊类型的会话键:
5.1.2 会话范围(DM Scope)
当用户通过私聊(DM, Direct Message)与 OpenClaw 交互时,一个关键问题是:同一个用户在不同通道(Telegram、Slack、Discord 等)上的对话应该共享同一个会话,还是各自独立?
OpenClaw 通过 dmScope 配置项提供了四种策略:
agent:<id>:<channel>:dm:<peerId>
agent:<id>:<channel>:<account>:dm:<peerId>
buildAgentPeerSessionKey() 函数根据 dmScope 生成对应的会话键:
衍生解释:身份链接(Identity Links)是一种跨平台用户身份合并机制。在 per-peer 或更细粒度的范围下,同一个人在 Telegram(ID: tg:12345)和 Slack(ID: slack:U678)上会被视为不同的发送者,产生不同的会话。通过配置 identityLinks,可以将这些 ID 映射到同一个规范身份,使得该用户在所有通道上共享同一个会话上下文。
5.1.3 会话持久化:JSON Store + JSONL 转录文件
OpenClaw 使用双层存储模型来持久化会话:
第一层:会话元数据(JSON Store)
会话的元数据(标签、配置覆盖、时间戳等)存储在一个 JSON 文件中,路径通常为 <stateDir>/agents/<agentId>/sessions.json。每个会话键对应一个 SessionEntry 对象:
第二层:对话转录(JSONL Transcript)
实际的对话历史(每条消息的内容、角色、工具调用结果等)存储为 JSONL(JSON Lines)格式的文件。每行是一个 JSON 对象,代表一条消息。JSONL 格式的优势在于:
追加友好:新消息只需追加一行,不需要读取和重写整个文件
流式读取:可以逐行读取,不需要将整个文件加载到内存
转录文件的路径与会话键相关,存储在 Agent 的工作目录下。session-utils.fs.ts 提供了读取转录文件的工具函数:
classifySessionKey() 函数根据会话键的结构判断会话类型:
OpenClaw 的会话模型设计有几个核心特点:
结构化的会话键:agent:<agentId>:<rest> 格式将 Agent 身份、消息来源、通道信息编码进键名,使得会话键本身就是可解析的元数据
灵活的 DM 范围:四种范围策略覆盖了从"所有人共享"到"每个通道每个账户每个用户独立"的完整频谱
双层持久化:JSON Store 存元数据(快速查询),JSONL 存对话历史(高效追加),各取所长
身份链接:跨通道的用户身份合并,避免同一个人在不同平台产生碎片化的会话
在下一节中,我们将深入会话路由的源码,看看 Gateway 如何根据入站消息的来源自动决定使用哪个会话。