编程 Claude-Mem 深度实战:56K+ Star 的 Claude Code 持久记忆插件——从 Hook 生命周期架构到渐进式检索的全链路解析

2026-05-07 14:38:24 +0800 CST views 8

Claude-Mem 深度实战:56K+ Star 的 Claude Code 持久记忆插件——从 Hook 生命周期架构到渐进式检索的全链路解析

当你的 AI 编程助手每天跟你对话,却始终像个失忆症患者——每次开会都从零开始,上次的决策、踩过的坑、架构选择一概不记得。这不是 AI 的能力问题,是记忆问题。Claude-Mem 用一套精巧的 Hook 生命周期架构,给 Claude Code 装上了"长期记忆"。

一、背景:AI 编程助手的"失忆症"

1.1 上下文窗口的天花板

当前 AI 编程助手面临一个根本性矛盾:模型能力越来越强,但上下文窗口始终有限。Claude Code 在一次会话中可能做出上百次工具调用——读文件、写代码、执行命令、修复 Bug——但会话一结束,这些上下文就烟消云散。

下次开会,你告诉 Claude"继续上次的工作",它只会茫然地看着你。你不得不重新描述项目结构、重新解释架构决策、重新警告它上次踩过的坑。这不是效率问题,是体验灾难。

1.2 现有方案的局限

CLAUDE.md 方案:Claude Code 原生支持通过 CLAUDE.md 文件持久化项目规则和约定,但这只是静态文档,无法自动捕获动态的编码过程。

auto memory 方案:Claude Code 的自动记忆功能会把纠正和偏好写入 memory/ 目录,但覆盖面太窄,只有主动纠正才会被记录,大量的技术决策、Bug 修复过程、探索路径全部丢失。

外部笔记方案:手动维护开发日志?别逗了,程序员的文档惰性是业界公认的。

1.3 Claude-Mem 的答案

Claude-Mem 的核心理念很简单:自动捕获 Claude 在编码会话中的一切行为,用 AI 压缩成语义摘要,然后在未来的会话中自动注入相关上下文。

听起来简单,但实现起来需要解决几个关键工程问题:

  • 如何在不侵入 Claude Code 源码的情况下捕获所有工具调用?
  • 如何在 Hook 的毫秒级时间约束内完成数据采集?
  • 如何用 AI 压缩海量观测数据而不丢失关键信息?
  • 如何在有限的 Token 预算内智能检索和注入上下文?

Claude-Mem 的答案是:Hook 生命周期架构 + 异步队列处理 + 渐进式披露检索。这套架构不仅解决了问题,而且优雅得让人想鼓掌。

二、核心概念:Hook 驱动的记忆系统

2.1 非侵入式设计原则

Claude-Mem 的第一条设计原则:不能修改 Claude Code 的行为,只能在旁边观察。

这不仅仅是技术选择,更是可靠性要求。如果记忆系统崩溃了,Claude Code 必须还能正常工作。这是一种"渐进增强"的哲学:

没有 Claude-Mem → Claude Code 正常工作
有了 Claude-Mem → Claude Code + 历史上下文
Claude-Mem 崩了 → 自动降级到正常工作

2.2 Hook 系统:Claude Code 的"侧信道"

Claude Code 提供了一套 Hook 机制,允许外部命令在特定生命周期事件中被调用。Claude-Mem 就是围绕这套机制构建的。

五个生命周期 Hook,形成完整的记忆闭环:

SessionStart → UserPromptSubmit → PostToolUse → Stop → SessionEnd
    ↓                ↓                 ↓          ↓        ↓
 上下文注入        会话创建          观测入队    摘要生成   会话清理

关键洞察:Hook 是非阻塞的。Claude Code 不会等 Hook 完成,而是并行执行。这意味着 Hook 必须极快返回,耗时操作必须异步处理。

2.3 数据流全景

Hook (stdin) → SQLite 队列 → Worker Service → Claude Agent SDK 压缩 → SQLite → 下次会话 Hook 注入

这是一个典型的生产者-消费者模式:

  1. 生产者:各种 Hook 脚本,在 Claude Code 运行时捕获数据
  2. 缓冲区:SQLite 的 observation_queue
  3. 消费者:后台 Worker Service,用 AI SDK 异步处理
  4. 存储:处理后的结构化观测数据存回 SQLite
  5. 消费端:下次会话的 SessionStart Hook 读取并注入

2.4 观测数据的层次化结构

Claude-Mem 不是简单地存储工具调用的原始日志,而是用 AI 提取结构化的层次信息:

interface Observation {
  title: string;       // 简洁标题:"修复数据库连接超时"
  subtitle: string;    // 副标题:"添加了重试机制"
  narrative: string;   // 叙述性描述:发生了什么,为什么
  text: string;        // 完整文本内容
  facts: string;       // 关键事实点
  concepts: string;    // 概念标签:security, performance, architecture
  type: ObservationType; // decision | bugfix | feature | refactor | discovery | change
  files_read: string;  // 读取了哪些文件
  files_modified: string; // 修改了哪些文件
}

