跳转到内容

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 方法)