0

ch-07

约 15000 字DraftOpenClaw Book

第 7 章 · 会话管理

每个模型都有"上下文窗口"——它能同时看到的最大信息量。就像人的短期记忆一样,超过容量就会"忘事"。OpenClaw 的会话管理系统,就是让 Agent 在有限记忆中保持连贯性的"记忆术"。

本章你将学到:

  • 理解会话的完整生命周期——从创建到重置到清理
  • 掌握上下文窗口管理策略——压缩、剪裁、手动控制
  • 学会会话调试和状态恢复的实用技巧

7.1 上下文:模型"看到"的一切

在理解会话管理之前,你需要先理解"上下文"(Context)这个概念。

"上下文"就是 OpenClaw 发送给模型的所有内容。它由三部分组成:

┌───────────────────────────────────────────────┐
│                系统提示词                      │
│  ├── 工具列表 + 工具 Schema(JSON 定义)       │
│  ├── 技能列表(名称 + 描述)                    │
│  ├── 工作空间位置                              │
│  ├── 时间信息(UTC + 用户本地时间)              │
│  ├── 运行时元信息(主机/OS/模型/思考模式)      │
│  └── 注入的工作空间文件(Project Context)       │
├───────────────────────────────────────────────┤
│              对话历史                          │
│  ├── 用户消息 + 助手消息                      │
│  ├── 工具调用 + 工具结果                     │
│  └── 附件(图片/音频/文件)                    │
├───────────────────────────────────────────────┤
│              压缩摘要(如果有的话)               │
└───────────────────────────────────────────────┘

关键区别:上下文 ≠ 记忆。上下文是"模型当前看到的",记忆是"存在磁盘上的"。上下文满了,OpenClaw 会压缩(Compaction);但压缩后的摘要仍然占上下文空间。真正的"释放"只有重置会话(/new)才能做到。

7.1.1 上下文检查命令

OpenClaw 提供了几条命令让你随时检查上下文使用情况:

/status         → 快速查看"上下文有多满"
/context list    → 查看注入的工作空间文件及大小
/context detail → 更详细的上下文分解(含工具 Schema 大小)
/usage tokens   → 在回复中追加 Token 用量统计

/context list 示例输出:

🧠 Context breakdown
System prompt (run): 38,412 chars (~9,603 tok)

Injected workspace files:
- AGENTS.md: 1,742 chars (~436 tok)
- SOUL.md: 912 chars (~228 tok)
- TOOLS.md: TRUNCATED | raw 54,210 chars → injected 20,962 chars
- IDENTITY.md: 211 chars (~53 tok)
- USER.md: 388 chars (~97 tok)

Skills list: 2,184 chars (~546 tok) (12 skills)
Tool schemas (JSON): 31,988 chars (~7,997 tok)

Session tokens (cached): 14,250 total / ctx=200,000

这个输出告诉你:当前会话的上下文占了约 14K token,总容量 200K。上下文越满,留给对话的空间就越少,也越接近触发自动压缩。

7.2 会话生命周期

7.2.1 会话的创建

OpenClaw 为每个对话场景创建独立的会话。会话用"会话键"(Session Key)来标识:

直接聊天(DM):
  agent:<agentId>:<mainKey>
  例如:agent:main:main

群聊:
  agent:<agentId>:<channel>:group:<id>
  例如:agent:main:discord:group:123456

Cron 任务:
  cron:<job.id>

Webhook:
  hook:<uuid>

Node 会话:
  node-<nodeId>

7.2.2 DM 隔离模式(安全配置)

默认情况下,所有 DM 共享同一个"主会话"。这在单用户场景下没问题,但在多用户场景下是安全隐患:

⚠️ 安全警告

如果你的 Agent 可以接收多个用户的 DM,强烈建议启用 DM 隔离。否则 Alice 的私人对话内容可能通过共享的会话泄露给 Bob。

{
  session: {
    dmScope: "per-channel-peer",  // 按通道 + 发送者隔离
  },
}

dmScope 的可选值:

行为 适用场景
main 所有 DM 共享一个会话 单用户(默认)
per-peer 按发送者隔离 多用户、单通道
per-channel-peer 按通道 + 发送者隔离(推荐) 多用户、多通道
per-account-channel-peer 按账户 + 通道 + 发送者隔离 多账户收件箱

