0%

Claude Code 源码解析 (11):会话持久化的架构设计

Claude Code 源码解析 (11):会话持久化的架构设计

导读: 这是 Claude Code 20 个功能特性源码解析系列的第 11 篇,深入分析会话管理系统的架构设计。


📋 目录

  1. 问题引入:为什么需要会话管理?
  2. 技术原理:会话持久化架构
  3. 设计思想:为什么这样设计
  4. 解决方案:完整实现详解
  5. OpenClaw 最佳实践
  6. 总结

问题引入:为什么需要会话管理?

痛点场景

场景 1:对话中断丢失

1
2
3
4
5
6
7
8
9
10
11
用户:"帮我分析这个项目... (长篇说明)"
AI:"好的,让我开始分析..."

网络断开 / 浏览器崩溃 / 超时

重新连接后
用户:"刚才说到哪了?"
AI:"抱歉,我没有之前的上下文..."

→ 用户需要重新说明
→ 体验极差

场景 2:多设备不同步

1
2
3
4
5
6
7
8
9
办公室电脑:
- 正在分析项目 A
- 有 50 条对话历史

家里电脑:
- 看不到办公室的对话
- 需要重新开始

→ 上下文不连续

场景 3:长周期任务

1
2
3
4
5
6
7
Day 1: "帮我重构这个项目"
AI:"好的,我先分析代码结构..."

Day 2: 继续昨天的工作
AI:"抱歉,我不记得昨天做了什么"

→ 长周期任务无法延续

核心问题

设计 AI 助手的会话管理系统时,面临以下挑战:

  1. 持久化问题

    • 如何保存会话状态?
    • 如何保证数据不丢失?
  2. 恢复问题

    • 如何快速恢复会话?
    • 如何保持上下文连续?
  3. 同步问题

    • 多设备如何同步?
    • 如何处理冲突?
  4. 隐私问题

    • 如何保护敏感信息?
    • 如何管理会话生命周期?

Claude Code 用完整的会话持久化机制解决了这些问题。


技术原理:会话持久化架构

整体架构

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
┌─────────────────────────────────────────────────────────────┐
│ 用户交互 │
│ (Web / CLI / App) │
└─────────────────────┬───────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│ Session Manager (会话管理器) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 1. 会话创建 (Create) │ │
│ │ - 生成会话 ID │ │
│ │ - 初始化状态 │ │
│ │ - 持久化存储 │ │
│ └─────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 2. 会话加载 (Load) │ │
│ │ - 从存储加载 │ │
│ │ - 恢复状态 │ │
│ │ - 验证完整性 │ │
│ └─────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 3. 会话更新 (Update) │ │
│ │ - 添加消息 │ │
│ │ - 更新状态 │ │
│ │ - 增量保存 │ │
│ └─────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 4. 会话恢复 (Resume) │ │
│ │ - 选择会话 │ │
│ │ - 恢复上下文 │ │
│ │ - 继续执行 │ │
│ └─────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 5. 会话清理 (Cleanup) │ │
│ │ - 过期删除 │ │
│ │ - 归档存储 │ │
│ │ - 隐私清理 │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│ 存储层 (Storage Layer) │
│ ├── 本地存储 (SQLite / JSON) │
│ ├── 云同步 (可选) │
│ └── 备份恢复 │
└─────────────────────────────────────────────────────────────┘

会话定义

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
interface Session {
// 基本信息
id: string;
name: string;
createdAt: Date;
updatedAt: Date;

// 状态
status: 'active' | 'paused' | 'completed' | 'archived';

// 上下文
messages: Message[];
context: SessionContext;

// 配置
config: SessionConfig;

// 元数据
metadata: {
userAgent?: string;
platform?: string;
lastIp?: string;
deviceFingerprint?: string;
};

// 统计
stats: {
messageCount: number;
tokenUsage: number;
toolCalls: number;
duration: number; // 毫秒
};
}

