Claude Code 上下文管理与记忆系统源码解读
Claude Code 上下文管理与记忆系统源码解读
项目路径:
/mnt/e/code/cc/claude-code-main研究日期:2026-04-03 研究者:P5 架构分析
目录
- Conversation History 管理
- System Prompt 注入
- 上下文窗口优化策略
- 记忆持久化机制
- 上下文压缩与摘要
- Session Memory(会话记忆)
- CLAUDE.md 与 Memory 文件发现
- 缓存与性能优化
- 关键设计模式
- 总结
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_instructions | MCP 服务器指令 | 动态检测变更 |
scratchpad | 临时工作目录说明 | 缓存 |
token_budget | Token 预算指示 | 缓存 |
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
两种触发模式:
- 自动压缩: 上下文达到阈值时自动触发
- 手动压缩: 用户执行
/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 的上下文管理与记忆系统是一个精心设计的复杂架构:
- 分层提示词: 静态/动态分离,最大化缓存利用率
- 多级记忆: 从全局 Managed 规则到用户、项目、本地记忆
- 自动提取: 后台子代理自动从对话中提取记忆
- 智能压缩: 自动/手动压缩结合,Session Memory 轻量级方案
- 精确计数: usage 数据 + 估算混合的 Token 计数
- 性能优先: 异步 IO、缓存复用、互斥节流
这套系统确保 Claude Code 能够在超长对话中保持上下文完整性,同时通过记忆持久化实现跨会话的连续性。
参考文件索引
| 模块 | 核心文件 |
|---|---|
| 上下文管理 | src/context.ts |
| System Prompt | src/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 Memory | src/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 |