Contents

Claude Code 上下文管理与记忆系统源码解读

Claude Code 上下文管理与记忆系统源码解读

项目路径:/mnt/e/code/cc/claude-code-main 研究日期:2026-04-03 研究者:P5 架构分析


目录

  1. Conversation History 管理
  2. System Prompt 注入
  3. 上下文窗口优化策略
  4. 记忆持久化机制
  5. 上下文压缩与摘要
  6. Session Memory(会话记忆)
  7. CLAUDE.md 与 Memory 文件发现
  8. 缓存与性能优化
  9. 关键设计模式
  10. 总结

1. Conversation History 管理

1.1 核心文件

文件职责
src/utils/sessionStorage.ts会话转录持久化,JSONL 格式存储
src/history.ts用户 prompt 历史(↑箭头历史)
src/assistant/sessionHistory.ts远程会话历史分页
src/utils/messages.ts消息规范化、重新排序、查找构建

1.2 存储结构

会话转录 (sessionStorage.ts):

// 存储位置: {projectDir}/{sessionId}.jsonl
// 示例: ~/.claude/projects/myproject/abc123-session.jsonl

type Transcript = (
  | UserMessage
  | AssistantMessage
  | AttachmentMessage
  | SystemMessage
)[]

消息链机制:通过 parentUuid 构建对话连续性

// 消息通过 parentUuid 链式连接,支持:
// - 恢复会话时重建对话链
// - 工具调用与结果的配对
// - 进度消息(progress)不参与链式结构

1.3 对话历史持久化

文件位置: src/history.ts

Claude Code 通过 JSONL 格式将用户输入历史持久化到磁盘:

// 历史记录结构
type LogEntry = {
  display: string          // 显示文本
  pastedContents: Record<number, StoredPastedContent>  // 粘贴内容
  timestamp: number        // 时间戳
  project: string          // 项目路径
  sessionId?: string       // 会话ID
}

// 存储位置: ~/.claude/history.jsonl(跨项目共享)

关键特性:

  • 双缓冲写入: 内存缓冲 + 异步刷盘,避免阻塞主线程
  • 最多 100 条: 同一项目最多保留 100 条历史记录
  • 粘贴内容处理: 小内容内联存储,大内容哈希引用外部存储
  • 会话隔离: 当前会话记录优先显示,避免会话间历史混淆
// 历史读取顺序:当前会话 → 其他会话(保持上下文连续性)
export async function* getHistory(): AsyncGenerator<HistoryEntry> {
  const currentProject = getProjectRoot()
  const currentSession = getSessionId()
  const otherSessionEntries: LogEntry[] = []

  // 当前会话条目优先
  for await (const entry of makeLogEntryReader()) {
    if (entry.sessionId === currentSession) {
      yield await logEntryToHistoryEntry(entry)
    } else {
      otherSessionEntries.push(entry)
    }
  }
  
  // 再输出其他会话(最多 MAX_HISTORY_ITEMS=100 条)
  for (const entry of otherSessionEntries) {
    if (yielded >= MAX_HISTORY_ITEMS) return
    yield await logEntryToHistoryEntry(entry)
  }
}

1.4 远程会话历史

文件位置: src/assistant/sessionHistory.ts

支持从远程 API 分页获取历史记录:

export const HISTORY_PAGE_SIZE = 100

// 获取最新的 N 条记录(从最新往历史方向)
export async function fetchLatestEvents(
  ctx: HistoryAuthCtx,
  limit = HISTORY_PAGE_SIZE,
): Promise<HistoryPage | null> {
  return fetchPage(ctx, { limit, anchor_to_latest: true }, 'fetchLatestEvents')
}

// 分页获取更早的记录
export async function fetchOlderEvents(
  ctx: HistoryAuthCtx,
  beforeId: string,
  limit = HISTORY_PAGE_SIZE,
): Promise<HistoryPage | null> {
  return fetchPage(ctx, { limit, before_id: beforeId }, 'fetchOlderEvents')
}

// 页面结构
export type HistoryPage = {
  events: SDKMessage[]      // 消息列表
  firstId: string | null    // 最旧消息 ID(下次查询的 cursor)
  hasMore: boolean          // 是否还有更早的消息
}

2. System Prompt 注入

2.1 系统提示词构建

文件位置: src/constants/prompts.ts