interface Message {
id: string;
role: 'user' | 'assistant' | 'system';
content: string;
timestamp: Date;

// 工具调用
toolCalls?: ToolCall[];
toolResults?: ToolResult[];

// 元数据
metadata?: {
model?: string;
tokens?: number;
duration?: number;
};
}

interface SessionContext {
// 工作目录
cwd: string;

// 环境变量
env: Record<string, string>;

// 工具状态
tools: Record<string, ToolState>;

// Agent 状态
agents: Record<string, AgentState>;

// 任务状态
tasks: Record<string, TaskState>;
}

会话存储

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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
class SessionStore {
private db: Database;
private cache: Map<string, Session> = new Map();

constructor(dbPath: string) {
this.db = new Database(dbPath);
this.initSchema();
}

private initSchema(): void {
this.db.exec(`
CREATE TABLE IF NOT EXISTS sessions (
id TEXT PRIMARY KEY,
name TEXT,
status TEXT,
created_at TEXT,
updated_at TEXT,
context TEXT,
config TEXT,
metadata TEXT,
stats TEXT
)
`);

this.db.exec(`
CREATE TABLE IF NOT EXISTS messages (
id TEXT PRIMARY KEY,
session_id TEXT,
role TEXT,
content TEXT,
timestamp TEXT,
tool_calls TEXT,
tool_results TEXT,
metadata TEXT,
FOREIGN KEY (session_id) REFERENCES sessions(id)
)
`);

this.db.exec(`
CREATE INDEX IF NOT EXISTS idx_messages_session
ON messages(session_id, timestamp)
`);
}

async create(session: Session): Promise<void> {
await this.db.run(`
INSERT INTO sessions (
id, name, status, created_at, updated_at,
context, config, metadata, stats
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
`, [
session.id,
session.name,
session.status,
session.createdAt.toISOString(),
session.updatedAt.toISOString(),
JSON.stringify(session.context),
JSON.stringify(session.config),
JSON.stringify(session.metadata),
JSON.stringify(session.stats),
]);

this.cache.set(session.id, session);
}

async get(sessionId: string): Promise<Session | null> {
// 先查缓存
const cached = this.cache.get(sessionId);
if (cached) {
return cached;
}

// 查数据库
const sessionRow = await this.db.get(
'SELECT * FROM sessions WHERE id = ?',
[sessionId]
);

if (!sessionRow) {
return null;
}

// 加载消息
const messages = await this.getMessages(sessionId);

// 构建会话对象
const session: Session = {
id: sessionRow.id,
name: sessionRow.name,
status: sessionRow.status,
createdAt: new Date(sessionRow.created_at),
updatedAt: new Date(sessionRow.updated_at),
context: JSON.parse(sessionRow.context),
config: JSON.parse(sessionRow.config),
metadata: JSON.parse(sessionRow.metadata),
stats: JSON.parse(sessionRow.stats),
messages,
};

this.cache.set(sessionId, session);
return session;
}

async update(session: Session): Promise<void> {
session.updatedAt = new Date();

await this.db.run(`
UPDATE sessions SET
name = ?,
status = ?,
updated_at = ?,
context = ?,
config = ?,
metadata = ?,
stats = ?
WHERE id = ?
`, [
session.name,
session.status,
session.updatedAt.toISOString(),
JSON.stringify(session.context),
JSON.stringify(session.config),
JSON.stringify(session.metadata),
JSON.stringify(session.stats),
session.id,
]);

this.cache.set(session.id, session);
}

async addMessage(sessionId: string, message: Message): Promise<void> {
await this.db.run(`
INSERT INTO messages (
id, session_id, role, content, timestamp,
tool_calls, tool_results, metadata
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
`, [
message.id,
sessionId,
message.role,
message.content,
message.timestamp.toISOString(),
JSON.stringify(message.toolCalls),
JSON.stringify(message.toolResults),
JSON.stringify(message.metadata),
]);

// 更新会话的 updatedAt
await this.db.run(`
UPDATE sessions SET updated_at = ? WHERE id = ?
`, [new Date().toISOString(), sessionId]);

// 更新缓存
const session = this.cache.get(sessionId);
if (session) {
session.messages.push(message);
session.stats.messageCount++;
}
}

private async getMessages(sessionId: string): Promise<Message[]> {
const rows = await this.db.all(
'SELECT * FROM messages WHERE session_id = ? ORDER BY timestamp ASC',
[sessionId]
);

return rows.map(row => ({
id: row.id,
role: row.role,
content: row.content,
timestamp: new Date(row.timestamp),
toolCalls: JSON.parse(row.tool_calls || '[]'),
toolResults: JSON.parse(row.tool_results || '[]'),
metadata: JSON.parse(row.metadata || '{}'),
}));
}

async list(filter?: SessionFilter): Promise<SessionSummary[]> {
let query = `
SELECT
id, name, status, created_at, updated_at,
stats
FROM sessions
`;

const params: any[] = [];
const conditions: string[] = [];

if (filter?.status) {
conditions.push('status = ?');
params.push(filter.status);
}

if (filter?.search) {
conditions.push('name LIKE ?');
params.push(`%${filter.search}%`);
}

if (filter?.startTime) {
conditions.push('created_at >= ?');
params.push(filter.startTime.toISOString());
}

if (conditions.length > 0) {
query += ' WHERE ' + conditions.join(' AND ');
}

query += ' ORDER BY updated_at DESC';

if (filter?.limit) {
query += ` LIMIT ${filter.limit}`;
}

const rows = await this.db.all(query, params);

return rows.map(row => ({
id: row.id,
name: row.name,
status: row.status,
createdAt: new Date(row.created_at),
updatedAt: new Date(row.updated_at),
messageCount: JSON.parse(row.stats).messageCount,
}));
}

async delete(sessionId: string): Promise<void> {
// 删除消息
await this.db.run('DELETE FROM messages WHERE session_id = ?', [sessionId]);

// 删除会话
await this.db.run('DELETE FROM sessions WHERE id = ?', [sessionId]);

// 清除缓存
this.cache.delete(sessionId);
}
}

