OpenClaw源码剖析 #07 · 会话与状态管理

一、会话系统定位

会话系统是 OpenClaw 的状态管理核心——管理会话生命周期、持久化、解析与投递。

┌─────────────────────────────────────────────────────────────┐
│                     Session System                           │
│  ┌──────────────────────────────────────────────────────┐  │
│  │                    SessionEntry                        │  │
│  │  sessionId · model · tokenCount · skillsSnapshot      │  │
│  │  acp meta · pluginExtensions · compactionState        │  │
│  └──────────────────────────────────────────────────────┘  │
│                           │                                  │
│  ┌────────────────┐  ┌─────────────┐  ┌─────────────┐     │
│  │  Session Key   │  │ Session     │  │  Transcript │     │
│  │  解析          │  │ Store       │  │  管理        │     │
│  └────────────────┘  └─────────────┘  └─────────────┘     │
└─────────────────────────────────────────────────────────────┘

二、核心类型

文件:config/sessions/types.ts

2.1 SessionEntry

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
export type SessionEntry = {
  // 身份
  sessionId: string;
  updatedAt: number;
  sessionFile?: string;

  // 父子关系
  spawnedBy?: string;           // 孵化此会话的父会话
  parentSessionKey?: string;    // Dashboard 创建的子会话
  spawnedWorkspaceDir?: string; // 子会话继承的工作目录

  // 时间戳
  sessionStartedAt?: number;    // 首次活跃时间
  lastInteractionAt?: number;   // 最后交互时间
  startedAt?: number;           // 首次运行开始
  endedAt?: number;             // 最后运行结束
  runtimeMs?: number;           // 累计运行时长

  // 模型与 Token
  model?: string;
  modelOverride?: string;
  providerOverride?: string;
  authProfileOverride?: string;
  inputTokens?: number;
  outputTokens?: number;
  totalTokens?: number;
  cacheRead?: number;
  cacheWrite?: number;
  estimatedCostUsd?: number;

  // 运行时状态
  status?: "running" | "done" | "failed" | "killed" | "timeout";
  thinkingLevel?: string;
  fastMode?: boolean;
  verboseLevel?: string;
  agentRuntimeOverride?: string;

  // 渠道信息
  channel?: string;
  groupId?: string;
  groupChannel?: string;
  space?: string;
  origin?: SessionOrigin;
  deliveryContext?: DeliveryContext;

  // Skills
  skillsSnapshot?: SessionSkillSnapshot;
  systemPromptReport?: SessionSystemPromptReport;

  // ACP
  acp?: SessionAcpMeta;

  // 插件扩展
  pluginExtensions?: Record<string, Record<string, SessionPluginJsonValue>>;
  pluginNextTurnInjections?: Record<string, SessionPluginNextTurnInjection[]>;

  // Compaction
  compactionCount?: number;
  compactionCheckpoints?: SessionCompactionCheckpoint[];
  memoryFlushAt?: number;
  memoryFlushCompactionCount?: number;

  // 子 Agent
  spawnDepth?: number;
  subagentRole?: "orchestrator" | "leaf";
  subagentControlScope?: "children" | "none";
  subagentRecovery?: SubagentRecoveryState;
};

2.2 SessionScope

1
export type SessionScope = "per-sender" | "global";

2.3 SessionOrigin

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
export type SessionOrigin = {
  label?: string;
  provider?: string;
  surface?: string;
  chatType?: SessionChatType;
  from?: string;
  to?: string;
  nativeChannelId?: string;
  nativeDirectUserId?: string;
  accountId?: string;
  threadId?: string | number;
};

三、会话 Key 系统

文件:config/sessions/session-key.ts

3.1 Key 格式

agent:{agentId}:{scope}:{id}

示例:

  • agent:main:session:abc123 — 主 Agent 会话
  • agent:main:sender:telegram:user:123 — Telegram 用户会话
  • agent:codex:acp:uuid — ACP 子 Agent 会话

3.2 Key 解析

1
2
3
4
5
6
7
8
9
export function parseAgentSessionKey(
  sessionKey: string
): { agentId: string; scope: string; id: string } | null;

export function resolveAgentIdFromSessionKey(sessionKey: string): string;

export function resolveMainSessionKey(agentId: string): string;

export function resolveAgentMainSessionKey(agentId: string): string;

四、会话存储

文件:config/sessions/store.ts

4.1 SessionStore

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
export type SessionStore = Record<string, SessionEntry>;

export function loadSessionStore(
  storePath: string,
  options?: { skipCache?: boolean }
): SessionStore;

export function updateSessionStore(
  storePath: string,
  updater: (store: SessionStore) => void
): void;

4.2 存储结构

{agentDir}/
└── sessions/
    └── {sessionKey}.json   // 每个会话一个文件

4.3 合并策略

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
export function mergeSessionEntry(
  existing: SessionEntry | undefined,
  patch: Partial<SessionEntry>
): SessionEntry;

export function mergeSessionEntryWithPolicy(
  existing: SessionEntry | undefined,
  patch: Partial<SessionEntry>,
  options?: { policy?: "touch-activity" | "preserve-activity" }
): SessionEntry;