Claude Code 采用分层构建策略组织系统提示词:

export async function getSystemPrompt(
  tools: Tools,
  model: string,
  additionalWorkingDirectories?: string[],
  mcpClients?: MCPServerConnection[],
): Promise<string[]>

2.2 静态与动态边界

关键设计: SYSTEM_PROMPT_DYNAMIC_BOUNDARY

// 边界标记分隔静态可缓存内容与动态内容
export const SYSTEM_PROMPT_DYNAMIC_BOUNDARY =
  '__SYSTEM_PROMPT_DYNAMIC_BOUNDARY__'

// 返回结构
return [
  // --- 静态内容(可全局缓存)---
  getSimpleIntroSection(outputStyleConfig),
  getSimpleSystemSection(),
  getSimpleDoingTasksSection(),
  getActionsSection(),
  getUsingYourToolsSection(enabledTools),
  getSimpleToneAndStyleSection(),
  getOutputEfficiencySection(),
  
  // === 边界标记 ===
  ...(shouldUseGlobalCacheScope() ? [SYSTEM_PROMPT_DYNAMIC_BOUNDARY] : []),
  
  // --- 动态内容(会话特定)---
  ...resolvedDynamicSections,  // session_guidance, memory, env_info, language 等
]

缓存优化: 边界标记前的静态内容可利用 scope: 'global' 缓存,节省 Token 开销。

2.3 动态提示词段落

文件位置: src/constants/systemPromptSections.ts

export function systemPromptSection(
  key: string,           // 段落唯一标识
  compute: () => string | Promise<string>  // 计算函数
): SystemPromptSection

动态段落包括:

段落 Key内容缓存策略
session_guidance会话特定指导(Agent 工具、Skills 等)每次计算
memory自动记忆提示(MEMORY.md 索引 + 行为指导)缓存
env_info_simple环境信息(CWD、平台、Shell 等)每次计算
language语言偏好设置缓存
output_style输出风格配置缓存
mcp_instructionsMCP 服务器指令动态检测变更
scratchpad临时工作目录说明缓存
token_budgetToken 预算指示缓存

2.4 上下文环境信息

export async function computeSimpleEnvInfo(
  modelId: string,
  additionalWorkingDirectories?: string[],
): Promise<string> {
  return `# Environment
  
You have been invoked in the following environment:
  - Primary working directory: ${cwd}
  - Is a git repository: ${isGit}
  - Additional working directories: ${additionalDirs}
  - Platform: ${env.platform}
  - Shell: ${shellName}
  - OS Version: ${unameSR}
  - Model: ${modelDescription}
  - Knowledge cutoff: ${knowledgeCutoff}`
}

2.5 System Prompt 缓存失效

文件位置: src/context.ts

// 系统提示词注入(用于缓存打破,ant-only)
let systemPromptInjection: string | null = null

export function getSystemPromptInjection(): string | null {
  return systemPromptInjection
}

export function setSystemPromptInjection(value: string | null): void {
  systemPromptInjection = value
  // 立即清除上下文缓存
  getUserContext.cache.clear?.()
  getSystemContext.cache.clear?.()
}

3. 上下文窗口优化策略

3.1 Token 计数与估算

文件位置: src/utils/tokens.ts

Claude Code 使用精确 + 估算混合策略:

/**
 * 核心函数:获取当前上下文窗口大小(Token)
 * 
 * 使用最后 API 响应的 usage 数据 + 新消息估算
 * 这是 autocompact、session memory 等功能的canonical计数方式
 */
export function tokenCountWithEstimation(messages: readonly Message[]): number {
  // 1. 找到最后一个有 usage 的消息(API 响应)
  let i = messages.length - 1
  while (i >= 0) {
    const message = messages[i]
    const usage = getTokenUsage(message)
    if (message && usage) {
      // 2. 处理并行工具调用:回溯到同一 API 响应的第一条消息
      const responseId = getAssistantMessageId(message)
      if (responseId) {
        let j = i - 1
        while (j >= 0) {
          const prior = messages[j]
          const priorId = prior ? getAssistantMessageId(prior) : undefined
          if (priorId === responseId) {
            i = j  // 锚定到同一响应的最早消息
          } else if (priorId !== undefined) {
            break  // 不同响应,停止
          }
          j--
        }
      }
      // 3. 精确 usage + 新消息估算
      return getTokenCountFromUsage(usage) + 
             roughTokenCountEstimationForMessages(messages.slice(i + 1))
    }
    i--
  }
  // 全部使用估算
  return roughTokenCountEstimationForMessages(messages)
}

