内存与上下文
四级压缩管道
渐进式压缩策略在保留关键上下文的同时,将 token 消耗控制在预算内
学习
概念讲解与核心设计分析
13.1 CLAUDE.md 多层加载
Claude Code 的内存系统从多个位置加载 CLAUDE.md 文件,形成分层的上下文配置体系:
- 项目级 — 项目根目录的
CLAUDE.md,包含项目特定的指令、架构说明和编码规范 - 用户级 —
~/.claude/CLAUDE.md,包含用户个人偏好和通用指令 - 企业级 — 组织管理员配置的 CLAUDE.md,强制所有成员遵循的规范
多层 CLAUDE.md 按优先级合并,企业级覆盖用户级,项目级具有最高优先级用于项目特定配置。
13.2 MEMORY.md 系统
memdir/memdir.ts 实现了 MEMORY.md 内存系统,用于持久化跨会话的关键信息:
ENTRYPOINT_NAME— 入口文件名标识MAX_ENTRYPOINT_LINES = 200— 入口文件最大行数限制MAX_ENTRYPOINT_BYTES = 25000— 入口文件最大字节数限制truncateEntrypointContent— 当内容超出限制时的截断策略,保留最重要的信息
13.3 内存类型分类
系统将内存分为四种类型,每种类型有不同的生命周期和用途:
- user — 用户级内存,跨项目持久化的个人偏好和习惯
- feedback — 反馈内存,来自用户纠正和指导的学习记录
- project — 项目内存,项目特定的上下文信息和决策记录
- reference — 参考内存,外部文档和知识的引用
findRelevantMemories.ts 通过语义搜索在海量内存中找到与当前任务最相关的记忆片段。
13.4 四级压缩级联
Claude Code 实现了 4 级上下文压缩策略,从轻量到重量级逐步释放上下文空间:
- snipCompact — 激进的旧消息移除(需启用 HISTORY_SNIP 特性标志)。直接删除最早的对话消息,释放大量空间但丢失历史上下文
- microCompact — 轻量级工具结果裁剪(始终运行)。保留工具调用记录但截断过长的输出结果,如大文件内容、长日志输出
- contextCollapse — 读时投影压缩(需启用 CONTEXT_COLLAPSE 特性标志)。不修改实际消息,而是在读取时动态折叠不活跃的消息段
- autoCompact — 完整压缩,使用分叉代理生成对话摘要。这是最彻底的压缩方式,会启动一个子代理来总结当前对话的关键信息
13.5 压缩后恢复与内存提取
compact.ts 中定义了关键常量:
POST_COMPACT_MAX_FILES_TO_RESTORE = 5— 压缩后最多恢复 5 个最近文件的上下文POST_COMPACT_TOKEN_BUDGET = 50000— 压缩后文件恢复的 Token 预算上限
sessionMemoryCompact.ts 在压缩后从对话中提取关键记忆,保存到 MEMORY.md 中,确保重要信息不会因压缩而丢失。extractMemories 服务负责智能识别值得记忆的信息片段。
架构
模块关系与设计决策
内存与上下文管理架构图
CLAUDE.md 多层加载
MEMORY.md + 内存类型系统
findRelevantMemories() 语义搜索
四级压缩级联
压缩后恢复
sessionMemoryCompact → extractMemories → MEMORY.md 持久化
MAX_FILES_TO_RESTORE=5 | TOKEN_BUDGET=50000
源码
共 3 个关键代码示例
// memdir.ts 核心常量与逻辑
const ENTRYPOINT_NAME = 'MEMORY.md';
const MAX_ENTRYPOINT_LINES = 200;
const MAX_ENTRYPOINT_BYTES = 25000;
// 截断入口内容,保留最重要的信息
function truncateEntrypointContent(
content: string
): string {
const lines = content.split('\n');
if (lines.length <= MAX_ENTRYPOINT_LINES &&
Buffer.byteLength(content) <= MAX_ENTRYPOINT_BYTES) {
return content;
}
// 优先保留最近添加的内存(文件末尾)
let truncated = lines
.slice(-MAX_ENTRYPOINT_LINES)
.join('\n');
// 字节数检查
while (Buffer.byteLength(truncated) > MAX_ENTRYPOINT_BYTES) {
const parts = truncated.split('\n');
parts.shift(); // 移除最旧的一行
truncated = parts.join('\n');
}
return truncated;
}
// 内存类型定义
type MemoryType = 'user' | 'feedback' | 'project' | 'reference';
interface MemoryEntry {
type: MemoryType;
content: string;
timestamp: number;
source: string; // 来源标识
relevanceScore?: number;
}
// findRelevantMemories: 语义搜索相关内存
async function findRelevantMemories(
query: string,
memories: MemoryEntry[],
topK: number = 10
): Promise<MemoryEntry[]> {
const scored = memories.map(m => ({
...m,
relevanceScore: computeSimilarity(query, m.content)
}));
scored.sort((a, b) =>
(b.relevanceScore || 0) - (a.relevanceScore || 0)
);
return scored.slice(0, topK);
}// 压缩后恢复常量
const POST_COMPACT_MAX_FILES_TO_RESTORE = 5;
const POST_COMPACT_TOKEN_BUDGET = 50000;
// L1: snipCompact - 激进的旧消息移除
function snipCompact(
messages: Message[],
targetTokens: number
): Message[] {
if (!isFeatureEnabled('HISTORY_SNIP')) {
return messages;
}
// 从最旧的消息开始删除
const result = [...messages];
let currentTokens = countTokens(result);
while (currentTokens > targetTokens && result.length > 2) {
result.shift();
currentTokens = countTokens(result);
}
return result;
}
// L2: microCompact - 轻量级工具结果裁剪(始终运行)
function microCompact(messages: Message[]): Message[] {
return messages.map(msg => {
if (msg.type !== 'tool_result') return msg;
// 截断过长的工具输出
if (msg.content.length > 10000) {
return {
...msg,
content: msg.content.slice(0, 5000)
+ '\n... [已截断] ...\n'
+ msg.content.slice(-2000)
};
}
return msg;
});
}
// L4: autoCompact - 使用分叉代理生成摘要
async function autoCompact(
messages: Message[],
context: CompactContext
): Promise<CompactResult> {
// 启动子代理进行摘要
const summary = await forkAgent({
prompt: '请总结以下对话的关键信息...',
messages: messages,
maxTokens: 2000
});
// 压缩后恢复最近文件
const recentFiles = getRecentlyAccessedFiles(messages)
.slice(0, POST_COMPACT_MAX_FILES_TO_RESTORE);
return {
summary: summary,
restoredFiles: recentFiles,
tokenBudget: POST_COMPACT_TOKEN_BUDGET
};
}// sessionMemoryCompact.ts: 压缩后内存提取
async function sessionMemoryCompact(
messages: Message[],
existingMemories: MemoryEntry[]
): Promise<MemoryEntry[]> {
// 使用 AI 提取值得记忆的信息
const extracted = await extractMemories(messages);
// 过滤重复内存
const newMemories = extracted.filter(
mem => !existingMemories.some(
existing => isSimilar(existing.content, mem.content)
)
);
// 写入 MEMORY.md
const memoryContent = newMemories
.map(m => formatMemoryEntry(m))
.join('\n');
await appendToMemoryFile(memoryContent);
return [...existingMemories, ...newMemories];
}
// extractMemories: 智能记忆提取服务
async function extractMemories(
messages: Message[]
): Promise<MemoryEntry[]> {
const memories: MemoryEntry[] = [];
// 提取用户纠正(feedback 类型)
for (const msg of messages) {
if (isUserCorrection(msg)) {
memories.push({
type: 'feedback',
content: msg.content,
timestamp: Date.now(),
source: 'user_correction'
});
}
}
// 提取项目发现(project 类型)
const projectInsights = await analyzeForInsights(
messages
);
for (const insight of projectInsights) {
memories.push({
type: 'project',
content: insight,
timestamp: Date.now(),
source: 'auto_extract'
});
}
return memories;
}
// CLAUDE.md 多层加载
async function loadClaudeMd(): Promise<string> {
const layers: string[] = [];
// 企业级(最低优先级)
const enterprise = await loadEnterpriseClaude();
if (enterprise) layers.push(enterprise);
// 用户级
const user = await loadFile('~/.claude/CLAUDE.md');
if (user) layers.push(user);
// 项目级(最高优先级)
const project = await loadFile('./CLAUDE.md');
if (project) layers.push(project);
return layers.join('\n---\n');
}互动
步进式流程演示
上下文压缩交互式演练
Step 1: 上下文空间监控
系统持续监控当前上下文窗口的 Token 使用量。当使用量接近模型上下文窗口限制时(如 200K tokens 中已使用 180K),触发压缩流程。
// Token 监控
currentTokens: 182,400 / 200,000
usage: 91.2% // 超过阈值,触发压缩
Step 2: 级联压缩执行
压缩按级联顺序执行。microCompact 始终运行,截断过长的工具输出。如果仍然空间不足,根据特性标志决定是否启用 snipCompact 和 contextCollapse。最后才使用 autoCompact 进行完整压缩。
L2 microCompact: 182K → 165K tokens // 截断工具输出
L3 contextCollapse: 165K → 140K tokens // 折叠不活跃段
L4 autoCompact: 140K → 45K tokens // AI 摘要压缩
Step 3: 内存提取与保存
压缩过程中,sessionMemoryCompact 从即将被压缩的消息中提取关键记忆。用户的纠正被标记为 feedback 类型,项目发现被标记为 project 类型,确保重要信息不丢失。
Step 4: 压缩后恢复
autoCompact 完成后,系统恢复最近 5 个文件的上下文(受 50000 Token 预算限制),确保代理能继续当前工作。摘要包含关键决策、文件修改记录和待完成任务。