如果同一个人在多个渠道联系你,可以用 identityLinks 把它们合并到一个会话:

{
  session: {
    identityLinks: {
      alice: ["telegram:123456789", "discord:987654321012345678"],
    },
  },
}

7.2.3 会话重置

会话不是无限长的。OpenClaw 提供了多种重置策略:

按时间重置(默认):

{
  session: {
    reset: {
      mode: "daily",    // 每天 4:00 AM 重置
      atHour: 4,
      idleMinutes: 120, // 或者空闲 2 小时后重置(两者先到先得)
    },
  },
}

按类型定制重置:

{
  session: {
    resetByType: {
      thread: { mode: "daily", atHour: 4 },
      direct: { mode: "idle", idleMinutes: 240 },
      group: { mode: "idle", idleMinutes: 120 },
    },
    resetByChannel: {
      discord: { mode: "idle", idleMinutes: 10080 },  // Discord 群聊 7 天后重置
    },
  },
}

手动重置:

/new             # 开始新会话
/new sonnet      # 开始新会话并切换到 Sonnet 模型
/reset          # 等同于 /new

重置时,当前的对话历史会被"封存"(不是删除),下次消息到来时创建新的会话 ID。

7.2.4 会话存储维护

长时间运行后,会话数据会越来越大。OpenClaw 内置了维护机制:

{
  session: {
    maintenance: {
      mode: "enforce",       // "warn" 只报告不执行
      pruneAfter: "30d",    // 30 天前的会话自动清理
      maxEntries: 500,       // 最多保留 500 个会话条目
      rotateBytes: "10mb",   // sessions.json 文件超过 10MB 时轮转
    },
  },
}

清理顺序

  1. 清理超过 pruneAfter 的过期会话
  2. 限制会话数量不超过 maxEntries
  3. 归档已删除会话的 JSONL 转录文件
  4. 清理旧的归档文件
# 预览会清理什么
openclaw sessions cleanup --dry-run

# 执行清理
openclaw sessions cleanup --enforce

7.3 上下文压缩(Compaction):长对话的"记忆整理术"

当对话历史接近上下文窗口上限时,OpenClaw 会自动触发压缩——把早期的对话总结成一个摘要,释放空间给新消息。

7.3.1 压缩的工作原理

压缩前:
┌─────────────────────────────────────────────────┐
│ [消息1] [消息2] ... [消息50] [消息51] ... [消息100] │
│                                              │
│              ↑ 这些会占用大量上下文空间             │
└─────────────────────────────────────────────────┘

压缩后:
┌─────────────────────────────────────────────────┐
│ [压缩摘要:前50条消息的精炼总结]                  │
│ [消息51] ... [消息100]                         │
│                                              │
│              ↑ 摘要只占一小部分空间               │
└─────────────────────────────────────────────────┘

压缩是持久化的——摘要被写入 JSONL 转录文件。未来的请求会使用"压缩摘要 + 最近消息"作为上下文。

7.3.2 自动压缩

默认开启。当会话接近或超过上下文窗口时自动触发。

🧹 Auto-compaction complete

7.3.3 手动压缩

你可以在任何时候手动触发压缩:

/compact                              # 使用默认策略压缩
/compact 重点关注决策和未解决的问题  # 带自定义指令的压缩

7.3.4 指定压缩模型

如果你用了一个较小的模型做日常对话,可以用更强的模型来做压缩摘要:

{
  agents: {
    defaults: {
      compaction: {
        model: "anthropic/claude-sonnet-4-6",  // 用 Sonnet 做压缩
      },
    },
  },
}

7.3.5 压缩前的自动记忆刷盘

上一章提到过,OpenClaw 在压缩之前会自动触发记忆刷盘,让 Agent 把重要信息写入磁盘。这个机制确保压缩不会丢失关键信息。

7.3.6 OpenAI 服务端压缩

如果你用的是 OpenAI 的 Responses 模型,OpenClaw 还支持让 OpenAI 服务端做压缩——这是和 OpenClaw 本地压缩并行运行的两道防线。

