Claude Code 源码剖析 模块一 · 第六节:autoDream 自动记忆整合

模块一 · 第六节:autoDream 自动记忆整合

核心问题

什么是 autoDream?Claude Code 如何在后台自动整合记忆?三重门控机制是如何工作的?分布式锁如何防止并发冲突?


◇ 本节位置

复制代码
Claude Code 全局架构

┌─────────────────────────────────────────────────────────────────────┐
│  入口与启动                                                          │
│                                                                      │
│  autoDream ← 本节                                                 │
│  ├── autoDream.ts ──> 主逻辑                                      │
│  ├── config.ts ──> 配置                                           │
│  └── consolidationLock.ts ──> 分布式锁                            │
└─────────────────────────────────────────────────────────────────────┘

一、autoDream 概述

1.1 什么是自动记忆整合

源码位置src/services/autoDream/autoDream.ts 第 1-9 行

typescript 复制代码
// Background memory consolidation. Fires the /dream prompt as a forked
// subagent when time-gate passes AND enough sessions have accumulated.
//
// Gate order (cheapest first):
//   1. Time: hours since lastConsolidatedAt >= minHours (one stat)
//   2. Sessions: transcript count with mtime > lastConsolidatedAt >= minSessions
//   3. Lock: no other process mid-consolidation

核心思想:Claude Code 在后台定期分析会话记忆,将有价值的信息提取到长期记忆中。

1.2 为什么需要 autoDream

复制代码
┌─────────────────────────────────────────────────────────────────────┐
│  没有 autoDream:                                                    │
│  - 每次会话的临时记忆很快被遗忘                                     │
│  - 跨会话的学习无法积累                                            │
└─────────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────────┐
│  有 autoDream:                                                      │
│  - 自动分析会话,提取重要信息                                       │
│  - 更新 CLAUDE.md、CLAUDE.local.md                                 │
│  - 形成长期记忆                                                     │
└─────────────────────────────────────────────────────────────────────┘

二、三重门控机制

2.1 门控顺序

源码位置src/services/autoDream/autoDream.ts 第 122 行

typescript 复制代码
export function initAutoDream(): void {
  // 按代价从低到高检查门控
  // 1. 时间门(stat 一个文件)
  // 2. 会话门(stat 多个文件)
  // 3. 锁门(尝试获取锁)
}

2.2 时间门

源码位置src/services/autoDream/autoDream.ts 第 55-80 行

typescript 复制代码
const DEFAULTS: AutoDreamConfig = {
  minHours: 24,       // 至少 24 小时
  minSessions: 5,       // 至少 5 个新会话
}

function getConfig(): AutoDreamConfig {
  const raw = getFeatureValue_CACHED_MAY_BE_STALE<Partial<AutoDreamConfig> | null>(
    'tengu_onyx_plover',  // GrowthBook feature key
    null,
  )
  return {
    minHours: raw?.minHours ?? DEFAULTS.minHours,
    minSessions: raw?.minSessions ?? DEFAULTS.minSessions,
  }
}

五问分析

**问 1:为什么用 mtime 而不是 ctime?

typescript 复制代码
// mtime(修改时间):文件内容修改时间
// ctime(变化时间):文件属性变化时间

// 选择 mtime 的原因:
// - 会话转录文件在有新消息时会被修改
// - mtime 更准确地反映会话活动

2.3 会话门

源码位置src/services/autoDream/consolidationLock.ts 第 30-35 行

typescript 复制代码
// Stale past this even if the PID is live (PID reuse guard).
const HOLDER_STALE_MS = 60 * 60 * 1000  // 1小时

/**
 * mtime of the lock file = lastConsolidatedAt. 0 if absent.
 * Per-turn cost: one stat.
 */
export async function readLastConsolidatedAt(): Promise<number> {
  try {
    const s = await stat(lockPath())
    return s.mtimeMs
  } catch {
    return 0
  }
}

五问分析

**问 1:为什么需要 PID reuse guard?