interface SessionSummary {
id: string;
name: string;
status: string;
createdAt: Date;
updatedAt: Date;
messageCount: number;
}

interface SessionFilter {
status?: string;
search?: string;
startTime?: Date;
endTime?: Date;
limit?: number;
}

会话恢复

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
69
70
71
72
73
74
75
76
77
78
79
80
class SessionResumer {
private store: SessionStore;
private contextManager: ContextManager;

constructor(store: SessionStore, contextManager: ContextManager) {
this.store = store;
this.contextManager = contextManager;
}

async resume(sessionId: string, options?: ResumeOptions): Promise<Session> {
// 1. 加载会话
const session = await this.store.get(sessionId);

if (!session) {
throw new Error(`Session not found: ${sessionId}`);
}

// 2. 验证会话状态
if (session.status === 'completed') {
if (options?.force !== true) {
throw new Error('Session is completed');
}
}

// 3. 恢复上下文
await this.contextManager.restore(session.context);

// 4. 恢复工具状态
await this.restoreToolStates(session.context.tools);

// 5. 恢复任务状态
await this.restoreTaskStates(session.context.tasks);

// 6. 更新会话状态
session.status = 'active';
await this.store.update(session);

// 7. 生成恢复报告
const resumeReport = this.generateResumeReport(session);

console.log(resumeReport);

return session;
}

private async restoreToolStates(tools: Record<string, ToolState>): Promise<void> {
for (const [toolName, state] of Object.entries(tools)) {
// 恢复工具状态
// (具体实现取决于工具类型)
}
}

private async restoreTaskStates(tasks: Record<string, TaskState>): Promise<void> {
for (const [taskId, state] of Object.entries(tasks)) {
if (state.status === 'running') {
// 任务中断了,需要决定如何处理
console.log(`Task ${taskId} was interrupted`);
// 选项:继续 / 取消 / 重新开始
}
}
}

private generateResumeReport(session: Session): string {
const lastMessage = session.messages[session.messages.length - 1];

return `
会话恢复:${session.name}
─────────────────────────────────────
会话 ID: ${session.id}
创建时间:${session.createdAt.toLocaleString()}
最后更新:${session.updatedAt.toLocaleString()}
消息数量:${session.stats.messageCount}

最后一条消息:
${lastMessage?.role}: ${lastMessage?.content?.substring(0, 100)}...

准备继续...
`.trim();
}
}