// 精确计算:usage.input_tokens + cache + output_tokens
export function getTokenCountFromUsage(usage: Usage): number {
  return (
    usage.input_tokens +
    (usage.cache_creation_input_tokens ?? 0) +
    (usage.cache_read_input_tokens ?? 0) +
    usage.output_tokens
  )
}

3.2 Token 预算解析

文件位置: src/utils/tokenBudget.ts

支持用户以自然语言指定 Token 预算:

// 支持格式
"+500k"           // 简写:增加 500,000 tokens
"spend 2M tokens" // 动词形式
"use 1B tokens"    // 动词形式

// 解析正则
const SHORTHAND_START_RE = /^\s*\+(\d+(?:\.\d+)?)\s*(k|m|b)\b/i
const VERBOSE_RE = /\b(?:use|spend)\s+(\d+(?:\.\d+)?)\s*(k|m|b)\s*tokens?\b/i
const MULTIPLIERS = { k: 1_000, m: 1_000_000, b: 1_000_000_000 }

3.3 上下文窗口阈值管理

文件位置: src/services/compact/autoCompact.ts

export const AUTOCOMPACT_BUFFER_TOKENS = 13_000      // 自动压缩缓冲
export const WARNING_THRESHOLD_BUFFER_TOKENS = 20_000 // 警告阈值缓冲
export const ERROR_THRESHOLD_BUFFER_TOKENS = 20_000   // 错误阈值缓冲
export const MANUAL_COMPACT_BUFFER_TOKENS = 3_000    // 手动压缩缓冲

export function getAutoCompactThreshold(model: string): number {
  const effectiveContextWindow = getEffectiveContextWindowSize(model)
  return effectiveContextWindow - AUTOCOMPACT_BUFFER_TOKENS
}

// 有效上下文窗口 = 模型上下文上限 - 最大输出预留
export function getEffectiveContextWindowSize(model: string): number {
  const reservedTokensForSummary = Math.min(
    getMaxOutputTokensForModel(model),
    MAX_OUTPUT_TOKENS_FOR_SUMMARY  // 20,000
  )
  let contextWindow = getContextWindowForModel(model, getSdkBetas())
  
  // 支持环境变量覆盖
  const autoCompactWindow = process.env.CLAUDE_CODE_AUTO_COMPACT_WINDOW
  if (autoCompactWindow) {
    const parsed = parseInt(autoCompactWindow, 10)
    if (!isNaN(parsed) && parsed > 0) {
      contextWindow = Math.min(contextWindow, parsed)
    }
  }
  
  return contextWindow - reservedTokensForSummary
}

4. 记忆持久化机制

4.1 记忆类型分类

文件位置: src/memdir/memoryTypes.ts

Claude Code 将记忆分为四类

类型作用域用途示例
user用户信息角色、目标、知识水平“用户是数据科学家”
feedback反馈指导行为纠正、偏好确认“不要 mock 数据库”
project项目上下文进行中的工作、目标、deadline“merge freeze 从周四开始”
reference外部引用外部系统指针“Linear 项目 INGEST 跟踪 pipeline bugs”

明确排除(可从代码/git 推导的信息):

  • 代码模式、架构、文件路径
  • Git 历史、最近更改
  • 调试方案或修复方案
  • CLAUDE.md 已记录的内容
export const WHAT_NOT_TO_SAVE_SECTION = `
## What NOT to save in memory

- Code patterns, conventions, architecture, file paths, or project structure
- Git history, recent changes, or who-changed-what
- Debugging solutions or fix recipes
- Anything already documented in CLAUDE.md files
- Ephemeral task details: in-progress work, temporary state
`

4.2 记忆目录结构

文件位置: src/memdir/memdir.ts

memory/
├── MEMORY.md          # 索引文件(最多 200 行,每行 ~150 字符)
├── user_role.md       # 用户记忆
├── feedback_*.md      # 反馈记忆
├── project_*.md      # 项目记忆
└── reference_*.md    # 外部引用记忆