这种层次化结构让检索和展示都更加精准——你可以只看标题快速扫描,也可以深入叙述了解细节。

三、架构深度分析:从 Hook 到 Worker 的全链路

3.1 目录结构解析

claude-mem/
├── src/
│   ├── hooks/                  # TypeScript Hook 实现
│   ├── sdk/                    # Claude Agent SDK 集成
│   ├── services/
│   │   ├── worker-service.ts   # Express HTTP + SSE Worker
│   │   ├── sync/ChromaSync.ts  # Chroma 向量索引
│   │   └── sqlite/             # SQLite + FTS5 存储层
│   │       ├── SessionStore.ts # CRUD 操作
│   │       ├── SessionSearch.ts # FTS5 搜索
│   │       └── migrations.ts   # 数据库迁移
│   ├── ui/viewer/              # React Web 查看器
│   └── shared/                 # 共享工具
├── plugin/                     # 插件分发目录
│   ├── hooks/hooks.json        # Hook 注册配置
│   ├── scripts/                # 构建后的可执行脚本
│   │   ├── version-check.js    # Setup 阶段版本检查
│   │   ├── bun-runner.js       # Bun 运行时解析器
│   │   ├── worker-service.cjs  # Worker 守护进程
│   │   ├── mcp-server.cjs      # MCP 搜索服务器
│   │   └── context-generator.cjs # 上下文生成器
│   ├── skills/                 # Agent 技能(mem-search 等)
│   └── ui/viewer.html          # 自包含 React Bundle
└── tests/                      # 测试套件

3.2 六个 Hook 脚本详解

3.2.1 Setup Hook:版本哨兵

{
  "hooks": {
    "Setup": [{
      "hooks": [{
        "type": "command",
        "command": "node ${CLAUDE_PLUGIN_ROOT}/scripts/version-check.js",
        "timeout": 60
      }]
    }]
  }
}

这个 Hook 的设计堪称教科书级:

  • 亚 100ms 执行:只读取一个版本标记文件,不做任何 I/O
  • 永远返回 0:即使版本不匹配也不阻塞会话
  • 只写 stderr:提示用户运行 npx claude-mem repair,不污染 Claude 的上下文
  • 分离关注点:实际的安装修复由 npx 命令完成,Setup Hook 只负责检测
// 版本检查的核心逻辑伪代码
function versionCheck(): ExitCode {
  const installedVersion = readFileSync('.install-version');
  const currentVersion = readPackageJson().version;
  
  if (installedVersion !== currentVersion) {
    stderr.write('run: npx claude-mem repair');
  }
  
  return 0; // 永远成功退出
}

3.2.2 SessionStart Hook:上下文注入

这是最关键的 Hook——它让 Claude Code 在会话开始时就能"回忆"起过去。

async function contextHook(): Promise<HookOutput> {
  // 1. 从当前工作目录提取项目名
  const project = extractProjectName(process.cwd());
  
  // 2. 查询最近 10 条会话摘要
  const summaries = await db.getRecentSummaries(project, 10);
  
  // 3. 查询最近 50 条观测(可配置)
  const observations = await db.getRecentObservations(
    project, 
    settings.CLAUDE_MEM_CONTEXT_OBSERVATIONS
  );
  
  // 4. 格式化为渐进式披露索引
  const context = formatProgressiveDisclosure(summaries, observations);
  
  // 5. 通过 hookSpecificOutput.additionalContext 静默注入
  return {
    hookSpecificOutput: {
      additionalContext: context
    }
  };
}

注入格式——渐进式披露索引:

# [claude-mem] recent context

**Legend:** 🎯 session-request | 🔴 gotcha | 🟡 problem-solution | 🔵 info | 🟢 feature | 🟣 decision

### May 7, 2026

**General**
| ID | Time | T | Title | Tokens |
|----|------|---|-------|--------|
| #2586 | 12:58 AM | 🔴 | 数据库连接超时修复 | ~51 |
| #2587 | 01:15 AM | 🟣 | 选择 Redis 做缓存层 | ~48 |
| #2588 | 02:30 AM | 🟢 | 实现 JWT 刷新机制 | ~62 |

*Use MCP search tools to access full details*

注意这个格式:只展示 ID、类型、标题和 Token 数——完整的细节需要用 MCP 搜索工具按需获取。这就是渐进式披露的核心思想。

3.2.3 UserPromptSubmit Hook:会话初始化

async function sessionInitHook(stdin: HookInput): Promise<void> {
  const { session_id, prompt } = parseInput(stdin);
  
  // 创建会话记录
  await db.createSession({
    sdk_session_id: generateId(),
    claude_session_id: session_id,
    project: extractProjectName(process.cwd()),
    user_prompt: prompt  // v4.2.0+: 保存原始提示用于搜索
  });
  
  // 确保 Worker 在运行
  await ensureWorkerRunning();
}

这个 Hook 的巧妙之处在于:它同时保存了用户的原始提示。这意味着你之后可以用自然语言搜索"我上次让你修复的那个数据库问题",FTS5 能直接命中。