typescript 复制代码
// PID(进程 ID)可能被操作系统回收复用
// 如果锁的持有者崩溃,新进程可能获得相同的 PID

// 解决方案:
// - 即使 PID 存在,也要检查是否真的在运行
// - 使用 HOLDER_STALE_MS(1小时)作为额外保护

三、分布式锁实现

3.1 锁文件设计

源码位置src/services/autoDream/consolidationLock.ts 第 14-20 行

typescript 复制代码
const LOCK_FILE = '.consolidate-lock'

function lockPath(): string {
  return join(getAutoMemPath(), LOCK_FILE)
}

锁文件特点

  • 存储当前进程的 PID
  • mtime = lastConsolidatedAt(上次整合时间)
  • 放在 memory 目录下(与记忆文件同目录)

3.2 获取锁

源码位置src/services/autoDream/consolidationLock.ts 第 46-80 行

typescript 复制代码
export async function tryAcquireConsolidationLock(): Promise<number | null> {
  const path = lockPath()

  // 1. 读取现有锁
  let mtimeMs: number | undefined
  let holderPid: number | undefined
  try {
    const [s, raw] = await Promise.all([stat(path), readFile(path, 'utf8')])
    mtimeMs = s.mtimeMs
    holderPid = parseInt(raw.trim(), 10)
  } catch {
    // ENOENT --- 没有现有锁
  }

  // 2. 检查锁是否有效
  if (mtimeMs !== undefined && Date.now() - mtimeMs < HOLDER_STALE_MS) {
    if (holderPid !== undefined && isProcessRunning(holderPid)) {
      // 锁被活跃进程持有
      return null
    }
  }

  // 3. 尝试获取锁
  await mkdir(getAutoMemPath(), { recursive: true })
  await writeFile(path, String(process.pid))

  // 4. 验证是否获取成功(可能被其他进程抢走)
  const verify = await readFile(path, 'utf8')
  if (parseInt(verify.trim(), 10) !== process.pid) {
    return null  // 获取失败
  }

  return mtimeMs ?? 0  // 返回之前的 mtime(用于回滚)
}

3.3 锁的语义

状态 含义 操作
文件不存在 从未整合过 直接获取锁
PID 不存在 持有者已退出 可以抢占
PID 存在且运行中 正在整合 等待
超过 STALE 时间 可能是僵尸锁 可以抢占

五问分析

**问 1:为什么不使用分布式锁库(如 Redis)?

typescript 复制代码
// 设计意图:简单、可靠、无额外依赖

// 优点:
// - 零额外服务
// - 文件系统原子操作保证一致性
// - 崩溃后自动恢复(mtime 作为时间戳)

// 缺点:
// - 不支持跨机器协调(但记忆只在本地)
// - 不支持锁超时自动释放(使用 PID 检测代替)

四、整合执行

4.1 启动整合任务

源码位置src/services/autoDream/autoDream.ts 第 200-220 行

typescript 复制代码
// 注册为 Dream Task
const taskId = registerDreamTask(setAppState, {
  id: randomUUID(),
  description: 'Memory consolidation',
})

// 运行 forked 子代理
const params = createCacheSafeParams({ ... })
await runForkedAgent({
  prompt: buildConsolidationPrompt(sessions),
  background: true,
  params,
})

4.2 整合提示词

源码位置src/services/autoDream/consolidationPrompt.ts

typescript 复制代码
export function buildConsolidationPrompt(sessions: Session[]): string {
  return `分析以下会话记录,提取有价值的信息:

${sessions.map(s => s.transcript).join('\n\n')}

请将重要信息分类整理到:
1. CLAUDE.md - 项目规范(团队共享)
2. CLAUDE.local.md - 个人偏好(仅自己可见)
3. Team Memory - 团队知识(跨项目)`
}

五、错误处理

5.1 锁获取失败

源码位置src/services/autoDream/autoDream.ts 第 180-190 行

typescript 复制代码
const priorMtime = await tryAcquireConsolidationLock()
if (priorMtime === null) {
  logForDebugging('[autoDream] lock held, skipping')
  return  // 锁被占用,跳过本次整合
}