五、Transcript 管理

文件:config/sessions/transcript.ts

5.1 Transcript 文件

每个会话关联一个 transcript 文件,记录完整的对话历史:

1
2
3
4
5
6
7
8
9
export function resolveSessionTranscriptFile(params: {
  sessionId: string;
  sessionKey: string;
  sessionEntry?: SessionEntry;
  sessionStore?: Record<string, SessionEntry>;
  storePath: string;
  agentId: string;
  threadId?: string | number;
}): Promise<{ sessionFile: string; sessionEntry: SessionEntry | undefined }>;

5.2 写入 Transcript

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// command/attempt-execution.ts
const sessionManager = SessionManager.open(sessionFile);
sessionManager.appendMessage({
  role: "user",
  content: promptText,
  timestamp: Date.now(),
});
sessionManager.appendMessage({
  role: "assistant",
  content: [{ type: "text", text: replyText }],
  api: "cli",
  provider, model,
  usage: resolveTranscriptUsage(params.assistant.usage),
  stopReason: "stop",
  timestamp: Date.now(),
});

六、会话生命周期

6.1 创建

config/sessions/lifecycle.ts

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
export function resolveSession(params: {
  cfg: OpenClawConfig;
  to?: string;
  sessionId?: string;
  sessionKey?: string;
  agentId?: string;
}) {
  // 1. 解析 Key
  // 2. 查找现有会话或创建新会话
  // 3. 返回会话元数据
}

6.2 更新

command/session-store.runtime.ts

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
export async function updateSessionStoreAfterAgentRun(params: {
  cfg: OpenClawConfig;
  sessionId, sessionKey, storePath, sessionStore,
  defaultProvider, defaultModel,
  fallbackProvider, fallbackModel,
  result,
  touchInteraction: boolean,
}) {
  // 更新 token 计数
  // 更新最后交互时间
  // 持久化到磁盘
}

6.3 重置

config/sessions/reset.ts

1
2
3
4
export function resolveSessionReset(params: {
  sessionKey: string;
  reason: "manual" | "idle" | "daily" | "compaction";
}): Promise<void>;

七、Compaction(压缩)

7.1 概念

会话过长时,通过 Compaction 将历史消息压缩为摘要,释放上下文窗口。

7.2 CompactionCheckpoint

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
export type SessionCompactionCheckpoint = {
  checkpointId: string;
  sessionKey: string;
  sessionId: string;
  createdAt: number;
  reason: SessionCompactionCheckpointReason;
  tokensBefore?: number;
  tokensAfter?: number;
  summary?: string;
  firstKeptEntryId?: string;
  preCompaction: SessionCompactionTranscriptReference;
  postCompaction: SessionCompactionTranscriptReference;
};

7.3 触发条件

条件说明
manual用户手动触发
auto-thresholdToken 超过阈值
overflow-retry上下文溢出重试
timeout-retry超时重试

八、ACP 会话

8.1 SessionAcpMeta

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
export type SessionAcpMeta = {
  backend: string;
  agent: string;
  runtimeSessionName: string;
  identity?: SessionAcpIdentity;
  mode: "persistent" | "oneshot";
  runtimeOptions?: AcpSessionRuntimeOptions;
  cwd?: string;
  state: "idle" | "running" | "error";
  lastActivityAt: number;
  lastError?: string;
};

8.2 ACP Identity

1
2
3
4
5
6
7
8
export type SessionAcpIdentity = {
  state: "pending" | "resolved";
  acpxRecordId?: string;
  acpxSessionId?: string;
  agentSessionId?: string;
  source: "ensure" | "status" | "event";
  lastUpdatedAt: number;
};

九、插件扩展

9.1 Plugin Extensions

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
export type SessionPluginExtension = {
  pluginId: string;
  namespace: string;
  value: SessionPluginJsonValue;
};

// 存储结构
pluginExtensions: {
  [pluginId]: {
    [namespace]: value
  }
}

9.2 Next Turn Injection

1
2
3
4
5
6
7
8
export type SessionPluginNextTurnInjection = {
  id: string;
  pluginId: string;
  text: string;
  placement: "prepend_context" | "append_context";
  ttlMs?: number;
  createdAt: number;
};

十、设计权衡

10.1 持久化策略

OpenClaw 采用每会话单文件策略:

  • 避免单一大文件锁竞争
  • 支持按会话独立清理
  • 简化备份和迁移

10.2 Activity 合并策略

1
2
// "touch-activity" — 更新 interaction 时间
// "preserve-activity" — 保留原有 interaction 时间

用于处理并发更新时的是否覆盖问题。

10.3 Subagent 继承

子 Agent 会话继承父会话的:

  • spawnedWorkspaceDir — 工作目录复用
  • spawnDepth — 深度计数
  • subagentRole — 角色分配

下一步

下一篇:08 - 记忆系统:Memory Architecture,深入长期记忆与上下文注入。


OpenClaw 源码剖析系列 · 2026 · skyseraph

SkySeraph
SkySeraph
AI for All & All for AI
留言 Comments