关键约束

  • MEMORY.md 是索引,不是记忆本身
  • 每条记忆单独一个文件
  • 记忆内容通过 frontmatter 定义元数据
# 记忆文件格式
---
name: user_role
description: 用户是数据科学家,专注于可观测性/日志
type: user
---

记忆内容正文...

**Why:** 原因说明
**How to apply:** 如何应用

4.3 记忆加载时机

文件位置: src/context.ts

export const getUserContext = memoize(
  async (): Promise<{ [k: string]: string }> => {
    // 获取 .md 文件(CLAUDE.md、MEMORY.md 等)
    const claudeMd = shouldDisableClaudeMd
      ? null
      : getClaudeMds(filterInjectedMemoryFiles(await getMemoryFiles()))
    
    return {
      ...(claudeMd && { claudeMd }),
      currentDate: `Today's date is ${getLocalISODate()}.`,
    }
  },
)

4.4 记忆自动提取

文件位置: src/services/extractMemories/extractMemories.ts

在每个查询循环结束时自动运行记忆提取子代理

async function runExtraction({ context, isTrailingRun }) {
  // 互斥检测:主代理已写入则跳过
  if (hasMemoryWritesSince(messages, lastMemoryMessageUuid)) {
    const lastMessage = messages.at(-1)
    if (lastMessage?.uuid) {
      lastMemoryMessageUuid = lastMessage.uuid
    }
    return
  }
  
  // 节流:每 N 个 eligible turns 执行一次
  if (!isTrailingRun) {
    turnsSinceLastExtraction++
    const throttle = getFeatureValue('tengu_bramble_lintel', 1)
    if (turnsSinceLastExtraction < throttle) {
      return
    }
  }
  turnsSinceLastExtraction = 0
  
  // 运行 forked agent
  const result = await runForkedAgent({
    promptMessages: [createUserMessage({ content: userPrompt })],
    cacheSafeParams,
    canUseTool,  // 仅允许记忆目录内的读写操作
    maxTurns: 5, // 最多 5 轮(防止验证陷阱)
  })
  
  // 更新光标
  const lastMessage = messages.at(-1)
  if (lastMessage?.uuid) {
    lastMemoryMessageUuid = lastMessage.uuid
  }
}

4.5 相关记忆检索

文件位置: src/memdir/findRelevantMemories.ts

通过 Sonnet 选择器从大量记忆文件中检索相关记忆:

const SELECT_MEMORIES_SYSTEM_PROMPT = `You are selecting memories that will be useful
to Claude Code as it processes a user's query. Return up to 5 relevant memories.`

export async function findRelevantMemories(
  query: string,
  memoryDir: string,
  signal: AbortSignal,
  recentTools: readonly string[] = [],
  alreadySurfaced: ReadonlySet<string> = new Set(),
): Promise<RelevantMemory[]> {
  // 1. 扫描记忆目录,获取所有记忆头部信息(最多 200 个)
  const memories = (await scanMemoryFiles(memoryDir, signal))
  
  // 2. 调用 Sonnet 选择最相关的记忆
  const selectedFilenames = await selectRelevantMemories(
    query,
    memories,
    signal,
    recentTools,
  )
  
  // 3. 返回文件路径 + mtime
  return selected.map(m => ({ path: m.filePath, mtimeMs: m.mtimeMs }))
}

工具过滤: 最近使用工具的参考文档会被排除(避免噪声)。


5. 上下文压缩与摘要

5.1 压缩触发机制

文件位置: src/services/compact/autoCompact.ts

两种触发模式:

  1. 自动压缩: 上下文达到阈值时自动触发
  2. 手动压缩: 用户执行 /compact 命令
export async function autoCompactIfNeeded(
  messages: Message[],
  toolUseContext: ToolUseContext,
  cacheSafeParams: CacheSafeParams,
): Promise<{ wasCompacted: boolean; compactionResult?: CompactionResult }> {
  // 递归守卫:避免在 session_memory 或 compact 子代理中重复触发
  if (querySource === 'session_memory' || querySource === 'compact') {
    return { wasCompacted: false }
  }
  
  // 检查是否应该压缩
  const shouldCompact = await shouldAutoCompact(messages, model, querySource)
  if (!shouldCompact) return { wasCompacted: false }
  
  // 尝试 Session Memory 压缩(轻量级)
  const sessionMemoryResult = await trySessionMemoryCompaction(...)
  if (sessionMemoryResult) return { wasCompacted: true, compactionResult: sessionMemoryResult }
  
  // 传统压缩(调用 API 生成摘要)
  const compactionResult = await compactConversation(...)
  return { wasCompacted: true, compactionResult }
}

