04 上下文压缩与摘要策略
学习目标
本章要解决什么问题:当对话历史超过上下文窗口限制时,Cline 如何截断和压缩?如何避免模型重复读取同一文件?
前置知识
本章涉及上下文管理的通用原理,建议先阅读:
下文假设你已理解截断、压缩、摘要策略,直接聚焦 Cline 的具体实现。
项目实践
ContextManager 架构
ContextManager 负责管理对话历史的截断和压缩。核心方法:
ContextManager ├── getNextTruncationRange() → 计算截断范围 ├── handleContextWindowExceeded() → 上下文超限处理 └── summarizeContext() → 触发摘要生成quarter 截断策略
当上下文窗口使用超过阈值时,Cline 默认使用 “quarter” 策略:截掉最后 1/4 的对话历史。
PreCompact Hook 的作用:在截断前运行,允许外部脚本保存重要上下文到外部存储,避免被截断丢失。
summarize_task 工具
当截断后仍然超出上下文窗口时,Cline 调用 summarize_task 工具,让 LLM 本身生成对话摘要。这比简单截断保留更多信息,因为摘要浓缩了多轮对话的关键内容。
文件读取去重
TaskState.fileReadCache 防止模型重复读取同一文件:
- 每次
read_file工具执行后,记录文件路径到缓存 - 当模型再次请求读取同一文件时,检查缓存并拒绝
- 缓存具有滑动窗口过期机制,一段时间后允许重新读取
设计启示:模型经常因为”忘记已读过”而重复读取文件,浪费 Token 和延迟。简单的去重缓存可以消除这种无意义的调用。
环境变量并发加载
loadContext 方法并行处理多个数据源:
@mention解析(文件引用、URL)- 斜杠命令处理
- 环境变量(可见文件、打开标签页、终端输出、最近修改文件)
优化效果:串行加载需要 2-3 秒,并发后降低到 500ms 以内。
问题与规避
| 陷阱 | 场景 | 对策 |
|---|---|---|
| 截断丢失关键信息 | 正在讨论的代码被截断 | PreCompact Hook 允许保存重要上下文 |
| 摘要质量差 | 低能力模型生成的摘要不准确 | 仅在截断后仍然超出时才触发摘要 |
| 去重缓存过期慢 | 文件更新后仍被拒绝读取 | 滑动窗口过期,超时后允许重新读取 |
设计取舍
quarter 截断 vs 固定条数截断
| 方案 | 优势 | 代价 |
|---|---|---|
| quarter(百分比) | 适配不同上下文窗口大小 | 大窗口时截断过多 |
| 固定条数(如移除最后 10 条) | 实现简单 | 不同模型的上下文窗口差异大 |
Cline 选择 quarter 百分比策略,因为 40+ 提供商的上下文窗口从 8K 到 200K 不等,百分比策略能自适应。
Hook-first 截断 vs 硬编码
| 方案 | 优势 | 代价 |
|---|---|---|
| Hook-first | 用户可自定义保存逻辑 | 增加系统复杂度 |
| 硬编码 | 实现简单 | 无法扩展 |
Cline 选择 Hook-first,允许用户在截断前注入自定义逻辑。
参考来源
- Cline 源码:
apps/vscode/src/core/context/context-management/ContextManager.ts - Cline 源码:
apps/vscode/src/core/task/index.ts(loadContext 方法)