第04课:三层上下文压缩机制(Claude Code核心亮点)
一、前言
上一节课我们讲了上下文管理的基础策略(截断、优先级排序),但这些策略有一个明显的缺陷:简单截断会丢失有用信息,而完全保留又会导致Token超限。
Claude Code的核心亮点之一,就是“三层上下文压缩机制”——它不是简单的“删减”,而是“信息提纯”,在保证Agent能记住关键信息的前提下,将上下文压缩到合理长度,兼顾性能和效果。
本节课我们深入拆解这三层压缩的设计逻辑、实现细节,以及触发机制,这也是工业级Agent与普通Agent的核心区别之一。
二、三层压缩的核心设计思想
核心原则:从浅到深、从快到慢、从无损耗到有损耗,优先使用低成本、无损耗的压缩方式,只有在必要时才使用高成本、有损耗的压缩,最大限度保证信息完整性和性能。
三层压缩的触发顺序:微压缩 → 会话压缩 → 全量压缩,只有当前一层压缩无法将Token降到阈值以下时,才触发下一层。
触发阈值:Token占用达到93%(可配置),由TokenTracker监控触发。
三、第一层:微压缩(Micro Compact)—— 本地快速压缩(无API调用)
1. 核心特点
- 无API调用:纯本地字符串处理,速度最快(毫秒级);
- 无信息损耗:只截断冗余的工具结果,不修改核心信息;
- 适用场景:上下文轻度膨胀,主要是工具结果过多导致Token超限。
2. 实现逻辑(Claude Code源码解析)
微压缩的核心类是com.claudecode.core.compact.MicroCompact,其逻辑是“按时间和数量截断工具结果”:
public class MicroCompact implements Compressor {
// 保留最近的工具结果数量(默认6个)
private static final int MAX_TOOL_RESULTS = 6;
// 时间阈值(默认10分钟),超过该时间的工具结果只保留2个
private static final long TIME_THRESHOLD = 10 * 60 * 1000; // 10分钟
@Override
public CompactionResult compact(ConversationMemory memory) {
List<ToolResult> toolResults = memory.getToolResults();
int originalSize = toolResults.size();
// 1. 按时间筛选:超过10分钟的工具结果,只保留最近2个
List<ToolResult> recentResults = new ArrayList<>();
long currentTime = System.currentTimeMillis();
for (ToolResult result : toolResults) {
if (currentTime - result.getTimestamp() <= TIME_THRESHOLD) {
recentResults.add(result);
}
}
// 超过10分钟的结果,保留最近2个
if (recentResults.size() < 2) {
recentResults.addAll(toolResults.subList(Math.max(0, toolResults.size() - 2), toolResults.size()));
}
// 2. 按数量筛选:保留最近6个工具结果
if (recentResults.size() > MAX_TOOL_RESULTS) {
recentResults = recentResults.subList(recentResults.size() - MAX_TOOL_RESULTS, recentResults.size());
}
// 3. 更新记忆中的工具结果
memory.setToolResults(recentResults);
// 计算压缩效果
int compressedSize = recentResults.size();
int reducedCount = originalSize - compressedSize;
return new CompactionResult("微压缩完成", reducedCount, false);
}
}
3. 关键设计点
- 时间感知:近期的工具结果更可能被后续推理使用,所以保留更多;远期结果只保留少量核心;
- 只压缩工具结果:不触碰系统提示词、用户目标、历史对话,保证核心信息不丢失;
- 无API调用:避免因压缩增加LLM成本和延迟。
四、第二层:会话压缩(Session Memory Compact)—— AI摘要压缩(1次API调用)
1. 核心特点
- 有API调用:调用LLM对历史对话和工具结果进行摘要,中等速度(秒级);
- 低信息损耗:LLM会提炼核心信息,删除冗余描述,保留关键数据;
- 适用场景:微压缩后Token仍超限,上下文包含大量冗余的历史对话和工具结果。
2. 实现逻辑(Claude Code源码解析)
会话压缩的核心类是com.claudecode.core.compact.SessionMemoryCompact,其逻辑是“让LLM生成上下文摘要”:
public class SessionMemoryCompact implements Compressor {
// 摘要后的Token范围(10K-40K)
private static final int MIN_SUMMARY_TOKENS = 10000;
private static final int MAX_SUMMARY_TOKENS = 40000;
// LLM客户端(用于生成摘要)
private final LlmClient llmClient;
@Override
public CompactionResult compact(ConversationMemory memory) {
// 1. 提取需要压缩的内容(历史对话+工具结果)
String contentToCompact = buildContentToCompact(memory);
int originalTokens = tokenizer.countTokens(contentToCompact);
// 2. 调用LLM生成摘要(提示词工程很关键)
String prompt = buildSummaryPrompt(contentToCompact);
String summary = llmClient.generateSummary(prompt);
// 3. 校验摘要长度,确保在合理范围
int summaryTokens = tokenizer.countTokens(summary);
if (summaryTokens < MIN_SUMMARY_TOKENS) {
summary = addDetails(summary, memory); // 补充细节
} else if (summaryTokens > MAX_SUMMARY_TOKENS) {
summary = truncateSummary(summary); // 进一步截断
}
// 4. 更新记忆:用摘要替换原始历史对话和工具结果
memory.setHistory(List.of(summary));
memory.setToolResults(List.of()); // 工具结果已融入摘要
// 计算压缩效果
int reducedTokens = originalTokens - summaryTokens;
return new CompactionResult("会话压缩完成", reducedTokens, true);
}
// 构建需要压缩的内容
private String buildContentToCompact(ConversationMemory memory) {
StringBuilder content = new StringBuilder();
content.append("历史对话:").append(memory.getHistory()).append("\n");
content.append("工具结果:").append(memory.getToolResults()).append("\n");
return content.toString();
}
// 构建摘要提示词(关键:告诉LLM如何提炼核心信息)
private String buildSummaryPrompt(String content) {
return "请你作为AI助手,对以下内容进行摘要,要求:" +
"1. 保留用户的核心目标和所有关键工具结果数据;" +
"2. 删除冗余的对话和重复的描述;" +
"3. 保持逻辑连贯,让后续推理能基于摘要继续推进任务;" +
"4. 摘要长度控制在10K-40K Token之间。" +
"需要摘要的内容:" + content;
}
}
3. 关键设计点
- 提示词工程:摘要的质量取决于提示词,必须明确告诉LLM“保留什么、删除什么”;
- 摘要范围控制:避免摘要过短(丢失信息)或过长(压缩无效);
- 核心信息保留:系统提示词和用户目标不参与压缩,只压缩历史对话和工具结果。
五、第三层:全量压缩(Full Compact)—— 兜底压缩(多次API调用)
1. 核心特点
- 多次API调用:对整个上下文(除系统提示词)进行全量重写,速度最慢(数十秒);
- 一定信息损耗:优先保留用户目标和核心工具结果,删除所有非必要信息;
- 适用场景:会话压缩后Token仍超限,属于极端情况(如长时间运行、大量工具调用)。
2. 实现逻辑(Claude Code源码解析)
全量压缩的核心类是com.claudecode.core.compact.FullCompact,其逻辑是“按API轮次分组,逐步丢弃非核心信息”:
public class FullCompact implements Compressor {
// 熔断器:连续3次压缩失败,停止压缩(防止无限调用API)
private static final int MAX_RETRY = 3;
private final LlmClient llmClient;
private int retryCount = 0;
@Override
public CompactionResult compact(ConversationMemory memory) {
if (retryCount >= MAX_RETRY) {
return new CompactionResult("全量压缩失败(熔断器触发)", 0, false);
}
try {
// 1. 提取全量上下文(除系统提示词)
String fullContext = buildFullContext(memory);
int originalTokens = tokenizer.countTokens(fullContext);
// 2. 按API轮次分组(每轮调用为一组)
List<String> contextGroups = groupByApiRound(fullContext);
// 3. 逐步丢弃早期非核心分组,生成全量摘要
List<String> keptGroups = new ArrayList<>();
int currentTokens = 0;
// 从后往前保留分组(近期分组更重要)
for (int i = contextGroups.size() - 1; i >= 0; i--) {
String group = contextGroups.get(i);
int groupTokens = tokenizer.countTokens(group);
if (currentTokens + groupTokens <= MAX_SUMMARY_TOKENS) {
keptGroups.add(0, group); // 插入到前面,保持顺序
currentTokens += groupTokens;
} else {
// 对当前分组进行摘要,再尝试加入
String groupSummary = llmClient.generateSummary("提炼以下内容的核心信息:" + group);
if (currentTokens + tokenizer.countTokens(groupSummary) <= MAX_SUMMARY_TOKENS) {
keptGroups.add(0, groupSummary);
currentTokens += tokenizer.countTokens(groupSummary);
} else {
break; // 无法再加入,停止保留
}
}
}
// 4. 生成最终全量摘要,更新记忆
String fullSummary = String.join("\n", keptGroups);
memory.setHistory(List.of(fullSummary));
memory.setToolResults(List.of());
// 计算压缩效果
int reducedTokens = originalTokens - tokenizer.countTokens(fullSummary);
retryCount = 0; // 重置重试次数
return new CompactionResult("全量压缩完成", reducedTokens, true);
} catch (Exception e) {
retryCount++;
return new CompactionResult("全量压缩失败,重试次数:" + retryCount, 0, false);
}
}
// 按API轮次分组(每一次LLM调用为一轮)
private List<String> groupByApiRound(String fullContext) {
// 解析上下文,按"API Round X"分组,逻辑略...
return new ArrayList<>();
}
}
3. 关键设计点
- 熔断器保护:避免因LLM异常导致多次无效API调用,浪费成本;
- 分组保留:按API轮次分组,优先保留近期分组,符合Agent的推理逻辑;
- 兜底策略:只有在前面两层压缩无效时才触发,是最后的保障。
六、三层压缩的触发流程(总结)
TokenTracker监控Token数量 → 达到93%阈值
↓
调用AutoCompactManager.autoCompactIfNeeded()
↓
1. 先调用MicroCompact(微压缩)
↓ 压缩后Token仍超限?
2. 再调用SessionMemoryCompact(会话压缩)
↓ 压缩后Token仍超限?
3. 最后调用FullCompact(全量压缩)
↓ 压缩后仍超限?
触发异常,提示用户“上下文过长,无法继续执行”
七、实操练习:实现简单的微压缩和会话压缩
结合本节课所学,实现微压缩(本地截断)和会话压缩(简化版,用固定摘要模拟LLM调用)。
public class SimpleCompactManager {
// 微压缩:保留最近3个工具结果
public List<String> microCompact(List<String> toolResults) {
if (toolResults.size() <= 3) {
return toolResults;
}
// 保留最近3个
return toolResults.subList(toolResults.size() - 3, toolResults.size());
}
// 会话压缩:简化版,模拟LLM摘要
public String sessionCompact(List<String> history, List<String> toolResults) {
// 模拟LLM摘要,提炼核心信息
StringBuilder summary = new StringBuilder();
summary.append("用户目标:帮我写一个字符串反转工具类\n");
summary.append("工具执行记录:\n");
for (String result : toolResults) {
if (result.contains("读取文件")) {
summary.append("- 读取文件成功,确认项目为Spring Boot架构\n");
} else if (result.contains("编码规范")) {
summary.append("- 检查编码规范,符合要求\n");
}
}
summary.append("历史对话:用户要求编写工具类,Agent已完成前期准备\n");
return summary.toString();
}
// 测试
public static void main(String[] args) {
SimpleCompactManager manager = new SimpleCompactManager();
// 测试微压缩
List<String> toolResults = new ArrayList<>();
toolResults.add("读取文件1成功");
toolResults.add("读取文件2成功");
toolResults.add("读取文件3成功");
toolResults.add("读取文件4成功");
List<String> compactedTools = manager.microCompact(toolResults);
System.out.println("微压缩后工具结果:" + compactedTools);
// 测试会话压缩
List<String> history = new ArrayList<>();
history.add("用户:帮我写一个字符串反转工具类");
history.add("Agent:好的,我需要先读取你的项目结构");
history.add("Agent:已读取文件,正在检查编码规范");
String summary = manager.sessionCompact(history, compactedTools);
System.out.println("\n会话压缩后摘要:" + summary);
}
}
八、本课重点总结
1. 三层压缩的核心是“从浅到深、兼顾性能和信息完整性”,避免简单截断导致的信息丢失;
2. 微压缩(本地、无损耗)→ 会话压缩(AI摘要、低损耗)→ 全量压缩(兜底、有损耗),逐步升级;
3. 关键设计点:时间感知、提示词工程
下节课预告
第05课:工具调用系统设计(Tool Calling)—— Agent的“手脚”,如何设计可扩展的工具系统,让Agent能执行文件操作、Shell命令、网络搜索等,对应tool包下的接口和实现类源码解析。
、熔断器保护,这也是Claude Code压缩机制的优势所在。
更多推荐
所有评论(0)