5.2 整合失败回滚

源码位置src/services/autoDream/autoDream.ts

typescript 复制代码
// 失败时回滚 mtime
await rollbackConsolidationLock(priorMtime)

export async function rollbackConsolidationLock(priorMtime: number): Promise<void> {
  if (priorMtime === 0) {
    // 之前没有锁,删除
    await unlink(lockPath()).catch(() => {})
  } else {
    // 恢复之前的 mtime
    await utimes(lockPath(), priorMtime, priorMtime)
  }
}

六、设计模式识别

6.1 双重检查锁定

typescript 复制代码
// initAutoDream() 中的双重检查:
if (Date.now() - lastConsolidatedAt >= minHours) {
  const sessions = await listSessionsTouchedSince(lastConsolidatedAt)
  if (sessions.length >= minSessions) {
    const lock = await tryAcquireConsolidationLock()
    if (lock !== null) {
      await runConsolidation()  // 真正执行
    }
  }
}

6.2 乐观锁

typescript 复制代码
// 使用 mtime 作为版本号
// - 获取锁时记录 priorMtime
// - 失败时恢复到 priorMtime
// - 类似于数据库的 MVCC

七、思考题

思考题 1:autoDream 和普通记忆提取有什么区别?

答案

typescript 复制代码
// 普通记忆提取:即时发生
// - 每次会话结束
// - 分析范围有限

// autoDream:定期批量处理
// - 后台运行,不影响前台
// - 可以处理多个会话
// - 更好地发现跨会话的模式

// 源码证据:
// autoDream.ts 第 1-9 行的注释明确说明:
// "Background memory consolidation"

思考题 2:为什么整合需要用户同意?

答案

typescript 复制代码
// autoDream 会修改 CLAUDE.md 等文件
// 用户需要审核整合结果
// 避免自动添加不准确的信息

// 源码证据:
// consolidationPrompt.ts 中的提示词要求:
// "Do NOT apply changes --- present proposals for user approval"

思考题 3:锁文件放在 memory 目录而不是项目根目录的原因?

答案

typescript 复制代码
// 1. 与记忆文件同目录,方便管理
// 2. 即使项目目录不可写,memory 目录通常可写
// 3. 符合"按 git-root 分目录"的设计

// 源码证据:
// consolidationLock.ts 第 8-10 行:
// "Lives inside the memory dir (getAutoMemPath) so it keys on git-root
// like memory does, and so it's writable even when the memory path comes
// from an env/settings override whose parent may not be."

八、延伸阅读

文件 行数 核心内容
src/services/autoDream/autoDream.ts ~350 主逻辑、三重门控
src/services/autoDream/consolidationLock.ts ~150 分布式锁实现
src/services/autoDream/config.ts ~30 配置管理

相关推荐
Alvin千里无风4 小时前
在 Ubuntu 上从源码安装 Nanobot:轻量级 AI 助手完整指南
linux·人工智能·ubuntu
环黄金线HHJX.4 小时前
龙虾钳足启发的AI集群语言交互新范式
开发语言·人工智能·算法·编辑器·交互
Omics Pro4 小时前
虚拟细胞:开启HIV/AIDS治疗新纪元的关键?
大数据·数据库·人工智能·深度学习·算法·机器学习·计算机视觉
悦来客栈的老板5 小时前
AI逆向|猿人学逆向反混淆练习平台第七题加密分析
人工智能
KOYUELEC光与电子努力加油5 小时前
JAE日本航空端子推出支持自走式机器人的自主充电功能浮动式连接器“DW15系列“方案与应用
服务器·人工智能·机器人·无人机
萤火阳光5 小时前
13|自定义 Skill 创作:打造专属自动化利器
人工智能
我哪会这个啊5 小时前
SpringAlibaba Ai基础入门
人工智能
掘根5 小时前
【微服务即时通讯项目】系统联调
微服务·云原生·架构
蓝色的杯子6 小时前
从 LLM 到 Agent Skill,龙虾的技术基础 · ② Token
人工智能