5.1 会话模型设计

生成模型: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 的标识符(如 maincoding-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:main

默认 Agent 的主会话

agent:main:dm:alice

默认 Agent 与 alice 的 DM 会话

agent:main:telegram:group:12345

默认 Agent 在 Telegram 群组 12345 的会话

agent:main:slack:dm:U123:thread:T456

Slack DM 中某个线程的会话

agent:coding:subagent:task-1

coding Agent 的子代理会话

agent:main:cron:daily-report:run:uuid

定时任务的运行会话

Agent ID 规范化

Agent ID 在存储前会经过严格的规范化处理:

这个设计保证了会话键是路径安全(Path-Safe)和Shell 安全(Shell-Friendly)的——因为会话键最终会被用作文件路径的一部分。

特殊会话键

除了标准的 agent:*:* 格式,还有两个特殊的会话键:

  • global — 全局会话,所有 Agent 共享

  • unknown — 未识别来源的消息会话

辅助函数可以检测特殊类型的会话键:

5.1.2 会话范围(DM Scope)

当用户通过私聊(DM, Direct Message)与 OpenClaw 交互时,一个关键问题是:同一个用户在不同通道(Telegram、Slack、Discord 等)上的对话应该共享同一个会话,还是各自独立?

OpenClaw 通过 dmScope 配置项提供了四种策略:

范围
会话键格式
行为

main

agent:<id>:main

所有 DM 共享同一个会话

per-peer

agent:<id>:dm:<peerId>

每个发送者一个会话

per-channel-peer

agent:<id>:<channel>:dm:<peerId>

每个通道+发送者一个会话

per-account-channel-peer

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 格式的优势在于:

  1. 追加友好:新消息只需追加一行,不需要读取和重写整个文件

  2. 流式读取:可以逐行读取,不需要将整个文件加载到内存

  3. 部分读取:可以只读取前 N 行或后 N 行

转录文件的路径与会话键相关,存储在 Agent 的工作目录下。session-utils.fs.ts 提供了读取转录文件的工具函数:

会话分类

classifySessionKey() 函数根据会话键的结构判断会话类型:


本节小结

OpenClaw 的会话模型设计有几个核心特点:

  1. 结构化的会话键agent:<agentId>:<rest> 格式将 Agent 身份、消息来源、通道信息编码进键名,使得会话键本身就是可解析的元数据

  2. 灵活的 DM 范围:四种范围策略覆盖了从"所有人共享"到"每个通道每个账户每个用户独立"的完整频谱

  3. 双层持久化:JSON Store 存元数据(快速查询),JSONL 存对话历史(高效追加),各取所长

  4. 身份链接:跨通道的用户身份合并,避免同一个人在不同平台产生碎片化的会话

在下一节中,我们将深入会话路由的源码,看看 Gateway 如何根据入站消息的来源自动决定使用哪个会话。

Last updated