会话历史

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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
class SessionHistory {
private store: SessionStore;
private maxHistory: number;

constructor(store: SessionStore, maxHistory: number = 100) {
this.store = store;
this.maxHistory = maxHistory;
}

// 获取会话历史
async getHistory(options?: HistoryOptions): Promise<SessionSummary[]> {
return await this.store.list({
status: options?.status,
search: options?.search,
limit: options?.limit || this.maxHistory,
});
}

// 搜索会话
async search(query: string): Promise<SessionSummary[]> {
// 全文搜索
const sessions = await this.store.list({
search: query,
limit: 50,
});

// 按相关性排序
return sessions.sort((a, b) => {
const scoreA = this.relevanceScore(a, query);
const scoreB = this.relevanceScore(b, query);
return scoreB - scoreA;
});
}

// 清理过期会话
async cleanup(maxAge: number): Promise<number> {
const cutoff = new Date(Date.now() - maxAge);
const sessions = await this.store.list();

let deleted = 0;

for (const session of sessions) {
if (session.updatedAt < cutoff) {
// 归档或删除
if (session.status === 'completed') {
await this.store.delete(session.id);
deleted++;
} else {
// 归档活跃会话
await this.archive(session.id);
}
}
}

return deleted;
}

// 导出会话
async export(sessionId: string, format: 'json' | 'markdown' | 'html'): Promise<string> {
const session = await this.store.get(sessionId);

if (!session) {
throw new Error(`Session not found: ${sessionId}`);
}

switch (format) {
case 'json':
return JSON.stringify(session, null, 2);

case 'markdown':
return this.exportMarkdown(session);

case 'html':
return this.exportHtml(session);

default:
throw new Error(`Unknown format: ${format}`);
}
}

private exportMarkdown(session: Session): string {
let md = `# ${session.name}\n\n`;
md += `**会话 ID:** ${session.id}\n`;
md += `**创建时间:** ${session.createdAt.toLocaleString()}\n`;
md += `**消息数量:** ${session.stats.messageCount}\n\n`;
md += `---\n\n`;

for (const msg of session.messages) {
const role = msg.role === 'user' ? '👤 用户' : '🤖 助手';
md += `### ${role} - ${msg.timestamp.toLocaleString()}\n\n`;
md += `${msg.content}\n\n`;

if (msg.toolCalls?.length) {
md += `**工具调用:**\n`;
for (const call of msg.toolCalls) {
md += `- ${call.name}(${JSON.stringify(call.args)})\n`;
}
md += `\n`;
}
}

return md;
}

private exportHtml(session: Session): string {
// 生成 HTML 报告
return `
<!DOCTYPE html>
<html>
<head>
<title>${session.name}</title>
<style>
.message { margin: 10px 0; padding: 10px; border-radius: 5px; }
.user { background: #e3f2fd; }
.assistant { background: #f5f5f5; }
.timestamp { color: #666; font-size: 12px; }
</style>
</head>
<body>
<h1>${session.name}</h1>
<p>会话 ID: ${session.id}</p>
<p>创建时间:${session.createdAt.toLocaleString()}</p>
<hr>
${session.messages.map(msg => `
<div class="message ${msg.role}">
<div class="timestamp">${msg.timestamp.toLocaleString()}</div>
<div>${this.escapeHtml(msg.content)}</div>
</div>
`).join('')}
</body>
</html>
`.trim();
}

private escapeHtml(text: string): string {
return text
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
}

private relevanceScore(session: SessionSummary, query: string): number {
// 简单的 relevance 评分
let score = 0;

if (session.name.toLowerCase().includes(query.toLowerCase())) {
score += 10;
}

// 最近的会话权重更高
const daysSinceUpdate = (Date.now() - session.updatedAt.getTime()) / 86400000;
score += Math.max(0, 30 - daysSinceUpdate);

return score;
}
}

