Appearance
6.2 会话状态管理
Agent 用了很久之后,transcript 会非常长。@pi/coding-agent 用 SessionManager 管理会话的持久化与结构,并实现**上下文压缩(compaction)**来应对 token 上限。这一节讲清楚"会话文件"与"压缩"两个核心机制。
会话 = 一个追加式日志文件
SessionManager(core/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关键步骤:
- 算 token:
estimateTokens/calculateContextTokens估算当前占用。 - 找截断点:
findCutPoint选择"从哪里开始保留",尽量在回合边界切,避免把半个回合截断。 - 生成摘要:
generateBranchSummary让 LLM 把被压缩的部分总结为一段CompactionSummaryEntry。 - 重建:用
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 内置工具。