跳转至

数据库架构

Claude-Mem 使用 SQLite 3 及其 bun:sqlite 原生模块进行持久化存储,并使用 FTS5 进行全文搜索。

数据库位置

路径:~/.claude-mem/claude-mem.db

数据库使用 SQLite 的 WAL(预写日志)模式以支持并发读写。

数据库实现

主要实现:bun:sqlite(原生 SQLite 模块) - 由 SessionStore 和 SessionSearch 使用 - 格式:同步 API,性能更佳 - 注意:Database.ts(使用 bun:sqlite)是遗留代码

核心表

1. 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
);

索引: - claude_session_id 上的 idx_sdk_sessions_claude_session - project 上的 idx_sdk_sessions_project - status 上的 idx_sdk_sessions_status - created_at_epoch DESC 上的 idx_sdk_sessions_created_at

2. 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,

  -- Hierarchical fields
  title TEXT,
  subtitle TEXT,
  narrative TEXT,
  text TEXT,
  facts TEXT,
  concepts TEXT,
  type TEXT,
  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)
);

观察类型: - decision - 架构或设计决策 - bugfix - 缺陷修复与更正 - feature - 新功能或新能力 - refactor - 代码重构与清理 - discovery - 对代码库的认知发现 - change - 常规变更与修改

索引: - session_id 上的 idx_observations_session - sdk_session_id 上的 idx_observations_sdk_session - project 上的 idx_observations_project - tool_name 上的 idx_observations_tool_name - created_at_epoch DESC 上的 idx_observations_created_at - type 上的 idx_observations_type

3. 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,

  -- Summary fields
  request TEXT,
  investigated TEXT,
  learned TEXT,
  completed TEXT,
  next_steps TEXT,
  notes TEXT,

  created_at TEXT NOT NULL,
  created_at_epoch INTEGER NOT NULL,

  FOREIGN KEY (sdk_session_id) REFERENCES sdk_sessions(sdk_session_id)
);

索引: - sdk_session_id 上的 idx_session_summaries_sdk_session - project 上的 idx_session_summaries_project - created_at_epoch DESC 上的 idx_session_summaries_created_at

4. user_prompts

原始用户提示词,支持 FTS5 搜索(自 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,

  FOREIGN KEY (sdk_session_id) REFERENCES sdk_sessions(sdk_session_id)
);

索引: - sdk_session_id 上的 idx_user_prompts_sdk_session - project 上的 idx_user_prompts_project - created_at_epoch DESC 上的 idx_user_prompts_created_at

5. tool_uses

原始工具 I/O 的持久化备份索引(schema v51)。它从同一摄取汇聚点写入——该汇聚点也为观察生成器供数——因此能捕获相同的调用,而无需新增第二条捕获路径。

CREATE TABLE tool_uses (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  tool_use_id TEXT NOT NULL,
  content_session_id TEXT NOT NULL,
  memory_session_id TEXT,
  session_db_id INTEGER,
  project TEXT NOT NULL,
  platform_source TEXT NOT NULL DEFAULT 'claude',
  tool_name TEXT NOT NULL,
  tool_input TEXT,
  tool_response TEXT,
  cwd TEXT,
  prompt_number INTEGER,
  agent_type TEXT,
  agent_id TEXT,
  observation_id INTEGER,
  or_generation_id TEXT,
  or_session_id TEXT,
  content_hash TEXT,
  created_at TEXT NOT NULL,
  created_at_epoch INTEGER NOT NULL,

  UNIQUE(content_session_id, tool_use_id)
);

存在原因:pending_messages 是生成队列——行会被认领、汇总并删除,因此一旦观察已生成,原始工具载荷便无法恢复。tool_uses 是可在队列清理后依然存留的持久化侧索引,get_tool_uses MCP 工具(渐进式披露层 4)读取的正是该表。

并非会话记录存储:磁盘上的 JSONL 转录记录及转录监视器仍是会话历史的主干。此表只是工具主体的引用型索引,仅此而已。

幂等性:UNIQUE(content_session_id, tool_use_id) 意味着重放的 PostToolUse(钩子重试、转录重新扫描)会更新该行而不是重复插入。observation_id 在生成之后才关联,一旦设置便不会被覆盖。

