上下文压缩
wescode 中的上下文管理与压缩机制——CognitiveSettlement、SessionState 与机械压缩。
为什么需要压缩
长对话面临三重限制:
| 限制 | 说明 |
|---|---|
| 物理窗口 | 模型的最大 context window(如 128K tokens) |
| 经济预算 | 输入 token 计费,超长上下文成本高 |
| 认知效率 | 信息过多时模型注意力分散 |
压缩目标
完整对话历史(可能 500K+ tokens)
→ 压缩为 SessionState(结构化摘要)
→ 保留关键事实和决策
→ 丢弃冗余细节(如工具的原始输出)
CognitiveSettlement
CognitiveSettlement 是 Run 生命周期的强制终态(INV-CTX-40)——每次 Run 结束时必须执行。
四类认知产物
| 产物 | 说明 |
|---|---|
| SessionState | 对话历史的结构化摘要 |
| Memory Entries | 值得记住的事实和偏好 |
| Reflections | 对执行过程的反思 |
| Learnings | 从错误中学到的教训 |
不变量
| 编号 | 规则 |
|---|---|
| INV-CTX-40 | Run 终态必须包含 SessionState |
| INV-CTX-41 | SessionState 由主对话模型产出(不用辅助模型) |
| INV-CTX-42 | Settlement 产出不可见于用户(不 emit StreamDelta) |
| INV-CTX-43 | SessionState 是下一轮 Context 的唯一历史来源 |
唯一例外:进程级崩溃 panic_stop。
SessionState
SessionState 是对话历史的结构化摘要,是下一轮 Context 的唯一历史来源(INV-CTX-43)。
SessionState 内容
{
"summary": "用户正在重构 UserService,已完成接口分析...",
"key_facts": [
"UserService 有 12 个公开方法",
"CRUD 操作需要拆分到 UserRepository"
],
"decisions": [
"使用 Repository 模式分离数据访问"
],
"plan": {
"status": "active",
"steps": [
{"description": "分析接口", "status": "done"},
{"description": "拆分逻辑", "status": "in_progress"}
]
},
"open_questions": []
}
Plan 是 SessionState 的子字段(INV-CTX-46)
Plan 状态不独立持久化,随 CognitiveSettlement 一同存储。
MidRunCompression vs EndOfRunSettlement
两者使用相同的 SessionState schema(INV-CTX-45):
| 维度 | MidRunCompression | EndOfRunSettlement |
|---|---|---|
| 触发时机 | Run 中途 token 接近上限 | Run 结束 |
| 目的 | 腾出空间继续执行 | 持久化认知成果 |
| 产物 | SessionState | SessionState + Memory + Reflections + Learnings |
| 频率 | 长对话可能多次 | 每 Run 一次 |
MidRunHandoff 流程
Run 中途,token 接近上限
→ MidRunCompression 触发
→ 生成 SessionState(中间版本)
→ 丢弃旧消息
→ 用 SessionState 替代历史
→ Run 继续执行
机械压缩 Fallback
信息保全优先级(INV-CTX-44)
首选:认知交班(MidRunHandoff / CognitiveSettlement)
→ LLM 做认知判断,理解重要性
Fallback:机械压缩
→ 丢弃前 ExtractToolEvidence 注入 Pinned system message
→ 确定性操作,不做 LLM 判断
ExtractToolEvidence
在丢弃旧消息之前,提取工具执行的关键证据:
旧消息中的工具调用
→ ExtractToolEvidence
→ 提取关键输入/输出/结论
→ 注入 Pinned system message
→ 然后丢弃旧消息
这确保即使在机械压缩场景下,关键的执行证据也不会丢失。
Settlement 不可见于用户
INV-CTX-42
CognitiveSettlement 的 LLM 调用不通过 SSE 推送到前端:
正常 Run 中:
StreamDelta → 推送到前端 → 用户看到 AI 输出 ✅
Settlement 时:
LLM 调用 → 不推送 StreamDelta → 用户看不到 ❌
→ 不产生 assistant 气泡
→ 对用户完全透明
Settlement 由主对话模型产出
INV-CTX-41
Settlement 使用主对话模型,而非辅助模型:
✅ Settlement 使用 gpt-4o(与对话相同的模型)
❌ 不使用 gpt-4o-mini 或其他辅助模型
原因:压缩质量 = 对话模型认知质量
→ 理解对话内容的模型 = 能总结对话内容的模型
Settlement 失败处理
Settlement 尝试(最多 3 次重试)
├── 成功 → 正常 SessionState
└── 3 次失败
→ mechanicalFallback 替代
+ degraded=true
+ degraded_reason 分类常量
→ 写入 worker 日志
Settlement 失败不阻塞 Run 完成——降级是合法状态。
CompressPipeline 概述
整个压缩管线的流程:
对话进行中
├── token 接近物理窗口 → MidRunCompression
│ └── 生成中间 SessionState → 丢弃旧消息 → 继续
├── token 接近经济预算 → 同上
└── Run 结束 → EndOfRunSettlement
├── 生成最终 SessionState
├── 提取 Memory Entries
├── 记录 Reflections
├── 提取 Learnings
└── 全部持久化
物理窗口 vs 经济预算
| 概念 | 说明 |
|---|---|
| 物理窗口 | 模型的 context_window(如 128K) |
| 经济预算 | 配置的 token 使用上限(可能小于物理窗口) |
压缩在两者中较小的那个接近上限时触发。
最佳实践
- 信任 Settlement:不需要手动管理对话历史
- 声明 ContextWindow:未声明时引擎无法正确触发压缩
- 理解延续性:SessionState 确保跨 Run 的认知延续
- 关注长对话性能:极长对话可能触发多次 MidRunCompression