interface HistoryOptions {
status?: string;
search?: string;
limit?: number;
}

设计思想:为什么这样设计

思想 1:增量持久化

问题: 每次保存整个会话效率低。

解决: 增量保存。

1
2
3
4
5
6
7
8
9
10
// 全量保存 (低效)
async save(session: Session): void {
await db.save('sessions', session); // 保存整个对象
}

// 增量保存 (高效)
async addMessage(sessionId: string, message: Message): void {
await db.insert('messages', message); // 只保存新消息
await db.update('sessions', { updatedAt: new Date() });
}

性能对比:

操作 全量保存 增量保存 提升
添加消息 50ms 5ms 10 倍
更新状态 30ms 3ms 10 倍

思想 2:懒加载

问题: 加载大量会话历史慢。

解决: 懒加载 + 分页。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 懒加载会话列表
async listSessions(page: number = 1, limit: number = 20): Promise<SessionSummary[]> {
const offset = (page - 1) * limit;
return await db.query(
'SELECT * FROM sessions ORDER BY updated_at DESC LIMIT ? OFFSET ?',
[limit, offset]
);
}

// 按需加载完整会话
async getSession(sessionId: string): Promise<Session> {
// 只加载基本信息
const session = await db.get('SELECT * FROM sessions WHERE id = ?', [sessionId]);

// 需要消息时才加载
session.getMessages = async () => {
return await db.query('SELECT * FROM messages WHERE session_id = ?', [sessionId]);
};

return session;
}

思想 3:自动恢复

问题: 用户需要手动找到上次会话。

解决: 自动恢复最近会话。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class AutoResume {
async findLastActive(): Promise<Session | null> {
const sessions = await this.store.list({
status: 'active',
limit: 1,
});

if (sessions.length === 0) {
return null;
}

return await this.store.get(sessions[0].id);
}

async autoResume(): Promise<void> {
const session = await this.findLastActive();

if (session) {
console.log(`自动恢复会话:${session.name}`);
await this.resumer.resume(session.id);
}
}
}

思想 4:隐私保护

问题: 会话可能包含敏感信息。

解决: 加密存储 + 自动清理。

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
class SecureSessionStore extends SessionStore {
private encryptionKey: Buffer;

async create(session: Session): Promise<void> {
// 加密敏感字段
const encrypted = {
...session,
context: this.encrypt(session.context),
messages: session.messages.map(m => ({
...m,
content: this.encrypt(m.content),
})),
};

await super.create(encrypted);
}

async get(sessionId: string): Promise<Session> {
const encrypted = await super.get(sessionId);

// 解密
return {
...encrypted,
context: this.decrypt(encrypted.context),
messages: encrypted.messages.map(m => ({
...m,
content: this.decrypt(m.content),
})),
};
}

// 自动清理过期会话
async autoCleanup(maxAge: number): Promise<void> {
const deleted = await this.cleanup(maxAge);
console.log(`Cleaned up ${deleted} expired sessions`);
}
}

思想 5:云同步

问题: 多设备会话不同步。

解决: 可选云同步。

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
class CloudSync {
private localStore: SessionStore;
private cloudClient: CloudClient;

async sync(): Promise<void> {
// 1. 获取本地变更
const localChanges = await this.getLocalChanges();

// 2. 获取云端变更
const cloudChanges = await this.cloudClient.getChanges();

// 3. 合并变更
const merged = this.mergeChanges(localChanges, cloudChanges);

// 4. 应用变更
await this.applyChanges(merged);

// 5. 更新同步时间
await this.updateLastSyncTime();
}

private mergeChanges(local: Change[], cloud: Change[]): Change[] {
// 冲突解决策略:最近写入优先
const allChanges = [...local, ...cloud];

return allChanges.sort((a, b) =>
b.timestamp - a.timestamp
);
}
}

