5.3 会话生命周期

生成模型:Claude Opus 4.6 (anthropic/claude-opus-4-6) Token 消耗:输入 ~250,000 tokens,输出 ~18,000 tokens(本章合计)


会话不是静态的数据容器——它有创建、活跃、过期和重置的完整生命周期。OpenClaw 通过灵活的重置策略控制会话的新旧交替,并提供运行时修补(Patch)机制让用户和 Agent 可以动态调整会话属性。本节将分析会话生命周期管理的核心源码。

5.3.1 创建与初始化:首条消息触发会话创建

OpenClaw 的会话采用懒创建(Lazy Creation)策略:不存在预先创建会话的步骤,而是在第一条消息到达时自动创建。src/auto-reply/reply/session.ts 中的 initSessionState() 函数是会话初始化的核心入口。

// src/auto-reply/reply/session.ts
export async function initSessionState(params: {
  ctx: MsgContext;
  cfg: OpenClawConfig;
  commandAuthorized: boolean;
}): Promise<SessionInitResult> {
  const { ctx, cfg } = params;
  const sessionCfg = cfg.session;

  // 1. 加载会话存储
  const storePath = resolveStorePath(sessionCfg?.store, { agentId });
  const sessionStore: Record<string, SessionEntry> = loadSessionStore(storePath);

  // 2. 解析会话键
  sessionKey = resolveSessionKey(sessionScope, sessionCtxForState, mainKey);
  const entry = sessionStore[sessionKey];

  // 3. 判断是否为新会话
  const freshEntry = entry
    ? evaluateSessionFreshness({ updatedAt: entry.updatedAt, now, policy: resetPolicy }).fresh
    : false;

  if (!isNewSession && freshEntry) {
    // 复用已有会话
    sessionId = entry.sessionId;
    systemSent = entry.systemSent ?? false;
  } else {
    // 创建新会话
    sessionId = crypto.randomUUID();
    isNewSession = true;
    systemSent = false;
  }
  // ...
}

这个流程的关键在于第 3 步的"新鲜度"判断:如果会话键在存储中不存在(entryundefined),freshEntryfalse,会话自动创建;如果存在但已过期(不再"新鲜"),同样创建新会话。

当会话被标记为新会话时,系统会重置一系列状态:

这确保新会话从一个干净的状态开始,不会继承旧会话的 Token 统计和压缩状态。

重置触发器(Reset Triggers)

用户可以通过发送特定命令(如 /new/reset)手动触发会话重置。这些命令被称为重置触发器

注意触发器匹配是大小写不敏感的(用户输入 /NEW 也能匹配),并且支持在重置命令后附加消息(如 /new 帮我写个邮件),此时会话重置后这条附加消息会作为新会话的第一条消息。

5.3.2 重置策略:每日重置(Daily Reset)与空闲重置(Idle Reset)

除了手动重置,OpenClaw 还支持两种自动重置策略。src/config/sessions/reset.ts 定义了相关类型和逻辑。

每日重置(Daily Reset)

每日重置是默认策略。它的语义是:如果会话的最后更新时间早于今天的重置时间点,则会话被视为过期。

举例:假设重置时间为凌晨 4 点:

  • 现在是 2 月 18 日上午 10 点 → 重置时间线是 2 月 18 日 04:00

  • 现在是 2 月 18 日凌晨 2 点 → 重置时间线是 2 月 17 日 04:00(因为还没过今天的 4 点)

任何在重置时间线之前最后更新的会话都被视为过期。

空闲重置(Idle Reset)

空闲重置基于会话的最后活跃时间。如果会话在指定分钟数内没有新消息,则视为过期。

新鲜度评估

evaluateSessionFreshness() 是判断会话是否过期的核心函数:

一个关键设计:两种过期条件可以同时生效。即使处于 daily 模式,如果同时配置了 idleMinutes,那么空闲超时也会触发过期。这意味着可以设置"每天凌晨 4 点重置,但如果空闲超过 2 小时也重置"这样的组合策略。

5.3.3 按类型/通道覆盖重置策略

不同类型的会话可能需要不同的重置策略。例如,DM 对话可能适合每日重置,而群组中的线程会话可能更适合短时间空闲后重置。OpenClaw 通过 resetByTyperesetByChannel 提供了细粒度的覆盖机制。

会话类型分类

resolveSessionResetType() 根据会话键和消息属性将会话分为三类:

策略解析优先级

resolveSessionResetPolicy() 按以下优先级解析重置策略:

以下配置示例展示了这种层次化的覆盖机制:

在这个配置下:

  • DM 会话:使用默认策略,每天凌晨 4 点重置

  • 线程会话:3 小时无活动后重置(由 resetByType.thread 覆盖)

  • Discord 上的所有会话:7 天无活动后重置(由 resetByChannel.discord 覆盖,优先级高于 resetByType

resolveChannelResetConfig() 负责查找通道特定的覆盖配置:

5.3.4 会话修补(Session Patch):运行时修改会话属性

会话一旦创建,其属性并非一成不变。用户可以通过 sessions.patch 协议方法在运行时修改会话的模型、思考级别、发送策略等属性,而无需重置整个会话。

src/gateway/sessions-patch.ts 中的 applySessionsPatchToStore() 函数实现了这一机制:

可修补的属性

会话修补支持以下属性:

属性
类型
说明

label

string | null

会话标签(必须唯一)

thinkingLevel

string | null

思考级别(low/medium/high/xhigh)

verboseLevel

string | null

详细输出级别

reasoningLevel

string | null

推理级别(on/off/stream)

responseUsage

string | null

用量显示(off/tokens/full)

model

string | null

模型覆盖(null 恢复默认)

sendPolicy

string | null

发送策略(allow/deny)

execHost

string | null

执行宿主(sandbox/gateway/node)

execSecurity

string | null

执行安全级别(deny/allowlist/full)

groupActivation

string | null

群组激活方式(mention/always)

每个字段都有严格的校验逻辑。以模型覆盖为例:

修补机制的一个重要特性是设置为 null 表示清除覆盖(恢复默认值),而 undefined(即 patch 中不包含该字段)表示不修改。这遵循了 JSON Patch 中常见的语义约定。

服务端会话重置

Gateway 的 sessions.reset 方法提供了服务端的完整重置能力:

重置时会保留个性化覆盖(如模型选择、思考级别),但清除对话状态(生成新的 session ID、重置 Token 计数)。这样用户在 /reset 后不需要重新配置偏好设置。


本节小结

OpenClaw 的会话生命周期管理有以下核心特点:

  1. 懒创建:会话在第一条消息到达时自动创建,无需预先初始化

  2. 双重过期机制:每日重置(按固定时间点)和空闲重置(按不活跃时长)可以同时生效,取 OR 关系

  3. 层次化覆盖resetByChannel > resetByType > reset,允许针对不同通道和会话类型设置不同策略

  4. 手动触发器/new/reset 命令提供即时重置能力,支持大小写不敏感匹配和附加消息

  5. 运行时修补sessions.patch 允许动态修改会话属性,而 sessions.reset 在重置时保留用户偏好

在下一节中,我们将分析 OpenClaw 如何在 LLM 调用前裁剪过长的上下文,以在有限的上下文窗口内保持对话质量。

Last updated