3.2.4 PostToolUse Hook:观测入队

这是整个系统中最频繁执行的 Hook——每次工具调用都会触发,一个会话可能触发上百次。

async function saveObservationHook(stdin: HookInput): Promise<HookOutput> {
  const { tool_name, tool_input, tool_output } = parseInput(stdin);
  
  // 快速入队,不等待处理
  await db.enqueueObservation({
    session_id: currentSession.id,
    tool_name,
    tool_input,
    tool_output,
    created_at_epoch: Date.now()
  });
  
  // 立即返回,不阻塞
  return { continue: true, suppressOutput: true };
}

Fire-and-Forget 模式:Hook 只负责把数据丢进队列,处理是 Worker 的事。这种解耦保证了 Hook 的执行时间在 20ms 以内。

入队的数据结构:

{
  "session_id": "abc123",
  "tool_name": "Edit",
  "tool_input": {
    "file_path": "/path/to/auth.ts",
    "old_string": "const token = jwt.sign(payload, secret);",
    "new_string": "const token = jwt.sign(payload, secret, { expiresIn: '1h' });"
  },
  "tool_output": {
    "success": true,
    "linesChanged": 1
  },
  "created_at_epoch": 1715053200
}

3.2.5 Stop Hook:AI 摘要生成

当 Claude 停止响应时触发(注意:不是会话结束,是一次交互完成)。

<!-- AI 生成的摘要结构 -->
<summary>
  <request>用户要求添加 JWT Token 过期机制</request>
  <investigated>检查了 auth.ts、middleware/jwt.ts、config/auth.ts</investigated>
  <learned>原实现没有设置 expiresIn 参数,导致 Token 永不过期</learned>
  <completed>在 jwt.sign 调用中添加了 expiresIn: '1h',同时添加了刷新 Token 的中间件</completed>
  <next_steps>需要添加 Token 过期的错误处理和自动刷新逻辑</next_steps>
  <files_read>
    <file>src/auth.ts</file>
    <file>src/middleware/jwt.ts</file>
  </files_read>
  <files_modified>
    <file>src/auth.ts</file>
  </files_modified>
  <notes>使用了 RS256 算法替代 HS256,因为项目需要支持多服务间 Token 共享</notes>
</summary>

一个会话可以产生多个摘要——每个 Stop 事件都会触发一次,形成"检查点"而非"终章"。这样即使会话很长,也不会丢失中间过程。

3.2.6 SessionEnd Hook:优雅清理

// v3 时代的暴力方式(已废弃)
SessionEnd → DELETE /worker/session → Worker 被强制终止

// v4.1.0+ 的优雅方式
SessionEnd → UPDATE sessions SET completed_at = NOW()
Worker 检测到完成 → 处理剩余观测 → 自然退出

为什么要优雅清理?因为暴力终止会导致:

  • 正在生成的摘要被中断
  • 队列中的观测丢失
  • 竞态条件引发数据不一致

而且注意:/clear 命令不触发 SessionEnd,这样用户清屏不会意外结束会话追踪。

3.3 Worker Service:后台大脑

3.3.1 为什么需要后台 Worker?

Hook 必须在毫秒级返回,但 AI 压缩需要 5-30 秒。这个时间差必须用异步处理来填平。

┌─────────────────────────────────────┐
│            Hook(快)                │
│  1. 读取 stdin (< 1ms)              │
│  2. 插入队列 (< 10ms)               │
│  3. 返回成功 (< 20ms 总计)          │
└─────────────────────────────────────┘
                    ↓ (队列)
┌─────────────────────────────────────┐
│          Worker(慢)                │
│  1. 每秒轮询队列                    │
│  2. 用 Claude Agent SDK 处理 (5-30s)│
│  3. 解析并存储结果                  │
│  4. 标记观测已处理                  │
└─────────────────────────────────────┘

3.3.2 技术选型

层级技术选型理由
语言TypeScript (ES2022)与 Claude Code 生态一致
运行时Bun ≥ 1.0快启动、低内存、内置 TypeScript
数据库SQLite + bun:sqlite零配置、单文件、WAL 模式并发
向量库Chroma(可选)语义搜索增强
HTTP 框架Express.js 5成熟稳定、中间件生态
实时推送Server-Sent Events单向推送、比 WebSocket 轻量
AI SDK@anthropic-ai/claude-agent-sdk官方 SDK、质量保证
构建工具esbuild极速打包、TypeScript 原生支持

3.3.3 Worker HTTP API

Worker 不仅是后台处理器,还暴露了一组 HTTP API 供搜索和查看:

// 端口计算:基于用户 UID,避免多用户冲突
const defaultPort = 37700 + (uid % 100);
// 可通过 CLAUDE_MEM_WORKER_PORT 环境变量覆盖

// 核心 API 端点
app.get('/health', healthCheck);
app.post('/sessions', createSession);
app.get('/sessions/:id', getSession);
app.patch('/sessions/:id', updateSession);
app.post('/observations', enqueueObservation);
app.get('/observations/:id', getObservation);