解决方案:完整实现详解

SessionTool 实现

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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
export class SessionTool extends Tool {
name = 'session';
description = '管理会话 (创建/恢复/列表/导出)';

inputSchema = {
type: 'object',
properties: {
action: {
type: 'string',
enum: ['create', 'resume', 'list', 'export', 'delete', 'archive'],
description: '操作类型',
},
sessionId: {
type: 'string',
description: '会话 ID',
},
name: {
type: 'string',
description: '会话名称',
},
format: {
type: 'string',
enum: ['json', 'markdown', 'html'],
description: '导出格式',
},
},
required: ['action'],
};

private store: SessionStore;
private resumer: SessionResumer;
private history: SessionHistory;

async execute(input: SessionInput, context: ToolContext): Promise<ToolResult> {
switch (input.action) {
case 'create':
return this.create(input.name);

case 'resume':
return this.resume(input.sessionId!);

case 'list':
return this.list();

case 'export':
return this.export(input.sessionId!, input.format || 'json');

case 'delete':
return this.delete(input.sessionId!);

case 'archive':
return this.archive(input.sessionId!);

default:
return {
success: false,
error: `Unknown action: ${input.action}`,
};
}
}

private async create(name?: string): Promise<ToolResult> {
const session: Session = {
id: generateSessionId(),
name: name || `Session ${new Date().toLocaleString()}`,
createdAt: new Date(),
updatedAt: new Date(),
status: 'active',
messages: [],
context: this.createDefaultContext(),
config: this.createDefaultConfig(),
metadata: {},
stats: {
messageCount: 0,
tokenUsage: 0,
toolCalls: 0,
duration: 0,
},
};

await this.store.create(session);

return {
success: true,
sessionId: session.id,
message: `会话已创建:${session.name}`,
};
}

private async resume(sessionId: string): Promise<ToolResult> {
try {
const session = await this.resumer.resume(sessionId);

return {
success: true,
session: {
id: session.id,
name: session.name,
messageCount: session.stats.messageCount,
lastUpdate: session.updatedAt,
},
};
} catch (error) {
return {
success: false,
error: error.message,
};
}
}

private async list(): Promise<ToolResult> {
const sessions = await this.history.getHistory({ limit: 20 });

return {
success: true,
sessions: sessions.map(s => ({
id: s.id,
name: s.name,
status: s.status,
messageCount: s.messageCount,
lastUpdate: s.updatedAt,
})),
total: sessions.length,
};
}

private async export(sessionId: string, format: string): Promise<ToolResult> {
try {
const content = await this.history.export(sessionId, format as any);

return {
success: true,
content,
format,
};
} catch (error) {
return {
success: false,
error: error.message,
};
}
}

private async delete(sessionId: string): Promise<ToolResult> {
await this.store.delete(sessionId);

return {
success: true,
message: `会话已删除:${sessionId}`,
};
}
}

会话配置

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
# ~/.openclaw/config/sessions.yaml

# 存储配置
storage:
# 数据库路径
db_path: ~/.openclaw/data/sessions.db

# 缓存大小
cache_size: 100

# 加密
encryption:
enabled: true
key_path: ~/.openclaw/keys/session.key

# 恢复配置
resume:
# 自动恢复最近会话
auto_resume: true

# 恢复时显示摘要
show_summary: true

# 历史配置
history:
# 最大历史记录数
max_history: 100

# 搜索配置
search:
enabled: true
full_text: true

# 清理配置
cleanup:
# 自动清理
auto_cleanup: true

# 清理间隔 (小时)
interval: 24

# 会话保留天数
retention_days: 90

# 归档配置
archive:
enabled: true
path: ~/.openclaw/archives

