上下文压缩 — OpenProgram 设计

Budget Reduction + Microcompact + Snip 已实现 Context Collapse / Reactive 待实现
代码: context/engine.pycontext/budget.pycontext/microcompact.pycontext/tool_aging/context/snip.py
参考: Claude Code 压缩参考(5-tier 级联,基于逆向分析)
节点类型 🔵 对话节点 🟢 函数节点 🟡 工具节点
1. 总览:常规操作 + 5 级级联

和 Claude Code 对齐,分两类:

常规操作(每轮 LLM 调用前都做,和上下文长度无关):Budget Reduction

级联压缩(按 Tier 1→2→3→4→5 顺序升级,成本低的先做,前一级不够才上后一级):

名称做什么触发条件调 LLM破缓存状态
常规Budget Reduction截断超大单个输出单个 tool_result > 4000 字符
Tier 1Microcompact旧工具输出清理工具调用 ≥ 50 次(之后每 25 次)/ 空闲 90 分钟cache-aware 不破
Tier 2Snip删最旧几轮Tier 1 后仍超预算
Tier 3Context Collapse分段摘要Tier 2 后仍超,~90%是(每段一次)
Tier 4Auto-Compact全量摘要Tier 3 后仍超 / 用户 /compact是(一次)
Tier 5Reactive紧急压缩API 返回 413

级联逻辑:每轮 LLM 调用前,从 Tier 1 开始检查。满足条件就执行,执行完重新检查是否还超。还超就进入下一个 Tier。

注:Tier 3 和 Tier 4 在 Claude Code 逆向分析中被描述为"overlapping strategies",不一定严格级联。/compact 手动命令直接触发 Tier 4,跳过 Tier 1-3。

2. 我们独有:函数调用节点的生命周期

Claude Code 是线性对话没有 DAG,不需要这一层。我们有函数调用节点(🟢),需要额外处理。

2.1 函数运行中

触发
函数被调用,status="running"
做什么
内部子树完整展开在上下文中
可见范围
render_context 的 callers/subcalls/expose 控制
节点状态
内部视角:子节点 full。外部视角:默认 expose=io,调用方只看到输入+输出
和压缩的关系
函数运行中,内部的 🟡 工具输出照常走 Budget Reduction(§3)和 Microcompact(Tier 1)。不参与 Snip(不能删正在用的)

2.2 函数结束 → expose=io,走常规级联

触发
函数返回,status="completed"/"error"
做什么
expose=io 天然隐藏内部子树。不需要额外操作。子节点保留在 DAG 中(可查)
后续
函数节点(输入+输出)进入 §3-§4 的常规级联,和 🔵 对话节点 一视同仁。不需要单独的策略
3. 常规操作:Budget Reduction ✅ 已实现

和 Claude Code 一致。每轮 LLM 调用前都做,和上下文长度无关。

做什么
截断超大的单个 🟡 工具输出。不处理 🔵 user/assistant 消息
阈值
4000 字符(约 1000 tokens)
截断方式
保留前 2400 字符(60%)+ 后 1600 字符(40%),中间丢弃
代码
context/tool_aging/truncate.py: middle_truncatecontext/tool_aging/policy.py: MAX_TOOL_RESULT_CHARS = 4000
4. 级联压缩(Tier 1→5)

Budget Reduction 之后,按 Tier 顺序检查。前一级做完仍超阈值才上后一级。

Tier 1: Microcompact ✅ 已实现

和 Claude Code 一致。清理旧 🟡 工具输出

触发
Cache-aware: 50 次工具调用后首次,之后每 25 次。Time-based: 空闲 ~90 分钟
只处理
FileRead、Shell、Grep、Glob、WebSearch、WebFetch、FileEdit、FileWrite
两条路径
Cache-aware(Anthropic provider):通过 Context Editing API(cache_edits,beta header context-management-2025-06-27)服务端清除旧 tool_result。客户端消息不变,缓存不破。sentinel 做了字节级归一化(byte-stable),重复操作不改变已缓存内容
Time-based(其他 provider / 空闲 90 分钟):客户端替换为占位文本(~20 tokens)。会破缓存
压缩效果
每个工具输出:原始大小 → 0 tokens(cache-aware)或 ~20 tokens(time-based)
代码
context/microcompact.pycontext/rules/microcompact.py

Tier 2: Snip ✅ 已实现

和 Claude Code 一致。直接删最旧的几轮对话。不做摘要,不调 LLM。

触发
Tier 1 做完后仍超预算
删除粒度
整轮(user + assistant + 该轮所有工具调用一起删)
删多少
从最旧的开始逐轮删,每删一轮重新算 token 数,删到阈值以下为止(不是固定 N 轮)
代价
信息完全丢失,缓存前缀被破坏。但免费(不调 LLM)
函数节点
已完成的 🟢 函数节点按时间参与 Snip。运行中的不删
代码
context/snip.py