// 搜索 API(10 个端点)
app.get('/api/search/observations', searchObservations);
app.get('/api/search/sessions', searchSessions);
app.get('/api/search/prompts', searchPrompts);
app.get('/api/search/by-concept', searchByConcept);
app.get('/api/search/by-file', searchByFile);
app.get('/api/search/by-type', searchByType);
app.get('/api/search/recent', getRecentContext);
app.get('/api/search/advanced', advancedSearch);
app.get('/api/timeline', getTimeline);
app.get('/api/observations/:id', getObservationDetail);

// SSE 端点(实时推送)
app.get('/api/stream', streamEvents);

// Viewer UI
app.use('/', express.static(viewerPath));

3.3.4 Bun 进程管理

Worker 的生命周期由 Bun 自动管理:

# 启动(Hook 自动触发,无需手动)
npm run worker:start

# 状态检查
npm run worker:status

# 查看日志
npm run worker:logs

# 重启
npm run worker:restart

# 停止
npm run worker:stop

Bun 的优势:

  • 自动重启:Worker 崩溃后自动恢复
  • 快启动:冷启动 < 100ms
  • 低内存:常驻内存约 30-50MB
  • 跨平台:macOS、Linux、Windows 一致体验

3.4 数据库架构:SQLite + FTS5

3.4.1 核心表结构

Claude-Mem 的数据存储在 ~/.claude-mem/claude-mem.db,使用 WAL 模式支持并发读写。

sdk_sessions 表——会话追踪:

CREATE TABLE sdk_sessions (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  sdk_session_id TEXT UNIQUE NOT NULL,
  claude_session_id TEXT,
  project TEXT NOT NULL,
  prompt_counter INTEGER DEFAULT 0,
  status TEXT NOT NULL DEFAULT 'active',
  created_at TEXT NOT NULL,
  created_at_epoch INTEGER NOT NULL,
  completed_at TEXT,
  completed_at_epoch INTEGER,
  last_activity_at TEXT,
  last_activity_epoch INTEGER
);

-- 关键索引
CREATE INDEX idx_sdk_sessions_project ON sdk_sessions(project);
CREATE INDEX idx_sdk_sessions_status ON sdk_sessions(status);
CREATE INDEX idx_sdk_sessions_created_at ON sdk_sessions(created_at_epoch DESC);

observations 表——观测数据(核心):

CREATE TABLE observations (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  session_id TEXT NOT NULL,
  sdk_session_id TEXT NOT NULL,
  claude_session_id TEXT,
  project TEXT NOT NULL,
  prompt_number INTEGER,
  tool_name TEXT NOT NULL,
  correlation_id TEXT,
  
  -- 层次化字段
  title TEXT,
  subtitle TEXT,
  narrative TEXT,
  text TEXT,
  facts TEXT,
  concepts TEXT,
  type TEXT,           -- decision | bugfix | feature | refactor | discovery | change
  files_read TEXT,
  files_modified TEXT,
  
  created_at TEXT NOT NULL,
  created_at_epoch INTEGER NOT NULL,
  
  FOREIGN KEY (sdk_session_id) REFERENCES sdk_sessions(sdk_session_id)
);

session_summaries 表——AI 生成的会话摘要:

CREATE TABLE session_summaries (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  sdk_session_id TEXT NOT NULL,
  claude_session_id TEXT,
  project TEXT NOT NULL,
  prompt_number INTEGER,
  request TEXT,        -- 用户原始请求
  investigated TEXT,   -- 检查了什么
  learned TEXT,        -- 发现了什么
  completed TEXT,      -- 完成了什么
  next_steps TEXT,     -- 后续步骤
  notes TEXT,          -- 额外备注
  created_at TEXT NOT NULL,
  created_at_epoch INTEGER NOT NULL
);

user_prompts 表——用户提示全文(v4.2.0+):

CREATE TABLE user_prompts (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  sdk_session_id TEXT NOT NULL,
  claude_session_id TEXT,
  project TEXT NOT NULL,
  prompt_number INTEGER,
  prompt_text TEXT NOT NULL,
  created_at TEXT NOT NULL,
  created_at_epoch INTEGER NOT NULL
);

3.4.2 FTS5 全文搜索引擎

Claude-Mem 为每个核心表创建了 FTS5 虚拟表,实现毫秒级全文检索:

-- 观测全文搜索
CREATE VIRTUAL TABLE observations_fts USING fts5(
  title, subtitle, narrative, text, facts, concepts,
  content='observations', content_rowid='id'
);

-- 摘要全文搜索
CREATE VIRTUAL TABLE session_summaries_fts USING fts5(
  request, investigated, learned, completed, next_steps, notes,
  content='session_summaries', content_rowid='id'
);

-- 用户提示全文搜索
CREATE VIRTUAL TABLE user_prompts_fts USING fts5(
  prompt_text,
  content='user_prompts', content_rowid='id'
);

自动同步触发器——保证 FTS 索引与源表一致:

-- 插入触发器
CREATE TRIGGER observations_ai AFTER INSERT ON observations BEGIN
  INSERT INTO observations_fts(rowid, title, subtitle, narrative, text, facts, concepts)
  VALUES (new.id, new.title, new.subtitle, new.narrative, new.text, new.facts, new.concepts);
END;

-- 更新触发器(先删旧索引,再插新索引)
CREATE TRIGGER observations_au AFTER UPDATE ON observations BEGIN
  INSERT INTO observations_fts(observations_fts, rowid, title, subtitle, narrative, text, facts, concepts)
  VALUES('delete', old.id, old.title, old.subtitle, old.narrative, old.text, old.facts, old.concepts);
  INSERT INTO observations_fts(rowid, title, subtitle, narrative, text, facts, concepts)
  VALUES (new.id, new.title, new.subtitle, new.narrative, new.text, new.facts, new.concepts);
END;

-- 删除触发器
CREATE TRIGGER observations_ad AFTER DELETE ON observations BEGIN
  INSERT INTO observations_fts(observations_fts, rowid, title, subtitle, narrative, text, facts, concepts)
  VALUES('delete', old.id, old.title, old.subtitle, old.narrative, old.text, old.facts, old.concepts);
END;

FTS5 的查询语法支持丰富:

-- 简单搜索
SELECT * FROM observations_fts WHERE observations_fts MATCH 'authentication';

-- AND/OR/NOT
SELECT * FROM observations_fts WHERE observations_fts MATCH 'error AND database';
SELECT * FROM observations_fts WHERE observations_fts MATCH 'bug OR fix';
SELECT * FROM observations_fts WHERE observations_fts MATCH 'security NOT deprecated';

-- 精确短语
SELECT * FROM observations_fts WHERE observations_fts MATCH '"database migration"';

-- 列限定搜索
SELECT * FROM observations_fts WHERE observations_fts MATCH 'title:authentication';
SELECT * FROM observations_fts WHERE observations_fts MATCH 'concepts:security';

-- 组合查询
SELECT * FROM observations_fts WHERE observations_fts MATCH '"user auth" AND (JWT OR session) NOT deprecated';

安全防护(v4.2.3+):所有 FTS5 查询都经过转义,防止 SQL 注入:

function escapeFTSQuery(query: string): string {
  return query.replace(/"/g, '""');
}
// 332 个注入攻击测试用例覆盖

3.4.3 数据库迁移策略

Claude-Mem 使用渐进式迁移管理 schema 演进:

Migration 001: 初始 schema(sessions, memories, overviews)
Migration 002: 层次化记忆字段(title, subtitle, facts, concepts)
Migration 003: SDK 会话和观测
Migration 004: 会话摘要
Migration 005: 多提示会话(prompt_counter, prompt_number)
Migration 006: FTS5 虚拟表和触发器
Migration 007-010: 各种改进和用户提示表

3.5 搜索架构:渐进式披露的哲学

3.5.1 为什么不能"一次全拉"?

传统 RAG 方案的做法:检索所有相关文档,一股脑塞进上下文窗口。这在记忆系统中行不通:

  • 20 条完整观测 = 10,000-20,000 Token
  • 其中只有 10% 真正相关
  • 浪费了 18,000 Token 在不相关的内容上
  • 还挤占了 Claude 处理实际任务的 Token 空间

3.5.2 三层检索工作流

Claude-Mem 的解决方案是"渐进式披露"(Progressive Disclosure),分三层逐步获取信息:

第一层:Search(索引)

// 获取轻量级索引
search(query: "authentication bug", type: "bugfix", limit: 10)
// 返回:ID + 标题 + 时间 + 类型,每条约 50-100 Token

第二层:Timeline(上下文)

// 获取某个时间点前后的上下文
timeline(anchor: 12345, depth_before: 3, depth_after: 3)
// 或基于查询自动定位
timeline(query: "authentication", depth_before: 2, depth_after: 2)
// 返回:时间线视图,每条约 100-200 Token

第三层:Get Observations(完整详情)

// 只获取筛选后的关键观测的完整信息
get_observations(ids: [123, 456, 789])
// 返回:完整叙述、事实、文件列表、概念标签,每条约 500-1000 Token

Token 节省效果

方法Token 消耗相关性
全量拉取 20 条10,000-20,000~10%
三层检索3,000-4,000~100%
节省70-80%10x 提升

3.5.3 MCP 搜索工具

Claude-Mem 通过 4 个 MCP 工具暴露搜索能力:

  1. __IMPORTANT:工作流文档,自动展示,提示 Claude 如何使用三层检索
  2. search:搜索记忆索引,支持 FTS5 完整语法
  3. timeline:获取时间线上下文
  4. get_observations:按 ID 批量获取完整观测

同时提供 mem-search Skill(v5.4.0+),通过 HTTP API 直接调用,比 MCP 更省 Token:

  • Skill 前置信息:约 250 Token(会话启动时加载)
  • 完整指令:约 2,500 Token(按需加载)
  • 比 MCP 方式每会话节省约 2,250 Token

3.5.4 混合搜索:FTS5 + Chroma 向量

Claude-Mem 的搜索不是单纯的全文检索,而是混合搜索:

  • FTS5:精确关键词匹配,速度快,确定性高
  • Chroma 向量搜索:语义相似度匹配,能理解"认证"和"鉴权"是同一概念
// ChromaSync:可选的向量索引同步
class ChromaSync {
  async syncObservation(observation: Observation): Promise<void> {
    // 将观测的 embedding 存入 Chroma
    const embedding = await this.generateEmbedding(observation.narrative);
    await this.chroma.upsert({
      id: observation.id.toString(),
      embedding,
      metadata: {
        type: observation.type,
        project: observation.project,
        concepts: observation.concepts
      }
    });
  }
}

Chroma 是可选组件,不安装也能用 FTS5 正常搜索。但有了它,语义搜索能力显著增强。

四、代码实战:从安装到生产部署

4.1 快速安装

# 方式一:npx 一键安装(推荐)
npx claude-mem install

# 方式二:插件市场安装
# 在 Claude Code 终端中执行:
/plugin marketplace add thedotmack/claude-mem
/plugin install claude-mem

# 方式三:OpenClaw 一键安装
curl -fsSL https://install.cmem.ai/openclaw.sh | bash

安装过程做了什么:

  1. 检测并安装 Bun 运行时(如果缺失)
  2. 检测并安装 uv Python 包管理器(向量搜索需要)
  3. 在插件缓存目录执行 bun install
  4. 写入 .install-version 标记文件
  5. 注册 Hook 脚本到 Claude Code 的 settings.json

4.2 验证安装

# 检查 Worker 是否运行
curl http://localhost:37777/health

# 打开 Web 查看器
open http://localhost:37777

# 在 Claude Code 中查看插件状态
/plugin list

4.3 配置详解

配置文件位置:~/.claude-mem/settings.json

{
  "CLAUDE_MEM_MODE": "code--zh",
  "CLAUDE_MEM_CONTEXT_OBSERVATIONS": 50,
  "CLAUDE_MEM_WORKER_PORT": 37777,
  "CLAUDE_MEM_DATA_DIR": "~/.claude-mem",
  "CLAUDE_MEM_LOG_LEVEL": "info"
}

工作模式

模式语言描述
codeEnglish默认英文模式
code--zh简体中文中文模式,生成的观测用中文
code--ja日本語日文模式

模式格式为 code--[lang],其中 lang 是 ISO 639-1 语言代码。选择中文模式后,Claude-Mem 生成的观测标题、叙述、事实等都会用中文,FTS5 搜索中文也更准确。

4.4 实战场景:Debug 历史回溯

假设你两周前修过一个数据库连接的 Bug,现在类似的问题又出现了。你问 Claude Code:

"上次我们修的那个数据库连接超时的问题,当时是怎么解决的?"

Claude 会自动调用 MCP 搜索工具:

Step 1: search(query="数据库连接超时", type="bugfix", limit=10)
  → 找到 3 条相关观测:#245, #312, #489

Step 2: timeline(anchor=312, depth_before=3, depth_after=3)
  → 看到 Bug 修复前后发生了什么

Step 3: get_observations(ids=[312, 489])
  → 获取完整的修复过程:改了什么文件、为什么、有什么约束

Claude 现在不仅知道你修过这个 Bug,还知道具体的修复方案、涉及的文件、以及当时的决策理由。这就是持久记忆的价值。

4.5 实战场景:架构决策追溯

Step 1: search(query="缓存", type="decision", limit=5)
  → 找到决策观测:"选择 Redis 做缓存层"

Step 2: get_observations(ids=[2587])
  → 获取完整决策理由:
     - 为什么选 Redis 不选 Memcached?
     - 考虑了哪些备选方案?
     - 有什么已知限制?
     - 数据一致性要求是什么?

4.6 实战场景:代码考古

想了解某个文件为什么长这样:

Step 1: search(query="worker-service.ts", limit=20)
  → 找到所有涉及该文件的观测

Step 2: timeline(query="worker-service.ts refactor", depth_before=2, depth_after=2)
  → 看到重构的前因后果

Step 3: get_observations(ids=[关键里程碑])
  → 深入每次变更的实现细节

4.7 Gemini CLI 和 OpenCode 支持

Claude-Mem 不止支持 Claude Code,还能给 Gemini CLI 和 OpenCode 加记忆:

# Gemini CLI 安装
npx claude-mem install --ide gemini-cli

# OpenCode 安装
npx claude-mem install --ide opencode

底层原理一样——都是通过 Hook 机制捕获工具调用,只是注册方式和 Hook 路径不同。

4.8 隐私控制

不是所有内容都应该被记忆。Claude-Mem 提供了两层隐私控制:

标签过滤:用 <private> 标签标记的内容不会被存储

<private>
API_KEY=sk-xxxx
DATABASE_URL=postgres://admin:password@...
</private>

精细上下文配置:控制哪些内容被注入到上下文中

{
  "CLAUDE_MEM_EXCLUDE_PATTERNS": [
    "**/*.env",
    "**/secrets/**",
    "**/credentials/**"
  ]
}

五、性能优化:Token 效率之道

5.1 Token 是最昂贵的资源

在 AI 编程助手的场景中,Token 不只是钱的问题,更是上下文窗口的容量问题。每多注入一个 Token 的历史记忆,就少了一个 Token 用于当前任务。Claude-Mem 的每个设计决策都围绕 Token 效率展开。

5.2 Skill vs MCP:2,250 Token 的差距

v5.4.0 引入的 mem-search Skill 比 MCP 方式每会话节省约 2,250 Token:

方式前置 Token按需 Token总计
MCP(4 个工具)~2,50002,500
Skill~250~2,500(按需加载)250-2,750
差异-2,250

MCP 方式的问题是:4 个工具的 schema 定义和描述在每次会话中都会被加载,即使你不用搜索功能。Skill 方式只在被调用时才加载完整指令,平时只占 250 Token 的轻量前置信息。

5.3 渐进式披露的数学

假设一个项目有 500 条观测,你想找"认证相关的 Bug 修复":

传统方案

500 条完整观测 × 750 Token/条 = 375,000 Token

这根本塞不进上下文窗口。

Claude-Mem 三层方案

第一层:search 返回 10 条索引 × 75 Token/条 = 750 Token
第二层:timeline 返回 6 条上下文 × 150 Token/条 = 900 Token
第三层:get_observations 返回 3 条详情 × 750 Token/条 = 2,250 Token
总计:3,900 Token

节省了 99% 的 Token,同时保持了 100% 的相关性。

5.4 Worker 的性能考量

队列处理延迟:Worker 每秒轮询一次队列。从 Hook 入队到 Worker 开始处理,最大延迟 1 秒。AI 压缩每条观测需要 5-30 秒,但这是异步的,不影响主会话。

数据库性能

  • SQLite WAL 模式允许并发读写
  • bun:sqlite 使用同步 API,比异步 API 更快(在单线程 Bun 运行时中,异步没有优势)
  • 所有外键和常用查询列都有索引
  • FTS5 搜索比 LIKE 查询快 10-100 倍

内存占用:Worker 常驻内存约 30-50MB,这在现代开发环境中完全可以接受。

5.5 版本检查的亚毫秒保证

Setup Hook 是每次 Claude Code 启动都会执行的,它必须绝对不影响启动速度:

// 整个操作只有一次文件读取 + 一次字符串比较
function versionCheck() {
  const marker = readFileSync('.install-version', 'utf8');  // < 1ms
  const current = PACKAGE_VERSION;                          // 内存中
  if (marker.trim() !== current) {
    process.stderr.write('run: npx claude-mem repair');
  }
  return 0;
}

实测执行时间 < 50ms,用户完全无感知。

六、设计模式提炼:可复用的架构智慧

6.1 Fire-and-Forget Hook

// ❌ 坏:Hook 等待处理完成
async function badHook(input: HookInput) {
  const observation = parseInput(input);
  await processObservation(observation);  // 阻塞!5-30 秒!
  return success();
}

// ✅ 好:Hook 入队后立即返回
async function goodHook(input: HookInput) {
  const observation = parseInput(input);
  await enqueueObservation(observation);  // 快速入队 < 10ms
  return success();                        // 立即返回
}

这个模式在任何需要"捕获但不阻塞"的场景中都适用:日志采集、指标上报、事件溯源。

6.2 队列解耦模式

Hook(捕获)→ 队列(缓冲)→ Worker(处理)

好处:

  • Hook 和 Worker 可以独立失败和重试
  • 并行 Hook 执行安全(每个 Hook 有自己的 stdin)
  • 重试逻辑集中在一处
  • 天然支持背压处理

6.3 优雅降级模式

try {
  await captureObservation();
} catch (error) {
  // 记录错误,但不抛出
  console.error('Memory capture failed:', error);
  return { continue: true, suppressOutput: true };
}

失败场景的处理策略:

  • 数据库锁死 → 跳过本次观测,记录错误
  • Worker 崩溃 → Bun 自动重启
  • 网络问题 → 指数退避重试
  • 磁盘满 → 警告用户,禁用记忆

6.4 渐进增强模式

没有记忆系统 → 正常工作
有记忆系统   → 增强版工作
记忆系统故障 → 降级到正常工作

这不是可选的,是必须的。任何扩展 Claude Code 的插件都应该遵循这个原则。

6.5 渐进式披露模式

适用于任何"信息量大但实际需求少"的场景:

  • IDE 的代码导航(先看大纲,再看具体实现)
  • 日志查看器(先看摘要,再看全文)
  • 监控面板(先看概览,再看详情)

七、Beta 功能:Endless Mode

Claude-Mem 还有一个实验性功能叫 Endless Mode(无尽模式),采用仿生记忆架构(Biomimetic Memory Architecture),旨在突破单次会话的上下文限制。

核心思想:人类的记忆不是"全部记住然后全部回忆",而是有一个"工作记忆"(短期)和"长期记忆"的分层系统。Endless Mode 模拟了这种分层:

  • 工作记忆:当前活跃的观测,完整保留
  • 短期记忆:最近的观测,压缩但可检索
  • 长期记忆:历史观测,高度压缩,只保留核心概念

在 Web 查看器(http://localhost:37777)的 Settings 中可以切换 stable/beta 版本。

八、架构演进史:从 v3 到 v5

Claude-Mem 的架构经历了三个大版本的演进,每一版都在解决上一版的痛点:

v3 时代:简单直接

  • 直接在 Hook 中处理观测(阻塞!)
  • Worker 通过 DELETE 命令管理(暴力!)
  • 简单的 memories 表存储压缩块
  • 没有层次化结构,搜索靠 LIKE

v4 时代:异步化 + 结构化

  • 引入 observation_queue 队列表
  • Worker 异步处理,Hook 不再阻塞
  • 层次化观测字段(title, subtitle, narrative...)
  • FTS5 全文搜索
  • 用户提示全文保存
  • 多摘要检查点(而非单次终局摘要)
  • 优雅会话清理

v5 时代:效率优化

  • mem-search Skill 替代纯 MCP 工具
  • 渐进式披露索引(索引→时间线→详情)
  • Chroma 向量搜索(可选)
  • Web 查看器(React + SSE 实时推送)
  • Beta Channel 和 Endless Mode
  • 多 IDE 支持(Claude Code + Gemini CLI + OpenCode)

每一次演进都在回答同一个问题:如何在有限的 Token 预算内,让 AI 记住更多有用的东西?

九、竞品对比与定位

特性Claude-MemCLAUDE.mdauto memory外部 RAG
自动捕获✅ 全自动❌ 手写🟡 部分自动❌ 需要自建
AI 压缩✅ Agent SDK🟡 看实现
全文搜索✅ FTS5🟡 看实现
语义搜索✅ Chroma🟡 看实现
渐进式披露✅ 三层
非侵入式🟡
多 IDE 支持
Web 查看器
实时推送✅ SSE

Claude-Mem 的核心优势:全自动 + AI 压缩 + 渐进式检索。你不需要做任何额外工作,它就像你的记忆一样,在后台默默工作。

十、总结与展望

10.1 Claude-Mem 解决了什么问题?

Claude-Mem 解决的是 AI 编程助手的"失忆症"——通过 Hook 生命周期架构自动捕获编码过程,通过 AI 压缩提炼关键信息,通过渐进式披露在有限 Token 预算内智能检索和注入上下文。

10.2 架构的精妙之处

  1. 非侵入式:不修改 Claude Code,只通过 Hook 观察
  2. 异步解耦:Hook 毫秒级返回,Worker 异步处理
  3. 渐进式披露:三层检索,10x Token 节省
  4. 优雅降级:记忆系统故障不影响 Claude Code
  5. 层次化存储:从标题到叙述,从索引到全文

10.3 适用场景

  • 长期项目维护:跨会话保持项目上下文
  • Bug 排查:回溯历史修复过程
  • 架构决策:追溯技术选型的理由
  • 团队协作:新人快速了解项目历史
  • 代码考古:理解代码演化的来龙去脉

10.4 未来展望

随着 AI 编程助手越来越强,记忆系统只会越来越重要。Claude-Mem 目前的方向:

  • 跨项目记忆:在项目间共享通用的编码模式
  • 团队记忆:团队成员共享项目知识库
  • 更智能的压缩:利用更小的模型本地压缩,降低 API 成本
  • Endless Mode 正式版:突破单次会话上下文限制
  • 多模态记忆:不只记忆文本,还记忆图表、截图

一句话总结:Claude-Mem 不只是一个插件,它是一种"AI 记忆系统"的参考架构——Hook 生命周期 + 异步队列 + AI 压缩 + 渐进式检索。如果你正在构建任何需要"让 AI 记住东西"的系统,这套架构值得深入研究。


项目地址:github.com/thedotmack/claude-mem | Star 数:56K+ | 许可证:AGPL-3.0 | 官方文档:docs.claude-mem.ai

复制全文 生成海报 Claude Code AI Agent 持久记忆 Hook FTS5

推荐文章

20个超实用的CSS动画库
2024-11-18 07:23:12 +0800 CST
快速提升Vue3开发者的效率和界面
2025-05-11 23:37:03 +0800 CST
Golang 中应该知道的 defer 知识
2024-11-18 13:18:56 +0800 CST
Nginx负载均衡详解
2024-11-17 07:43:48 +0800 CST
16.6k+ 开源精准 IP 地址库
2024-11-17 23:14:40 +0800 CST
CSS 奇技淫巧
2024-11-19 08:34:21 +0800 CST
Vue3中的Store模式有哪些改进?
2024-11-18 11:47:53 +0800 CST
55个常用的JavaScript代码段
2024-11-18 22:38:45 +0800 CST
程序员茄子在线接单