# 同步配置 (可选)
sync:
enabled: false
provider: 'webdav' # webdav / icloud / custom
webdav_url: https://your-webdav.com
sync_interval: 300 # 5 分钟

OpenClaw 最佳实践

实践 1:创建会话

1
2
3
4
5
6
# 创建新会话
openclaw run session --action create --name "项目分析"

# 输出:
会话已创建:项目分析
会话 ID: session-20260403-001

实践 2:恢复会话

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
# 列出会话
openclaw run session --action list

# 输出:
会话列表 (共 5 个):

┌───────────────────────────────────────────┐
│ ID │ 名称 │ 消息 │ 最后更新 │
├───────────────────────────────────────────┤
│ session-001 │ 项目分析 │ 50 │ 10 分钟前│
│ session-002 │ 代码审查 │ 30 │ 1 小时前 │
│ session-003 │ bug 修复 │ 25 │ 2 小时前 │
└───────────────────────────────────────────┘

# 恢复会话
openclaw run session --action resume --sessionId session-001

# 输出:
会话恢复:项目分析
─────────────────────────────────────
会话 ID: session-001
创建时间:2026-04-03 10:00:00
最后更新:2026-04-03 20:50:00
消息数量:50

最后一条消息:
🤖 助手:让我继续分析项目结构...

准备继续...

实践 3:导出会话

1
2
3
4
5
6
7
8
9
10
11
# 导出为 Markdown
openclaw run session --action export \
--sessionId session-001 \
--format markdown \
--output session-report.md

# 导出为 HTML
openclaw run session --action export \
--sessionId session-001 \
--format html \
--output session-report.html

实践 4:搜索会话

1
2
3
4
5
6
7
8
9
10
11
# 搜索会话
openclaw run session --action search --query "项目"

# 输出:
搜索结果:

1. 项目分析 (session-001)
50 条消息,最后更新:10 分钟前

2. 项目部署 (session-005)
20 条消息,最后更新:昨天

实践 5:自动清理

1
2
3
4
5
6
# 清理 90 天前的会话
openclaw run session --action cleanup --maxAge 90d

# 输出:
已清理 15 个过期会话
已归档 3 个活跃会话

总结

核心要点

  1. 增量持久化 - 只保存变更,提升性能
  2. 懒加载 - 按需加载,减少内存
  3. 自动恢复 - 无缝继续上次工作
  4. 隐私保护 - 加密存储 + 自动清理
  5. 云同步 - 多设备会话连续

设计智慧

好的会话管理让用户”永不丢失上下文”。

Claude Code 的会话管理设计告诉我们:

  • 持久化保证数据安全
  • 快速恢复提升体验
  • 隐私保护建立信任
  • 云同步实现连续性

会话生命周期

1
创建 → 活跃 → (暂停) → 恢复 → 完成 → 归档 → 清理

下一步

  • 实现会话存储
  • 添加自动恢复
  • 配置加密存储
  • 启用云同步 (可选)

系列文章:

  • [1] Bash 命令执行的安全艺术 (已发布)
  • [2] 差异编辑的设计艺术 (已发布)
  • [3] 文件搜索的底层原理 (已发布)
  • [4] 多 Agent 协作的架构设计 (已发布)
  • [5] 技能系统的设计哲学 (已发布)
  • [6] MCP 协议集成的完整指南 (已发布)
  • [7] 后台任务管理的完整方案 (已发布)
  • [8] Web 抓取的 SSRF 防护设计 (已发布)
  • [9] 多层权限决策引擎设计 (已发布)
  • [10] 插件生命周期的设计智慧 (已发布)
  • [11] 会话持久化的架构设计 (本文)
  • [12+] 更多高级功能 (继续中…)

进度:11/N (Phase 2 开始)

上一篇: Claude Code 源码解析 (10):插件生命周期的设计智慧


关于作者: John,OpenClaw 平台开发者,专注 AI 助手架构设计与实现。