载荷上限:tool_input / tool_response 在 64 KB 处截断,并附带 …[truncated: N bytes] 标记。content_hash 基于截断前的原始载荷计算。

成本列:故意不设置。or_generation_id 和 or_session_id 是可空连接键,指向 OpenRouter 的支出记录行;金额存放在那一行中,绝不会出现在这里。

索引:project、memory_session_id、content_session_id、session_db_id、tool_name、created_at_epoch、observation_id、or_generation_id

遗留表

  • sessions:遗留会话跟踪(v3.x)
  • memories:遗留压缩记忆块(v3.x)
  • overviews:遗留会话摘要(v3.x)

SQLite FTS5(全文搜索)虚拟表可对观测记录和会话摘要执行快速全文搜索。用户提示通过子串匹配(LIKE)进行搜索,没有 FTS 索引。

FTS5 虚拟表

observations_fts

CREATE VIRTUAL TABLE observations_fts USING fts5(
  title,
  subtitle,
  narrative,
  text,
  facts,
  concepts,
  content='observations',
  content_rowid='id'
);

session_summaries_fts

CREATE VIRTUAL TABLE session_summaries_fts USING fts5(
  request,
  investigated,
  learned,
  completed,
  next_steps,
  notes,
  content='session_summaries',
  content_rowid='id'
);

自动同步

FTS5 表通过触发器保持同步。更新触发器仅在写入索引列时触发,因此维护性更新(同步修订、项目合并、内容哈希)永远不会触及索引。每次索引更新都会追加一条删除标记以及该行文本的重新插入,因此未限定列的更新触发器会在每次此类写入时增大索引:

-- Insert trigger example
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;

-- Update trigger example (fires only when an indexed column is written)
CREATE TRIGGER observations_au
AFTER UPDATE OF title, subtitle, narrative, text, facts, concepts 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;

-- Delete trigger example
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 查询语法

FTS5 支持丰富的查询语法:

  • 简单:"error handling"
  • AND(与):"error" AND "handling"
  • OR(或):"bug" OR "fix"
  • NOT(非):"bug" NOT "feature"
  • 短语:'exact phrase'
  • 列:title:"authentication"

安全性

自 v4.2.3 起,所有 FTS5 查询都会正确转义以防止 SQL 注入: - 双引号会被转义:query.replace(/"/g, '""') - 全面测试套件包含 332 项注入攻击测试

数据库类

SessionStore

针对会话、观测、摘要和用户提示的 CRUD 操作。

位置:src/services/sqlite/SessionStore.ts

方法: - createSession() - getSession() - updateSession() - createObservation() - getObservations() - createSummary() - getSummaries() - createUserPrompt()

SessionSearch

FTS5 全文搜索,提供 8 种专用搜索方法。

位置:src/services/sqlite/SessionSearch.ts

方法: - searchObservations() - 在观测记录中进行全文搜索 - searchSessions() - 在会话摘要中进行全文搜索 - searchUserPrompts() - 在用户提示中进行全文搜索 - findByConcept() - 按概念标签查找 - findByFile() - 按文件引用查找 - findByType() - 按观测类型查找 - getRecentContext() - 获取最近的会话上下文 - advancedSearch() - 组合筛选

迁移

数据库模式通过 src/services/sqlite/migrations.ts 中的迁移进行管理。

迁移历史: - 迁移 001:初始模式(sessions、memories、overviews、diagnostics、transcript_events) - 迁移 002:分层记忆字段(title、subtitle、facts、concepts、files_touched) - 迁移 003:SDK 会话和观测 - 迁移 004:会话摘要 - 迁移 005:多提示词会话(prompt_counter、prompt_number) - 迁移 006:FTS5 虚拟表和触发器 - 迁移 007-010:各项改进及用户提示表 - 迁移 051:tool_uses 持久化原始工具 I/O 索引

性能注意事项

  • 索引:所有外键和频繁查询的列均已建立索引
  • FTS5:全文搜索明显快于 LIKE 查询
  • 触发器:自动同步开销极小
  • 连接池:bun:sqlite 高效地复用连接
  • 同步 API:bun:sqlite 使用同步 API 以获得更好的性能

故障排除

常见问题及解决方案请参阅 故障排除 - 数据库问题。

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 cl/claude-mem