claude-mem 深度解析:如何为 Claude Code 构建跨会话记忆系统
仓库:github.com/thedotmack/claude-mem · v13.0.1 · TypeScript · Bun · Apache-2.0
一、引言:Claude Code 的记忆困境
Claude Code 是一个强大的 AI 编程助手,但它有一个根本性问题:每次会话都是从零开始的。当你关闭一个终端窗口,下次再打开时,Claude Code 完全不记得之前讨论过什么、做过什么决策、遇到了什么问题。
这意味着你每次都要重复解释项目背景、重新描述架构决策、反复说明之前的坑。对于长期项目来说,这种"失忆"严重降低了协作效率。
claude-mem 正是为了解决这个问题而生的。它通过一套精巧的架构,让 Claude Code 拥有了跨会话的持久记忆,并且能够自动从历史交互中构建知识图谱。
二、整体架构概览
claude-mem 的架构可以分为四层:
┌─────────────────────────────────────────────────────┐
│ Claude Code │
│ (Hooks 系统 · 会话生命周期管理) │
└──────────────────────┬──────────────────────────────┘
│ 每个生命周期事件触发 bash 脚本
▼
┌─────────────────────────────────────────────────────┐
│ Hook 路由层 (hooks.json) │
│ Setup / SessionStart / UserPromptSubmit / │
│ PreToolUse / PostToolUse / Stop │
└──────────────────────┬──────────────────────────────┘
│ node bun-runner.js worker-service.js
▼
┌─────────────────────────────────────────────────────┐
│ Worker 服务 (后台常驻进程) │
│ ┌──────────┐ ┌───────────┐ ┌──────────────────┐ │
│ │ HTTP │ │ Session │ │ Knowledge │ │
│ │ Server │ │ Manager │ │ Agent │ │
│ ├──────────┤ ├───────────┤ ├──────────────────┤ │
│ │ Search │ │ Database │ │ Transcript │ │
│ │ Routes │ │ Manager │ │ Watcher │ │
│ └──────────┘ └───────────┘ └──────────────────┘ │
│ ┌──────────────────────────────────────────┐ │
│ │ AI Providers: Claude / Gemini / OpenRouter│ │
│ └──────────────────────────────────────────┘ │
└──────────────────────┬──────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ SQLite 数据库 (bun:sqlite) │
│ Sessions · Observations · Summaries · Prompts │
│ Entities · Relations · Timeline · Corpus │
└─────────────────────────────────────────────────────┘
这种分层设计的核心思想是:Claude Code 本身不需要任何修改,所有记忆功能都通过 Hooks 机制"外挂"上去,实现了零侵入集成。
三、Hook 机制:事件驱动的生命周期拦截
3.1 Claude Code Hooks 系统
Claude Code 提供了一套 Hook 系统,允许插件在特定生命周期事件发生时执行自定义脚本。claude-mem 的 hooks.json 定义了 6 个 Hook,覆盖了完整生命周期:
| Hook 名称 | 触发时机 | 执行动作 |
Setup | Claude Code 进程启动 | 版本检查(version-check.js) |
SessionStart | 新会话 / 清空 / 压缩 | 启动 Worker 守护进程 + 注入上下文 |
UserPromptSubmit | 用户提交 prompt | 注入会话初始化指令 |
PreToolUse | 工具调用前(Read) | 注入文件上下文指令 |
PostToolUse | 工具调用完成后 | 注入观察记录指令 |
Stop | 会话结束 | 注入摘要生成指令 |
3.2 Hook 的执行流程
每个 Hook 本质上是一段 bash 脚本,核心任务有三个:
- 定位插件安装路径:在
~/.claude/plugins/cache/下查找插件目录 - 解析路径:通过
cygpath处理 Windows/Unix 路径兼容性 - 调用 Worker:执行
node bun-runner.js worker-service.js <command>
以 SessionStart Hook 为例,其 bash 脚本的核心逻辑是:
# 1. 设置 PATH 环境变量
export PATH="$HOME/.nvm/versions/node/v.../bin:$HOME/.local/bin:..."
# 2. 定位插件目录
_CLAUDE_DIR="${CLAUDE_CONFIG_DIR:-$HOME/.claude}"
_PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-${PLUGIN_ROOT:-}}"
# 3. 查找包含 bun-runner.js 和 worker-service.js 的目录
for _R in "$_PLUGIN_ROOT" ...; do
[ -d "$_R/plugin/scripts" ] && _Q="$_R/plugin" || _Q="$_R"
[ -f "$_Q/scripts/bun-runner.js" ] && \
[ -f "$_Q/scripts/worker-service.cjs" ] && \
{ printf '%s\n' "$_Q"; break; }
done
# 4. 启动 Worker 守护进程
node "$_P/scripts/bun-runner.js" "$_P/scripts/worker-service.cjs" start
# 5. 注入上下文指令
echo '{"continue":true,"suppressOutput":true}'
注意最后一步返回的 JSON:{"continue":true,"suppressOutput":true}。这是 Claude Code Hook 协议的标准返回格式,告诉 Claude Code"继续执行,不要把 Hook 的输出显示给用户"。
3.3 Hook 注入的"指令"是什么?
5 个 Hook 除了启动 Worker 外,还会向 Claude Code 注入一段隐式指令,格式为:
hook claude-code <context|session-init|file-context|observation|summary>
这些指令会被 Claude Code 当作系统消息处理,触发 Worker 执行相应的后台任务:
hook claude-code context→ Worker 加载历史记忆并注入到当前会话上下文hook claude-code observation→ Worker 记录本次工具调用的结果作为"观察"hook claude-code summary→ Worker 在会话结束时生成压缩摘要
四、Worker 服务:后台常驻的核心引擎
4.1 启动与生命周期管理
Worker 是一个基于 Bun 运行时的 HTTP 服务器,通过 PID 文件实现进程管理:
// 写入 PID 文件
writePidFile({
pid: process.pid,
port,
startedAt: new Date().toISOString()
});
// 注册到 Supervisor
getSupervisor().registerProcess('worker', {
pid: process.pid,
type: 'worker',
startedAt: new Date().toISOString()
});
Worker 使用端口自动分配机制(getWorkerPort()),避免端口冲突。启动后,它会:
- 初始化 SQLite 数据库连接
- 启动 SessionManager(会话队列引擎)
- 启动 SSEBroadcaster(实时事件推送)
- 启动 HTTP Server 并注册所有路由
- 执行一次性迁移(Chroma 迁移、CWD 重映射)
- 合并所有已知仓库的 merged worktree
- 启动后台知识图谱处理
4.2 HTTP API 层
| 路由组 | 功能 |
ViewerRoutes | 前端 Viewer 的数据接口 |
SessionRoutes | 会话管理(创建、查询、删除) |
DataRoutes | 数据导出与统计 |
SearchRoutes | 跨会话搜索 |
SettingsRoutes | 配置管理 |
MemoryRoutes | 手动记忆保存 |
CorpRoutes | 语料库管理 |
ChromeRoutes | Chrome 浏览器数据同步 |
4.3 多 Provider 支持
Worker 内置了三个 AI Provider,用于执行记忆压缩和摘要任务:
- ClaudeProvider:调用 Anthropic Claude API
- GeminiProvider:调用 Google Gemini API
- OpenRouterProvider:通过 OpenRouter 调用任意模型
KnowledgeAgent 在调用 AI 时会禁用大量工具,只保留纯对话能力:
const DISALLOWED_TOOLS = [
'Bash', // 防止无限循环
'Read', // 不读文件
'Write', // 不写文件
'Edit', // 不编辑文件
'Grep', // 不搜索代码
'Glob', // 不匹配文件
'WebFetch', // 不抓取网页
'WebSearch', // 不搜索网页
'Task', // 不生成子 Agent
'NotebookEdit',
'AskUserQuestion',
'TodoWrite'
];
这确保了知识图谱构建过程是纯推理的,不会触发副作用。
五、数据存储层:SQLite 的深度运用
5.1 存储选型
claude-mem 选择 SQLite(通过 bun:sqlite)作为核心存储,而非向量数据库或文件系统:
- 零依赖:SQLite 是嵌入式的,不需要额外部署
- 事务安全:ACID 保证数据一致性
- 全文搜索:SQLite 内置 FTS5 扩展,支持中文分词
- 单文件:便于备份和迁移
5.2 核心数据模型
SessionStore(95KB,最大的模块):存储所有会话的完整数据,包括消息、工具调用、观察记录等。不仅是一个存储层,还包含复杂的查询逻辑、分页支持和 Hydration(从 JSONL 转录文件重建会话)。
Observations(观察记录):每次工具调用后记录的结构化数据:
interface Observation {
type: string; // 'discovery' | 'file_read' | 'code_change' | ...
title: string; // 简短标题
subtitle?: string; // 副标题
narrative?: string; // 详细叙述
facts: string[]; // 关键事实列表
concepts: string[]; // 概念标签
files_read: string[]; // 读取的文件
files_modified: string[]; // 修改的文件
project: string; // 所属项目
created_at: string; // 创建时间
created_at_epoch: number; // 时间戳(毫秒)
}
5.3 PendingMessageStore:消息队列
一个精巧的设计:当 Claude Code 触发一个 Hook 时,Worker 可能还在处理上一个任务。PendingMessageStore 作为消息队列,确保所有事件都不会丢失。GeneratorExitHandler 展示了优雅的处理逻辑:
if (pendingCount === 0) {
// 没有待处理消息,正常完成
session.restartGuard?.recordSuccess();
session.consecutiveRestarts = 0;
await terminateSession('Natural completion', false);
return;
}
// 还有待处理消息,需要重启 Generator
const restartAllowed = session.restartGuard?.recordRestart();
if (!restartAllowed) {
await terminateSession('Restart guard', true);
return;
}
// 指数退避重启
const backoffMs = Math.min(
1000 * Math.pow(2, session.consecutiveRestarts - 1), 8000
);
session.respawnTimer = setTimeout(() => {
restartGenerator(session, 'pending-work-restart');
}, backoffMs);
这里有一个 RestartGuard 机制:如果同一个会话在短时间内重启次数过多(超过 maxRestarts),系统会放弃处理,防止无限循环。
六、知识图谱构建:从观察到知识
6.1 观察(Observation)的产生
知识图谱的原始数据来自工具调用的观察。每当 Claude Code 执行一个工具(读文件、写代码、搜索等),PostToolUse Hook 会触发 Worker 记录一条观察。观察记录包含:
- 类型:
discovery(发现)、file_read(文件读取)、code_change(代码变更)等 - 叙述:自然语言描述本次操作的结果
- 事实:从操作中提取的关键事实列表
- 概念:操作涉及的概念标签
- 文件:涉及的文件路径
6.2 CorpusBuilder:语料库构建
CorpusBuilder 负责将分散的观察记录聚合为结构化的语料库(Corpus):
async build(name, description, filter): Promise<CorpusFile> {
// 1. 根据过滤条件搜索观察记录
const searchResult = await this.searchOrchestrator.search(searchArgs);
// 2. 提取观察 ID 列表
const observationIds = searchResult.results.observations.map(obs => obs.id);
// 3. 从数据库中获取完整的观察记录
const observationRows = this.sessionStore.getObservationsByIds(observationIds);
// 4. 映射为语料库格式 + 计算统计信息
// 5. 生成 system prompt + 估算 token 数量
// 6. 持久化存储
this.corpusStore.write(corpus);
}
6.3 KnowledgeAgent:知识图谱 Agent
KnowledgeAgent 是整个知识图谱系统中最核心的组件。它的工作原理是:用一个受限的 Claude Agent 实例,从语料库中推理并提取知识。关键设计:
- Prime(预热)阶段:先将完整的语料库作为 system prompt 注入 Agent,让 Agent "了解"已有知识
- Query 阶段:用自然语言查询 Agent,Agent 返回结构化的答案
- Session 管理:每个语料库对应一个独立的 Agent 会话,支持会话恢复和自动续期
async prime(corpus: CorpusFile): Promise<string> {
const renderedCorpus = this.renderer.renderCorpus(corpus);
const primePrompt = [
corpus.system_prompt,
'',
'Here is your complete knowledge base:',
'',
renderedCorpus,
'',
'Acknowledge what you\'ve received.',
'Summarize the key themes and topics you can answer questions about.'
].join('\n');
// 使用受限的 Agent SDK 调用 — 禁用所有副作用工具
const queryResult = query({
prompt: primePrompt,
options: {
model: this.getModelId(),
disallowedTools: DISALLOWED_TOOLS,
...
}
});
}
七、搜索系统:多策略检索
7.1 SearchOrchestrator
搜索系统采用策略模式,SearchOrchestrator 作为编排器,根据查询参数选择合适的搜索策略:
src/services/worker/search/
├── SearchOrchestrator.ts ← 搜索编排器
├── ResultFormatter.ts ← 结果格式化
├── TimelineBuilder.ts ← 时间线构建
├── types.ts ← 类型定义
├── errors.ts ← 错误处理
├── strategies/ ← 搜索策略
└── filters/ ← 过滤器
搜索支持的过滤维度:project、type、concepts、files、query(全文关键词)、date_start / date_end(时间范围)、limit。
7.2 SQLite FTS5 全文搜索
底层搜索使用 SQLite 的 FTS5(Full-Text Search 5)扩展。SessionSearch 模块(20KB)实现了中英文分词、相关性排序、高亮匹配、多字段联合搜索。
八、转录监控:TranscriptWatcher
Claude Code 会将每次会话的完整对话记录保存为 JSONL 文件(位于 ~/.claude/projects/ 下)。TranscriptWatcher 组件监控这些文件的变化:
- 实时捕获:当新的 JSONL 文件出现时,自动开始处理
- 增量更新:当文件内容增长时,只处理新增部分
- 会话重建:从 JSONL 文件中重建完整的会话上下文
这是 claude-mem 能够"看到"所有历史会话的关键机制。
九、多语言与 MCP 集成
plugin/modes/ 目录下有 20+ 个语言配置文件(code--zh.json、code--ja.json、code--fr.json 等),每个文件定义了该语言下的 prompt 模板、行为配置和输出格式偏好。
claude-mem 通过 MCP(Model Context Protocol)向 Claude Code 暴露搜索工具:search(跨会话搜索)、timeline(查看时间线)、corpus(管理语料库)。此外,ChromeSync 组件可以与 Chrome 浏览器扩展配合,将浏览器操作也纳入记忆系统。
十、与其他记忆方案的对比
| 维度 | claude-mem | OpenClaw MEMORY.md | Mem0 | Letta (MemGPT) |
| 集成方式 | Claude Code Hooks | 文件系统 | API 服务 | API 服务 |
| 存储 | SQLite | Markdown 文件 | 向量数据库 | SQLite + 向量 |
| 知识表示 | 观察 + 知识图谱 | 纯文本 | 向量嵌入 | 分层记忆 |
| 搜索 | FTS5 全文搜索 | 语义搜索 | 向量相似度 | 混合搜索 |
| 自动化 | 全自动(Hook 驱动) | 手动/半自动 | API 调用 | API 调用 |
| 适用场景 | Claude Code 专属 | 通用 Agent | 通用 | 通用 |
十一、总结
通过 Claude Code 的 Hooks 机制零侵入地拦截会话事件,用后台 Worker 异步处理观察记录,存入 SQLite 数据库,并用受限的 AI Agent 实例从观察中构建知识图谱,最终通过 MCP 工具和 Hook 注入实现跨会话记忆的自动检索与注入。
它的精妙之处在于:
- 零侵入:完全通过 Hooks 实现,不修改 Claude Code 任何代码
- 事件驱动:所有处理都是异步的,不阻塞用户交互
- 自动压缩:用 AI 自己来总结和压缩历史记忆
- 知识图谱:不是简单的文本存储,而是结构化的实体-关系图谱
- 优雅降级:RestartGuard、PendingMessageStore 等机制确保系统稳定性
当然,它也有局限性——强绑定 Claude Code 生态、依赖 Bun 运行时、知识图谱质量取决于底层模型能力。但对于 Claude Code 用户来说,这可能是目前最成熟的跨会话记忆解决方案。