会话系统

一句话概括

会话系统以追加式 JSONL 转录文件管理对话持久化,并通过分层存储提供元数据管理、维护和多 agent 协调。

职责

  • 以追加式 JSONL 文件持久化对话转录(~/.openclaw/agents/<id>/sessions/<sessionId>.jsonl
  • 维护会话存储,包含元数据、模型覆盖、投递上下文和标签
  • 执行自动维护:清理过期条目、限制条目数量、轮转大文件、执行磁盘预算
  • 解析和分类会话 key(直聊/群组/频道、定时任务、子 agent、ACP、线程)
  • 追踪输入来源(外部用户 vs 跨会话 vs 内部系统)

架构图

关键源码文件

文件行数角色
src/config/sessions/store.ts1154核心存储:加载/保存(原子文件操作)、缓存(TTL)、维护(清理、限数、轮转)、文件锁、key 归一化
src/gateway/session-utils.ts875Gateway API 层:会话列表/过滤、多 agent 存储合并、标题推导、模型解析
src/config/sessions/transcript.ts159向 JSONL 转录追加 assistant 消息,发出更新事件
src/sessions/session-key-utils.ts128解析会话 key,检测 cron/subagent/ACP/线程会话,推导聊天类型
src/sessions/send-policy.ts124基于频道、聊天类型、会话 key 模式解析发送策略(允许/拒绝)
src/sessions/model-overrides.ts77对会话条目应用模型/provider 覆盖
src/sessions/input-provenance.ts80追踪消息来源:external_user、inter_session、internal_system
src/sessions/level-overrides.ts33应用 verbose 级别覆盖
src/sessions/transcript-events.ts26转录更新事件发射器
src/sessions/session-label.ts21标签验证(最大 64 字符)
src/config/sessions/disk-budget.ts会话存储磁盘预算执行
src/config/sessions/paths.ts会话文件路径解析
src/config/sessions/metadata.ts从入站消息推导元数据

数据流

入站(写入)

用户消息到达

Auto-Reply 管道处理消息

Pi Agent 运行时执行 LLM 调用

Pi SDK 向 JSONL 转录文件追加本轮内容
  (用户消息 + assistant 回复 + 工具调用 + 工具结果)

transcript.ts 追加 assistant 元数据

transcript-events.ts 发出更新事件

store.ts 记录元数据(updatedAt、deliveryContext、model 等)

出站(读取)

下一条用户消息到达

Pi Agent 运行时加载完整 JSONL 转录为 AgentMessage[]

estimateTokens() 计算历史大小

如果 history > contextWindow - reserveTokens → 触发压缩

否则:将完整历史作为输入发送给 LLM

维护(后台)

saveSessionStore() 触发维护:
  1. pruneStaleEntries() — 移除超过 maxAge 的条目
  2. capEntryCount() — 保留最近 N 条
  3. rotateSessionFile() — 轮转超过 maxSize 的文件
  4. disk-budget.ts — 执行总磁盘预算

模式:"warn"(仅记录)或 "enforce"(删除)

Token 优化影响

会话系统是最大的单一 token 消费来源(输入 token 的 60-80%):

机制Token 影响详情
完整历史加载40,000-150,000 tokens/次调用每轮加载完整 JSONL 转录。无窗口化。
追加式增长每轮线性增长每次工具调用增加约 500-2,000 tokens
无内建截断增长直到压缩触发压缩仅在约 180k tokens 时触发(200k 上下文 - 20k 预留)
历史中的工具结果每条可能很大文件读取、网页获取原样存储在转录中

优化关键洞察

JSONL 转录格式意味着每个过去的工具结果在每次 LLM 调用中都被重新发送,直到压缩。一个 5,000 tokens 的 web_fetchread 工具结果会在所有后续调用中停留在历史中。10 次这样的工具调用意味着每次调用有 50,000 tokens 的过时工具结果。

会话存储维护 vs Token 优化

存储维护(清理、限数、轮转)操作的是会话元数据,而非转录内容。它控制磁盘上有多少会话,而非每个会话转录有多大。转录大小由 src/agents/compaction.ts 中的压缩系统控制。

与其他模块的关系

  • 依赖

    • @mariozechner/pi-coding-agentSessionManagerCURRENT_SESSION_VERSION
    • config/ — 维护设置、路径
    • agents/session-write-lock.ts — 并发访问文件锁
    • channels/chat-type.ts — 聊天类型归一化
    • routing/session-key.ts — key 归一化
  • 被依赖

    • agents/pi-embedded-runner/ — 为 LLM 调用加载转录
    • auto-reply/ — 会话条目创建、模型/级别覆盖
    • gateway/ — 控制 UI 的会话列表 API
    • cron/ — 定时任务的会话定向
    • memory/ — 会话感知的记忆索引

会话 key 格式

agent:<agentId>:<channel>:<chatType>:<identifier>

示例:
  agent:default:telegram:direct:12345
  agent:default:discord:group:guild-channel
  agent:default:cron:run:job-123
  agent:default:subagent:depth-1:parent-key

检测辅助函数:

  • isCronRunSessionKey() — 匹配 cron:run: 模式
  • isSubagentSessionKey() — 匹配 subagent: 或检查深度
  • isAcpSessionKey() — 匹配 ACP 协议会话
  • getSubagentDepth() — 返回嵌套层级

我的认知盲区

  • JSONL 的精确格式以及 Pi SDK 如何读写转录行
  • disk-budget.ts 的执行策略 — 如何决定淘汰哪些会话
  • agents/session-write-lock.ts 中的文件锁实现 — 竞态条件?
  • rotateSessionFile() 的工作方式 — 是归档还是截断?
  • Gateway API 是否支持会话列表分页(影响 UI 性能)
  • session-utils.fs.ts — 文件系统操作和归档工具

相关贡献

  • 暂无

变更频率

  • store.ts:高 — 维护逻辑、归一化和并发访问处理频繁演进
  • session-utils.ts:高 — gateway API 功能(过滤、多 agent、展示)随 UI 需求扩展
  • session-key-utils.ts:中 — 新功能上线时添加新会话类型(ACP、线程)
  • transcript.ts:低 — 追加式逻辑稳定
  • send-policy.ts:低 — 策略规则很少修改