5.2 压缩实现

文件位置: src/services/compact/compact.ts

核心流程

export async function compactConversation(
  messages: Message[],
  context: ToolUseContext,
  cacheSafeParams: CacheSafeParams,
  suppressFollowUpQuestions: boolean,
  customInstructions?: string,
  isAutoCompact: boolean = false,
): Promise<CompactionResult> {
  // 1. 执行 PreCompact Hooks
  const hookResult = await executePreCompactHooks(...)
  
  // 2. 构建压缩提示词
  const compactPrompt = getCompactPrompt(customInstructions)
  
  // 3. 调用模型生成摘要(支持缓存复用)
  const summaryResponse = await streamCompactSummary({ messages, summaryRequest, ... })
  
  // 4. 创建边界标记消息
  const boundaryMarker = createCompactBoundaryMessage(
    isAutoCompact ? 'auto' : 'manual',
    preCompactTokenCount,
    messages.at(-1)?.uuid,
  )
  
  // 5. 生成摘要消息
  const summaryMessages = [
    createUserMessage({
      content: getCompactUserSummaryMessage(summary, ...),
      isCompactSummary: true,
    })
  ]
  
  // 6. 创建附件(恢复最近访问的文件等)
  const postCompactFileAttachments = await createPostCompactFileAttachments(...)
  
  // 7. 执行 PostCompact Hooks
  await executePostCompactHooks(...)
  
  return {
    boundaryMarker,
    summaryMessages,
    attachments: postCompactFileAttachments,
    preCompactTokenCount,
    postCompactTokenCount: compactionCallTotalTokens,
    truePostCompactTokenCount,
    compactionUsage,
  }
}

5.3 图片剥离

关键优化: 压缩前剥离图片块,避免图片占用压缩 API 的上下文空间:

export function stripImagesFromMessages(messages: Message[]): Message[] {
  return messages.map(message => {
    if (message.type !== 'user') return message
    
    return {
      ...message,
      message: {
        ...message.message,
        content: content.map(block => {
          if (block.type === 'image') {
            return { type: 'text', text: '[image]' }
          }
          // 同时处理 tool_result 中的嵌套图片
          if (block.type === 'tool_result' && Array.isArray(block.content)) {
            return block.content.map(item => 
              item.type === 'image' ? { type: 'text', text: '[image]' } : item
            )
          }
          return block
        })
      }
    }
  })
}

5.4 Session Memory 压缩

文件位置: src/services/compact/sessionMemoryCompact.ts

一种轻量级压缩方案,利用已提取的 Session Memory:

export async function trySessionMemoryCompaction(
  messages: Message[],
  agentId?: AgentId,
  autoCompactThreshold?: number,
): Promise<CompactionResult | null> {
  // 1. 检查 Session Memory 是否有实际内容
  const sessionMemory = await getSessionMemoryContent()
  if (!sessionMemory || await isSessionMemoryEmpty(sessionMemory)) {
    return null
  }
  
  // 2. 计算需要保留的消息起始位置
  const lastSummarizedIndex = messages.findIndex(
    msg => msg.uuid === lastSummarizedMessageId
  )
  
  // 3. 从 lastSummarizedIndex 向前扩展,满足 minTokens 和 minTextBlockMessages
  const startIndex = calculateMessagesToKeepIndex(messages, lastSummarizedIndex)
  
  // 4. 构建 CompactionResult
  return createCompactionResultFromSessionMemory(
    messages,
    sessionMemory,
    messagesToKeep,
    hookResults,
    transcriptPath,
  )
}

5.5 消息保留策略

export const DEFAULT_SM_COMPACT_CONFIG: SessionMemoryCompactConfig = {
  minTokens: 10_000,           // 最少保留 10,000 tokens
  minTextBlockMessages: 5,     // 最少保留 5 条含文本的消息
  maxTokens: 40_000,            // 最多保留 40,000 tokens
}

