Claude Code 源码解析
返回目录
扩展与集成13

内存与上下文

四级压缩管道

核心洞察

渐进式压缩策略在保留关键上下文的同时,将 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 级上下文压缩策略,从轻量到重量级逐步释放上下文空间:

  1. snipCompact — 激进的旧消息移除(需启用 HISTORY_SNIP 特性标志)。直接删除最早的对话消息,释放大量空间但丢失历史上下文
  2. microCompact — 轻量级工具结果裁剪(始终运行)。保留工具调用记录但截断过长的输出结果,如大文件内容、长日志输出
  3. contextCollapse — 读时投影压缩(需启用 CONTEXT_COLLAPSE 特性标志)。不修改实际消息,而是在读取时动态折叠不活跃的消息段
  4. 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 多层加载

企业级
组织强制规范
用户级
~/.claude/CLAUDE.md
项目级
./CLAUDE.md

MEMORY.md + 内存类型系统

user
feedback
project
reference

findRelevantMemories() 语义搜索

四级压缩级联

L1 snipCompact
L2 microCompact
L3 contextCollapse
L4 autoCompact

压缩后恢复

sessionMemoryCompact → extractMemories → MEMORY.md 持久化

MAX_FILES_TO_RESTORE=5 | TOKEN_BUDGET=50000

源码

3 个关键代码示例

01
MEMORY.md 系统与内存类型
TypeScript
// 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);
}
02
四级压缩级联实现
TypeScript
// 压缩后恢复常量
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
  };
}
03
会话内存提取与持久化
TypeScript
// 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 预算限制),确保代理能继续当前工作。摘要包含关键决策、文件修改记录和待完成任务。

相关源文件

memdir/services/compact/services/SessionMemory/