第 3 期:解剖 Claude-Mem 的「大脑」— 数据存储架构全解
💡 进群学习加 wx: agentupdate
(申请发送: agentupdate)
(申请发送: agentupdate)
本期场景:你用 Claude Code 开发博客项目已经两个会话了。Web UI 上显示了一堆 Observations。问题来了:这些数据到底存在哪?长什么样?为什么需要两个数据库?
3.1 数据存储的物理位置
Claude-Mem 的所有数据都存在你本地硬盘的一个目录里:
~/.claude-mem/
├── claude-mem.db # SQLite 数据库(核心)
├── settings.json # 配置文件
├── chroma/ # ChromaDB 向量数据库
│ └── ...
└── logs/ # 运行日志
└── worker.log
没有任何数据会上传到云端。 你的开发记忆完全属于你自己。
3.2 SQLite — 结构化存储引擎
为什么选 SQLite?
| 特性 | 好处 |
|---|---|
| 零配置 | 不需要安装数据库服务器 |
| 单文件 | 整个数据库就是一个 .db 文件,复制就能备份 |
| WAL 模式 | Write-Ahead Logging,支持 Hook 和 Worker 同时读写 |
| 内置 FTS5 | 全文搜索引擎,关键词检索飞快 |
四张核心表
Claude-Mem 的 SQLite 数据库包含四张核心表。让我们逐一解剖:
表 1:sdk_sessions — 会话记录
每次你打开 Claude Code 开始工作,就会创建一条记录。
-- 查看最近 5 个会话
SELECT sdk_session_id, project, status, created_at, completed_at
FROM sdk_sessions
ORDER BY created_at DESC
LIMIT 5;
┌──────────────────┬─────────────┬───────────┬─────────────────────┐
│ sdk_session_id │ project │ status │ created_at │
├──────────────────┼─────────────┼───────────┼─────────────────────┤
│ sess_abc123 │ my-blog │ completed │ 2026-04-21 10:30:00 │
│ sess_def456 │ my-blog │ completed │ 2026-04-20 14:00:00 │
│ sess_ghi789 │ other-proj │ completed │ 2026-04-19 09:15:00 │
└──────────────────┴─────────────┴───────────┴─────────────────────┘
表 2:observations — 观察记录(核心中的核心)
每当 Claude 执行一个工具操作(读文件、写文件、运行命令),Worker 就会生成一条 Observation。
-- 查看最近的观察
SELECT id, title, type, tool_name, created_at
FROM observations
WHERE session_id = 'sess_abc123'
ORDER BY prompt_number;
┌────┬─────────────────────────────┬──────────┬────────────┬─────────────────────┐
│ id │ title │ type │ tool_name │ created_at │
├────┼─────────────────────────────┼──────────┼────────────┼─────────────────────┤
│ 1 │ 读取 prisma schema 文件 │ discovery│ Read │ 2026-04-21 10:31:00 │
│ 2 │ 添加 Comment 模型 │ feature │ Write │ 2026-04-21 10:33:00 │
│ 3 │ 运行 prisma migrate │ change │ Bash │ 2026-04-21 10:34:00 │
│ 4 │ 修复外键约束错误 │ bugfix │ Write │ 2026-04-21 10:36:00 │
│ 5 │ 选择级联删除策略 │ decision │ Write │ 2026-04-21 10:38:00 │
└────┴─────────────────────────────┴──────────┴────────────┴─────────────────────┘
每条 Observation 包含丰富的字段:
| 字段 | 含义 | 示例 |
|---|---|---|
title |
一句话标题 | "修复 JWT 刷新逻辑" |
narrative |
详细叙述(以第三人称) | "开发者发现 refreshToken 函数缺少过期检查..." |
facts |
提炼出的事实列表 | ["使用了 jsonwebtoken 库", "token 有效期 7 天"] |
concepts |
涉及的概念 | ["JWT", "认证", "中间件"] |
type |
分类(6 种) | "bugfix" |
tool_name |
触发的工具 | "Write" |
files_read |
读取的文件 | ["src/auth/jwt.ts"] |
files_modified |
修改的文件 | ["src/auth/jwt.ts"] |
表 3:user_prompts — 用户提示词
记录你在每个会话中发送给 Claude 的每一条指令。
SELECT prompt_number, content FROM user_prompts
WHERE session_id = 'sess_abc123'
ORDER BY prompt_number;
┌───────────────┬────────────────────────────────────────────┐
│ prompt_number │ content │
├───────────────┼────────────────────────────────────────────┤
│ 1 │ 帮我给博客加一个评论功能 │
│ 2 │ 评论要支持 Markdown 格式 │
│ 3 │ 修一下刚才那个外键报错 │
└───────────────┴────────────────────────────────────────────┘
表 4:session_summaries — 会话摘要
当会话结束时,AI 自动生成的结构化总结。
SELECT request, completed, next_steps FROM session_summaries
WHERE session_id = 'sess_abc123';
{
"request": "为博客添加评论系统",
"investigated": ["Prisma 的关联模型语法", "级联删除的几种策略"],
"completed": ["创建 Comment 模型", "实现评论 API", "修复外键约束"],
"next_steps": ["添加评论的前端 UI", "实现评论通知功能"]
}
注意
next_steps的魔力 —— 它会在下一次会话启动时被自动注入 Claude 的上下文,让 AI 知道"上次你还没做完什么"。
3.3 ChromaDB — 语义向量搜索引擎
SQLite 已经有 FTS5 全文搜索了,为什么还需要 ChromaDB?
因为关键词搜索和语义搜索是两种完全不同的能力:
| 搜索类型 | 引擎 | 搜索「登录 Bug」时... |
|---|---|---|
| 关键词搜索 | SQLite FTS5 | ✅ 匹配含"登录"和"Bug"的记录 ❌ 错过"authentication error"的记录 |
| 语义搜索 | ChromaDB | ✅ 匹配"登录 Bug" ✅ 也匹配"authentication error"(语义相近) ✅ 还能匹配"JWT token 过期异常" |
ChromaDB 如何工作?
graph LR
A["新的 Observation:
修复 JWT 刷新逻辑"] --> B["向量化
(文字 → 数字向量)"]
B --> C["存入 ChromaDB
[0.23, -0.41, 0.87, ...]"]
D["用户搜索:
登录功能的问题"] --> E["向量化
(搜索词 → 数字向量)"]
E --> F["向量相似度计算"]
C --> F
F --> G["返回最相近的记录"]
style A fill:#f59e0b,color:#000
style D fill:#6366f1,color:#fff
style G fill:#10b981,color:#fff简单来说:
- SQLite FTS5 = 精确匹配(像 Ctrl+F 搜索)
- ChromaDB = 理解含义的匹配(像一个懂你在说什么的同事)
两者结合,就是 Claude-Mem 的混合搜索(Hybrid Search),让你无论用什么关键词都能找到相关的历史记录。
3.4 Observation 的 6 种类型
Worker 在压缩 Observation 时,会自动分类为以下 6 种类型:
| 类型 | 含义 | 博客项目中的例子 |
|---|---|---|
decision |
架构或设计决策 | "选择 Prisma 而不是 TypeORM" |
bugfix |
修复了一个 Bug | "修复文章 slug 重复时的 500 错误" |
feature |
实现了新功能 | "添加文章标签系统" |
refactor |
重构了代码 | "将 API 路由从 pages/ 迁移到 app/" |
discovery |
发现了新知识 | "Prisma 的 findMany 默认不包含关联" |
change |
通用变更 | "更新了 .env 配置文件" |
这些类型标签让你可以精准过滤。比如想知道项目中做过哪些架构决策:
search(query="数据库", type="decision")
3.5 数据流全景
让我们把 SQLite 和 ChromaDB 的角色放到完整的数据流中:
graph TB
A["Claude 执行工具操作
(读文件/写文件/运行命令)"] --> B["PostToolUse Hook 触发"]
B --> C["Worker 接收原始工具输出"]
C --> D["Claude Agent SDK
压缩提炼"]
D --> E["生成结构化 Observation
{title, narrative, facts,
concepts, type}"]
E --> F["SQLite"]
E --> G["ChromaDB"]
F --> F1["INSERT 到 observations 表"]
F --> F2["更新 FTS5 全文索引"]
G --> G1["文本 → 向量嵌入"]
G --> G2["存入向量集合"]
H["下次搜索时"] --> I{"搜索类型?"}
I -->|"关键词精确匹配"| F
I -->|"语义模糊匹配"| G
I -->|"混合搜索(默认)"| J["FTS5 + 向量
结果合并排序"]
style A fill:#6366f1,color:#fff
style F fill:#10b981,color:#fff
style G fill:#10b981,color:#fff
style J fill:#f59e0b,color:#000实操练习
- 在博客项目中用 Claude Code 工作一个会话(比如创建数据库 Schema)
- 结束会话后,用 SQLite 工具直接查看数据:
# 打开数据库
sqlite3 ~/.claude-mem/claude-mem.db
# 查看有多少会话
SELECT COUNT(*) FROM sdk_sessions;
# 查看最近的 Observations
SELECT id, title, type FROM observations ORDER BY created_at DESC LIMIT 10;
# 查看某个会话的摘要
SELECT request, completed, next_steps FROM session_summaries LIMIT 1;
# 退出
.quit
- 打开
http://localhost:37777,在 Web UI 中对比你看到的内容和 SQL 查询结果 —— 它们应该是一致的。
下期预告
数据存好了,但数据是怎么"自动"被捕获存进去的?下一期我们揭秘 Claude-Mem 的 5 大生命周期钩子 —— 它们就是那个"偷偷记笔记"的隐形秘书。我们会用一张完整的时序图,追踪从你按下回车到数据入库的每一步。