上下文管理实战:窗口守卫、历史卫生、压缩与快照
这一章只讲“怎么做出来”:把长会话做成可恢复而不是“越聊越炸”。
对应源码入口(可选)
src/agents/pi-embedded-runner/run.tssrc/agents/pi-embedded-runner/run/attempt.tssrc/agents/pi-embedded-runner/history.tssrc/agents/context-window-guard.tssrc/agents/session-transcript-repair.tssrc/agents/pi-embedded-runner/run/compaction-timeout.tssrc/agents/pi-embedded-subscribe.ts
你要复刻的最小能力
- 历史消息不会无限增长把模型窗口撑爆。
tool_call/tool_result永远成对(避免 provider 400)。- 超窗时系统能自动压缩/恢复,不直接崩。
- 压缩中超时时,仍能返回可用快照。
真实执行顺序(按链路拆开写)
1) run 层先做窗口守卫(阻断“明知会炸”的调用)
目标:窗口低于硬下限时直接阻断,避免进入昂贵的 attempt。
对照概念页:上下文 / 压缩与 Compaction。
2) attempt 进入 session 前做转录卫生(顺序很关键)
最容易出错的是顺序:先清洗与校验 → 再截断 → 再修复配对。
为什么最后还要“修复配对”:截断可能会删掉某个 tool_call,但留下 tool_result,导致孤儿消息。
3) subscribe 暴露“压缩重试可等待”语义
在 attempt 返回“完成”之前,应该能等待 compaction 重试稳定(避免半压缩状态被当作最终结果)。
4) 溢出恢复固定为:压缩 → 截断 → 可读错误
建议把恢复做成固定顺序,并限制次数:
- 有限次压缩重试
- 仍失败则裁剪超大工具结果(降损)
- 最后返回可读错误(提示 reset 或换更大窗口模型)
5) 压缩超时必须选快照(不要返回脏状态)
压缩中超时属于“可预期失败”,应优先回退到压缩前快照,而不是把当前半压缩 transcript 返给用户。
失败场景与排障入口
- provider 400(多见于 tool 配对问题):优先检查
tool_call/tool_result是否成对;参见 工具系统 与 会话整理与修剪。 - 长会话突然变慢/频繁压缩:检查窗口守卫阈值与压缩触发;用 日志 观察是否出现 compaction 重试与超时。
验收清单(建议你做压力测试)
- 同一会话跑很多轮后,messages 增长仍受控。
- 任意
tool_call都能找到匹配的tool_result。 - 手工注入超长
tool_result,系统会触发压缩或截断恢复。 - 压缩开始/结束能被观测(日志或事件)。
- 压缩中超时仍能返回一致快照。
- 窗口过小会在 run 前被阻断,不进入昂贵调用。