上下文压缩

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-40Run 终态必须包含 SessionState
INV-CTX-41SessionState 由主对话模型产出(不用辅助模型)
INV-CTX-42Settlement 产出不可见于用户(不 emit StreamDelta)
INV-CTX-43SessionState 是下一轮 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):

维度MidRunCompressionEndOfRunSettlement
触发时机Run 中途 token 接近上限Run 结束
目的腾出空间继续执行持久化认知成果
产物SessionStateSessionState + 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 使用上限(可能小于物理窗口)

压缩在两者中较小的那个接近上限时触发。


最佳实践

  1. 信任 Settlement:不需要手动管理对话历史
  2. 声明 ContextWindow:未声明时引擎无法正确触发压缩
  3. 理解延续性:SessionState 确保跨 Run 的认知延续
  4. 关注长对话性能:极长对话可能触发多次 MidRunCompression