export function calculateMessagesToKeepIndex(
  messages: Message[],
  lastSummarizedIndex: number,
): number {
  let startIndex = lastSummarizedIndex >= 0 
    ? lastSummarizedIndex + 1 
    : messages.length
  
  // 从后向前扩展,满足最小阈值后停止
  for (let i = startIndex - 1; i >= floor; i--) {
    totalTokens += estimateMessageTokens(msg)
    if (hasTextBlocks(msg)) textBlockMessageCount++
    
    if (totalTokens >= maxTokens) break
    if (totalTokens >= minTokens && textBlockMessageCount >= minTextBlockMessages) break
  }
  
  // 调整确保不拆分 tool_use/tool_result 对
  return adjustIndexToPreserveAPIInvariants(messages, startIndex)
}

6. Session Memory(会话记忆)

6.1 概念

Session Memory 是 Claude Code 在会话进行过程中自动维护的markdown笔记,记录当前对话的关键信息。与持久化记忆不同,Session Memory 仅服务于当前会话。

文件位置: src/services/SessionMemory/sessionMemory.ts

6.2 提取时机

export function shouldExtractMemory(messages: Message[]): boolean {
  const currentTokenCount = tokenCountWithEstimation(messages)
  
  // 检查初始化阈值
  if (!isSessionMemoryInitialized()) {
    if (!hasMetInitializationThreshold(currentTokenCount)) {
      return false
    }
    markSessionMemoryInitialized()
  }
  
  // 检查 token 增长阈值
  const hasMetTokenThreshold = hasMetUpdateThreshold(currentTokenCount)
  
  // 检查工具调用次数阈值
  const toolCallsSinceLastUpdate = countToolCallsSince(messages, lastMemoryMessageUuid)
  const hasMetToolCallThreshold = toolCallsSinceLastUpdate >= getToolCallsBetweenUpdates()
  
  // 检查最后一条 assistant 消息是否有工具调用
  const hasToolCallsInLastTurn = hasToolCallsInLastAssistantTurn(messages)
  
  // 触发条件:
  // 1. Token 阈值 AND 工具调用阈值都满足
  // 2. Token 阈值满足 AND 最后一轮没有工具调用(自然对话间隙)
  return (hasMetTokenThreshold && hasMetToolCallThreshold) ||
         (hasMetTokenThreshold && !hasToolCallsInLastTurn)
}

6.3 配置参数

export const DEFAULT_SESSION_MEMORY_CONFIG: SessionMemoryConfig = {
  minimumMessageTokensToInit: 10_000,     // 初始化最低上下文
  minimumTokensBetweenUpdate: 5_000,      // 更新间隔(Token 增长)
  toolCallsBetweenUpdates: 3,            // 更新间隔(工具调用次数)
}

6.4 提取执行

const extractSessionMemory = sequential(async function (context: REPLHookContext) {
  // 仅在主 REPL 线程执行
  if (context.querySource !== 'repl_main_thread') return
  
  if (!shouldExtractMemory(messages)) return
  
  // 设置会话记忆文件
  const { memoryPath, currentMemory } = await setupSessionMemoryFile(toolUseContext)
  
  // 构建提取提示
  const userPrompt = await buildSessionMemoryUpdatePrompt(currentMemory, memoryPath)
  
  // 运行 forked agent
  await runForkedAgent({
    promptMessages: [createUserMessage({ content: userPrompt })],
    cacheSafeParams: createCacheSafeParams(context),
    canUseTool: createMemoryFileCanUseTool(memoryPath),  // 仅允许编辑记忆文件
    querySource: 'session_memory',
    forkLabel: 'session_memory',
  })
  
  // 更新最后摘要消息 ID
  updateLastSummarizedMessageIdIfSafe(messages)
})

7. CLAUDE.md 与 Memory 文件发现

7.1 Memory 文件层级

文件位置: src/utils/claudemd.ts

加载顺序(优先级从低到高):
1. Managed (/etc/claude-code/CLAUDE.md)      - 全局策略设置
2. User (~/.claude/CLAUDE.md)               - 用户全局指令
3. Project (CLAUDE.md, .claude/CLAUDE.md)   - 项目代码库指令
4. Local (CLAUDE.local.md)                   - 本地私有指令

加载路径发现

  • User/Managed: 从固定位置加载
  • Project: 从 CWD 向上遍历到根目录,逐层加载
  • Local: 每个目录的 CLAUDE.local.md