7.4 会话剪裁(Session Pruning):减少工具结果的体积

剪裁和压缩是两个不同的机制:

压缩(Compaction) 剪裁(Pruning)
作用对象 所有消息 仅工具结果
持久性 写入 JSONL 转录 仅内存中,不写磁盘
触发方式 上下文接近上限 按 TTL(时间)触发
效果 生成摘要 截断或清除旧工具结果

7.4.1 为什么需要剪裁

Agent 在工作中会频繁调用工具——读文件、执行命令、搜索网页。这些工具结果会累积在上下文中,占据大量空间。剪裁专门清理这些"过期的工具结果",释放空间。

7.4.2 TTL 模式

剪裁基于"缓存 TTL"——当上次 Anthropic API 调用超过 TTL 时间后,下次请求时会触发剪裁:

{
  agents: {
    defaults: {
      contextPruning: {
        mode: "cache-ttl",
        ttl: "5m",  // 5 分钟后触发剪裁
      },
    },
  },
}

推荐配置ttl 应该和模型的 cacheRetention 匹配。如果你用的是 Anthropic 的 cacheRetention: "short"(5 分钟),TTL 设为 5m 最合适。

7.4.3 软剪裁 vs 硬清除

软剪裁(Soft Trim):
  保留工具结果的头和尾,中间替换为 "..."
  适合:太大的工具输出,你可能还需要部分信息

硬清除(Hard Clear):
  整个工具结果替换为 "[Old tool result content cleared]"
  适合:已经完全不需要的旧工具结果
{
  agents: {
    defaults: {
      contextPruning: {
        mode: "cache-ttl",
        softTrimRatio: 0.3,    // 超过 30% 的空间被剪裁时触发软剪裁
        hardClearRatio: 0.5,   // 超过 50% 时触发硬清除
        softTrim: {
          maxChars: 4000,
          headChars: 1500,
          tailChars: 1500,
        },
      },
    },
  },
}

7.4.4 限制剪裁范围

你可以指定只剪裁特定工具的结果:

{
  agents: {
    defaults: {
      contextPruning: {
        mode: "cache-ttl",
        tools: {
          allow: ["exec", "read"],  // 只剪裁这两个工具的结果
          deny: ["*image*"],      // 永远不剪裁包含图片的结果
        },
      },
    },
  },
}

7.4.5 受保护的内容

剪裁不会影响以下内容:

  • 用户消息和助手消息(永远不会被修改)
  • 最近 keepLastAssistants 条助手消息之后的工具结果(默认 3 条)
  • 包含图片块的工具结果

7.5 流式输出:让回复"实时"显示

没有人愿意盯着空白屏幕等 30 秒才看到回复。OpenClaw 的流式输出(Streaming)让 Agent 的回复逐步显示在聊天中。

7.5.1 两层流式架构

OpenClaw 有两个独立的流式层:

模型输出
└─ text_delta/events
   ├─ 分块流(Block Streaming)
   │   └─ 把文本分成块,逐块发送为独立消息
   └─ 预览流(Preview Streaming)
       └─ 在 Telegram/Discord/Slack 中实时编辑一条临时消息

分块流:Agent 生成过程中,每积累一定字数就发送一条消息。这样用户可以看到"进度"。

预览流:先发送一条临时消息,然后在生成过程中不断编辑更新这条消息的内容。生成完成后替换为最终回复。

7.5.2 启用分块流

{
  agents: {
    defaults: {
      blockStreamingDefault: "on",         // 全局启用
      blockStreamingBreak: "text_end",      // 遇到句子结尾就发送
      blockStreamingChunk: {
        minChars: 100,                      // 最少积累 100 字符再发送
        maxChars: 2000,                     // 最多 2000 字符就发送
        breakPreference: "paragraph",       // 优先在段落边界分割
      },
    },
  },
}

7.5.3 人类感延迟

分块流可以加入随机延迟,让多段回复更像人类在"打字":

{
  agents: {
    defaults: {
      humanDelay: "natural",  // 800-2500ms 的随机延迟
    },
  },
}

7.5.4 预览流配置

不同通道支持不同的预览模式:

通道 off partial block progress
Telegram 支持 支持 支持 映射为 partial
Discord 支持 支持 支持 映射为 partial
Slack 支持 支持 支持 支持
{
  channels: {
    telegram: {
      streaming: "partial",  // 实时编辑预览消息
    },
    discord: {
      streaming: "block",     // 分块追加预览
    },
  },
}

⚠️ 注意:分块流和预览流可以同时启用,但如果你为通道启用了分块流(*.blockStreaming: true),预览流会被跳过(避免重复发送)。

7.6 会话工具:跨会话操作

Agent 可以通过工具操作会话——查看会话列表、获取历史、跨会话发送消息。

7.6.1 会话可见性

{
  tools: {
    sessions: {
      visibility: "tree",  // "self" | "tree" | "agent" | "all"
    },
  },
}
可见范围
self 仅当前会话
tree 当前会话 + 当前会话派生的子 Agent 会话
agent 同一 Agent 的所有会话
all 所有 Agent 的所有会话

在沙盒环境中,可见性会被进一步限制为 tree(只能看到自己派生的子 Agent)。

7.6.2 会话工具的使用场景

场景 1:Agent 查看自己最近在做什么

用户:总结一下今天所有的对话
Agent:[调用 sessions_list 查看所有活跃会话]
Agent:你今天和我聊了 3 个话题...

场景 2:Agent 跨会话传递信息

用户:告诉 Discord 群里的人,我改了 API 的端点
Agent:[调用 sessions_send 向 Discord 群会发送消息]
Agent:已经通知了 Discord 群。

场景 3:Agent 派生子任务

用户:帮我同时查一下这三个项目的状态
Agent:[调用 sessions_spawn 派生 3 个子 Agent 并行查询]
Agent:查询完成,三个项目的状态如下...

7.7 发送策略:控制消息投递

你可以基于会话类型限制消息的投递:

{
  session: {
    sendPolicy: {
      rules: [
        { action: "deny", match: { channel: "discord", chatType: "group" } },
        { action: "deny", match: { keyPrefix: "cron:" } },
      ],
      default: "allow",
    },
  },
}

运行时覆盖(仅限会话所有者):

/send on       # 允许当前会话发送消息
/send off      # 禁止当前会话发送消息
/send inherit # 继承配置中的规则

7.8 实用调试技巧

7.8.1 查看会话状态

/status         → 查看当前会话的模型、上下文使用量、压缩次数
/context list    → 查看上下文组成和大小
/compact        → 手动压缩,释放空间
/new            → 重新开始
/stop           → 中止当前运行,清除排队中的后续任务

7.8.2 查看会话列表

# 列出所有会话
openclaw sessions --json

# 只看最近 60 分钟活跃的会话
openclaw sessions --active 60

7.8.3 手动管理会话

# 查看会话存储路径和最近会话
openclaw status

# 清理过期会话(预览)
openclaw sessions cleanup --dry-run

# 清理过期会话(执行)
openclaw sessions cleanup --enforce

7.8.4 Token 用量追踪

/usage tokens   → 在每条回复末尾追加 Token 用量

这对于监控成本和优化上下文管理非常有用。

💡 提示/usage tokens 是实时监控成本的最佳方式。开启后你可以在每条回复中看到精确的 Token 消耗,快速识别哪些对话在"烧钱"。

本章小结

  • 上下文是模型"看到"的一切,包括系统提示词、对话历史、工具结果
  • 会话按场景隔离(DM、群聊、Cron、Webhook 各有独立键)
  • 多用户场景务必设置 dmScope 隔离 DM 会话,防止隐私泄露
  • 压缩(Compaction)把旧对话总结为摘要,持久化到 JSONL
  • 剪裁(Pruning)清除旧的工具结果,只在内存中生效,不写磁盘
  • TTL 模式的剪裁与 Prompt Caching 配合使用效果最佳
  • 分块流和预览流让用户实时看到 Agent 的回复进度
  • /status/context/compact 是会话调试的三件套

下一步

Agent 有了"记忆术"(会话管理),接下来要让它"能做事"——这就是工具体系的范畴。下一章深入实践 OpenClaw 的工具、技能和插件三层扩展体系,让你的 Agent 从"能聊天"进化到"能干活"。