Tier 3: Context Collapse ⏳ 待实现

和 Claude Code 一致。分段 LLM 摘要,原始消息保留(可回滚)。

触发
Tier 2 Snip 后仍超(约 90%,95% 阻塞强制)
分段
5-10 轮一段,每段独立 LLM 摘要(每段约 100-300 tokens)
LLM 调用
次数 = 段数(例:30 轮分 3 段 = 3 次 LLM 调用)
可回滚
原始消息保留在 collapse store 中(读时投影 / read-time projection)

Tier 4: Auto-Compact ✅ 已实现

和 Claude Code 一致。全量 LLM 摘要,替换所有旧消息。不可回滚。

触发
Tier 3 后仍超。或用户手动 /compact(直接跳到这里)
LLM 调用
1 次(全量摘要)
摘要大小
2000-5000 tokens
保留
最近 1-2 轮完整。system prompt 从配置文件重新加载(不依赖摘要)
/compact 命令
/compact/compact 保留数据库迁移的讨论。建议在 60% 时手动做
链式压缩
压缩后继续聊,再次满了在上一次摘要基础上再压。信息逐层衰减
代码
context/engine.py: compact()

Tier 5: Reactive ⏳ 待实现

API 返回 413(prompt too long)时的紧急压缩。每轮至多触发一次。都失败则终止。

5. 执行流程
每轮 LLM 调用前:

1. Budget Reduction → 截断超大工具输出(每轮都做)

2. 级联检查(按 Tier 顺序):
   Tier 1: Microcompact 条件满足?→ 清旧工具输出
   Tier 2: 仍超阈值?→ Snip 删旧轮
   Tier 3: 仍超阈值?→ Context Collapse 分段摘要
   Tier 4: 仍超阈值?→ Auto-Compact 全量摘要

3. 调 LLM

4. 如果 API 返回 413:
   Tier 5: Reactive → 紧急压缩 → 重试
   → 失败则终止
6. 完整运行示例

场景:200K context,持续工作几小时,中间调用了 research_agent。

阶段 1(0-30%):正常对话

🔵 user🔵 assistant,Budget Reduction 检查工具输出,没超的跳过。级联都不触发。

阶段 2(30-50%):Tier 1 Microcompact 开始工作

工具调用超过 50 次。Microcompact cache-aware 首次触发,清理旧工具输出,释放约 15K tokens。之后每 25 次再清一轮。Tier 2-5 不触发。

阶段 3:调用 🟢 research_agent

函数运行中:内部 🟡 工具调用展开。Budget Reduction 截断超大输出。函数结束后 expose=io 隐藏内部子树,节点走常规级联。

阶段 4(超阈值):Tier 2 Snip 触发

Microcompact 做了还是超。Snip 删最旧 5 轮(包括已完成的函数节点),降到阈值以下。Tier 3-5 不触发。

阶段 5(极端):Tier 3/4 触发

反复 Snip 后可删的不多了。Context Collapse 分段摘要。还不够则 Auto-Compact 全量摘要。很少发生。

阶段 6:继续使用

压缩后正常对话。再次超阈值时重复级联。大模型想查旧的函数内部过程,可以通过工具读取 DAG 子节点。

7. 和 Claude Code 的对应
我们Claude Code说明
§2 函数节点生命周期我们独有(DAG + expose=io)
§3 Budget ReductionBudget Reduction一致(4000 字符阈值,60/40 截断)
Tier 1 MicrocompactTier 1 Microcompact一致。Anthropic provider 用 cache-aware path
Tier 2 SnipTier 2 Snip一致(整轮删)
Tier 3 Context CollapseTier 3 Context Collapse一致(分段摘要,可回滚)
Tier 4 Auto-Compact / /compactTier 4 Auto-Compact / /compact一致(全量摘要)
Tier 5 ReactiveTier 5 Reactive一致(413 紧急压缩)

来源:Claude Code 压缩参考(基于逆向分析,非官方文档)

8. 实施计划
步骤做什么状态代码
1Budget Reduction — 截断超大输出(§3)context/tool_aging/truncate.py
2Tier 1 Microcompact — 旧工具输出清理context/microcompact.py
3Tier 2 Snip — 删最旧几轮context/snip.py
4Tier 3 Context Collapse — 分段 LLM 摘要
5Tier 4 Auto-Compact — 全量 LLM 摘要context/engine.py
6Tier 5 Reactive — 413 紧急压缩
7/compact 命令
8/context 命令(token 分布)_cli_chat/handlers.py
9Anthropic Context Editing API 集成