7.2 @include 指令支持

// 语法
@path                    // 相对路径
@./relative/path         // 显式相对路径
@~/home/path             // 用户目录路径
@/absolute/path          // 绝对路径

// 示例
Please follow the guidelines in @./coding-standards.md

7.3 条件规则

支持通过 frontmatter paths 字段设置文件路径匹配条件:

---
name: react-guidelines
description: React specific guidelines
type: project
paths:
  - src/**/*.tsx
  - src/**/*.jsx
---

# React Guidelines
...

8. 缓存与性能优化

8.1 系统提示词缓存

文件位置: src/constants/systemPromptSections.ts

// 段落注册表
const SYSTEM_PROMPT_SECTIONS: Record<string, SystemPromptSectionConfig> = {
  memory: {
    cacheKey: 'memory',  // 相同 key 的段落共享缓存
    cacheTtlMs: 60_000, // TTL: 60 秒
    compute: () => loadMemoryPrompt(),
  },
  // ...
}

// 缓存查找
const cached = cache.get(cacheKey)
if (cached && Date.now() - cached.timestamp < cacheTtl) {
  return cached.value
}

8.2 Prompt 缓存复用

forked agent 机制: 压缩、记忆提取等任务共享主会话的 prompt 缓存:

export async function runForkedAgent({
  promptMessages,
  cacheSafeParams,  // 包含 systemPrompt, userContext, systemContext
  forkLabel,
  // ...
}): Promise<AgentRunResult> {
  // 使用与主会话相同的缓存 key 参数
  // 实现缓存命中复用
}

9. 关键设计模式

9.1 异步非阻塞

所有 IO 操作(文件读取、API 调用)都是异步的,不阻塞事件循环:

// getMemoryFiles 使用 memoize 缓存
export const getMemoryFiles = memoize(
  async (forceIncludeExternal: boolean = false): Promise<MemoryFileInfo[]> => {
    // 异步文件扫描和读取
  }
)

// 记忆提取使用 sequential 包装
const extractSessionMemory = sequential(async function (...) {
  // 确保同一时间只有一个提取任务运行
})

9.2 互斥与节流

// 记忆提取互斥
if (hasMemoryWritesSince(messages, lastMemoryMessageUuid)) {
  return  // 主代理已写入,跳过
}

// 节流控制
if (turnsSinceLastExtraction < throttleThreshold) {
  return
}

9.3 缓存失效联动

// 压缩时联动失效
export async function compactConversation(...) {
  // 清除 readFileState
  context.readFileState.clear()
  
  // 重置记忆文件缓存
  resetGetMemoryFilesCache('compact')
}

// System Prompt 注入变化时
export function setSystemPromptInjection(value: string | null): void {
  systemPromptInjection = value
  getUserContext.cache.clear?.()
  getSystemContext.cache.clear?.()
}

10. 总结

Claude Code 的上下文管理与记忆系统是一个精心设计的复杂架构:

  1. 分层提示词: 静态/动态分离,最大化缓存利用率
  2. 多级记忆: 从全局 Managed 规则到用户、项目、本地记忆
  3. 自动提取: 后台子代理自动从对话中提取记忆
  4. 智能压缩: 自动/手动压缩结合,Session Memory 轻量级方案
  5. 精确计数: usage 数据 + 估算混合的 Token 计数
  6. 性能优先: 异步 IO、缓存复用、互斥节流

这套系统确保 Claude Code 能够在超长对话中保持上下文完整性,同时通过记忆持久化实现跨会话的连续性。


参考文件索引

模块核心文件
上下文管理src/context.ts
System Promptsrc/constants/prompts.ts, src/constants/systemPromptSections.ts
Token 计数src/utils/tokens.ts, src/utils/tokenBudget.ts
对话历史src/history.ts, src/assistant/sessionHistory.ts
记忆系统src/memdir/memdir.ts, src/memdir/memoryTypes.ts, src/memdir/findRelevantMemories.ts
记忆提取src/services/extractMemories/extractMemories.ts
Session Memorysrc/services/SessionMemory/sessionMemory.ts, src/services/SessionMemory/sessionMemoryUtils.ts
压缩系统src/services/compact/compact.ts, src/services/compact/autoCompact.ts, src/services/compact/sessionMemoryCompact.ts
Memory 文件src/utils/claudemd.ts