Skip to content

6.2 会话状态管理

Agent 用了很久之后,transcript 会非常长。@pi/coding-agentSessionManager 管理会话的持久化与结构,并实现**上下文压缩(compaction)**来应对 token 上限。这一节讲清楚"会话文件"与"压缩"两个核心机制。

会话 = 一个追加式日志文件

SessionManagercore/session-manager.ts)把一次会话存成一个追加式条目文件。每个条目都继承自:

ts
interface SessionEntryBase {
	type: string;
	id: string;
	parentId: string | null;   // 支持分支(fork)
	timestamp: string;
}

常见的条目类型:

ts
SessionMessageEntry        // { type: "message", message: AgentMessage }
ThinkingLevelChangeEntry   // { type: "thinking_level_change", thinkingLevel }
ModelChangeEntry           // { type: "model_change", provider, modelId }
CompactionEntry            // { type: "compaction", summary, firstKeptEntryId, tokensBefore }
BranchSummaryEntry         // { type: "branch_summary", summary, ... }

为什么是追加式日志?

  • 天然可恢复:上次中断后重读整个文件即可还原会话。
  • 天然可审计:每一条消息、每一次模型切换都有记录。
  • 天然可分支:parentId 支持把一个会话 fork 出多个分支(比如"换个思路重试")。

会话的恢复与迁移

  • SessionManager 从磁盘读条目文件,buildSessionContext() 还原出 AgentMessage[] 和模型/思考档位。
  • migrateSessionEntries() 处理旧版本会话文件的迁移(CURRENT_SESSION_VERSION = 3)。
  • 会话文件本身带 version,保证向后兼容。

上下文压缩(Compaction)

当 transcript 超过上下文窗口,直接把全部消息发给模型会爆。pi 采用压缩策略:把早期对话总结成摘要,释放上下文空间。

core/compaction/ 实现了完整的压缩管线:

shouldCompact(transcript)          // 判断是否需要压缩
  → 是
collectEntriesForBranchSummary()   // 收集要被压缩的条目
prepareBranchEntries()             // 准备分支条目
generateBranchSummary()            // 调用 LLM 生成摘要
findCutPoint() / findTurnStartIndex()  // 找到安全的截断点
compact()                          // 用摘要 + 保留的尾部重建 transcript

关键步骤:

  1. 算 tokenestimateTokens / calculateContextTokens 估算当前占用。
  2. 找截断点findCutPoint 选择"从哪里开始保留",尽量在回合边界切,避免把半个回合截断。
  3. 生成摘要generateBranchSummary 让 LLM 把被压缩的部分总结为一段 CompactionSummaryEntry
  4. 重建:用 summary + 保留的尾部 替换掉早期消息,作为新的会话上下文。
ts
// 压缩后:早期消息被一条摘要替换
[
	{ type: "compaction", summary: "用户要求重构 utils 模块,已完成 read 工具改造…", ... },
	...近期的完整消息
]

DEFAULT_COMPACTION_SETTINGS 提供默认阈值(如触发 token 比例)。压缩会把被压缩内容持久化(存进 compaction 条目),保证后续还能追溯。

分支(Branch)与分支摘要

  • 分支parentId 让会话可 fork。SessionManager 维护一棵会话树(SessionTreeNode)。
  • 分支摘要BranchSummaryEntry 记录某个分支被压缩时的摘要,用于"切换分支"时的上下文重建。

CANNED 与恢复的配合

  • 压缩前:collectEntriesForBranchSummary 确定要压缩的边界。
  • 压缩后:getLatestCompactionEntry 拿最新摘要并入上下文。
  • 恢复时优先用摘要,避免重新加载全部历史。

小结

  • 会话 = 追加式日志文件(SessionMessageEntry 等 + parentId 支持分支)。
  • 恢复 = 重读文件 + buildSessionContext + 版本迁移。
  • 压缩 = 总结早期对话 + 找截断点 + 重建上下文,解决 token 上限。
  • 分支摘要让 fork 出来的会话也能独立压缩与恢复。
真实源码位置
  • 条目类型:packages/coding-agent/src/core/session-manager.ts:32-156
  • 会话树:同文件 :159
  • 压缩管线:packages/coding-agent/src/core/compaction/

下一步:6.3 内置工具