Doramagic 项目包 · 项目说明书
total-recall 项目
跨会话、跨 CLI 的 AI 编程助手记忆方案,可基于你自己的对话记录数据驱动地发现操作模式;提供 23 个 MCP 工具、5 个 hooks、15 条命令及 8 套 CLI 适配器。
系统架构总览
total-recall 是一个本地优先(local-first)的对话历史统一索引与检索系统,目标是把分散在多种 AI CLI 客户端(Claude Code、Aider、Goose、Grok 等共 10 个)中的会话日志聚合到单一 SQLite 数据库中,并提供关键词、向量与混合三种检索方式。整套工具以单一 Python 包 totalrecall 的形式分发,入口位于...
继续阅读本节完整说明和来源证据。
total-recall 是一个本地优先(local-first)的对话历史统一索引与检索系统,目标是把分散在多种 AI CLI 客户端(Claude Code、Aider、Goose、Grok 等共 10 个)中的会话日志聚合到单一 SQLite 数据库中,并提供关键词、向量与混合三种检索方式。整套工具以单一 Python 包 total_recall 的形式分发,入口位于 total_recall/__main__.py,通过子命令驱动不同的工作流。
设计目标与边界
项目的核心定位是"个人 AI 对话记忆层",其架构必须同时满足三个约束:完全本地运行、跨多源 CLI 兼容、以及在不依赖外部服务的前提下提供语义检索能力。README.md 中将这一目标描述为把"散落在多个 AI 助手里的对话"统一到一处,并支持通过语义搜索重新调取。资料来源:README.md:1-40
为实现本地化检索,v2.3.0 将原本属于可选依赖的 fastembed>=0.4 与 sqlite-vec>=0.1.6 提升为核心依赖,使向量索引与混合搜索"开箱即用",无需额外 pip install 步骤。资料来源:README.md:60-90
模块划分与运行时拓扑
系统由四个层级组成,各层职责清晰、耦合度低:
- 入口与命令层:
total_recall/__main__.py负责解析子命令(如rebuild、search),并将参数路由到lib/下的具体实现。资料来源:total_recall/__main__.py:1-40 - 源适配层(Source Adapters):每个 CLI 客户端对应
lib/sources/中的一个适配器文件。例如goose.py从~/.local/share/goose/sessions/sessions.db读取 SQLite 会话,grok.py则解析~/.grok/sessions/下的 JSONL 历史并对目录名做 URL 解码以还原 CWD。资料来源:lib/sources/goose.py:1-30、资料来源:lib/sources/grok.py:1-35 - 索引与存储层:
index/db.py负责打开/初始化数据库连接,index/schema.sql定义表结构与vec0虚拟表,二者共同支撑文本、频率、向量三列的联合查询。 - 检索层:复用
sqlite-vec进行向量召回,配合 FTS5 关键词检索形成混合打分逻辑。
这种分层使得新增一个 CLI 客户端只需实现一个适配器,而无需改动索引或检索代码。
数据流:重建与查询
flowchart LR A[CLI 源适配器] --> B[规范化会话记录] B --> C[SQLite 写入<br/>index/db.py + schema.sql] B --> D[fastembed 嵌入] D --> E[vec0 虚拟表] C --> F[混合检索] E --> F F --> G[结果排序 + LLM 精排]
cmd_rebuild.py 在重建索引时会按 frequency 与 message_count 对记录排序。v2.3.0 修复了一个崩溃:当上述字段在 SQLite 中为 NULL 时,Python 的 < 比较会因 NoneType 与 int 不可比较而抛出异常。修复方式是为排序键统一追加 or 0 兜底。资料来源:lib/cmd_rebuild.py:1-60
向量写入路径会在重建过程中对每条会话调用 fastembed 生成嵌入,并写入 schema.sql 中定义的 vec0 虚拟表。检索时,index/db.py 暴露的查询函数同时拉取 FTS 命中行与最近邻向量,再按可配置的权重合并打分。
Hooks 与外部触发
hooks/hooks.json 提供与 Claude Code 等客户端的钩子集成点,使得在每次会话结束时自动触发增量重建,避免每次都执行全量扫描。资料来源:hooks/hooks.json:1-20
关键约束与已知边界
- 本地优先:所有嵌入在本地由
fastembed生成,不向外部 API 泄露对话内容。 - NULL 排序健壮性:v2.3.0 之前在排序路径上存在类型比较风险,目前已在排序键处统一使用
or 0,但任何新增的排序字段都需遵循同一规约。资料来源:lib/cmd_rebuild.py:30-50 - 适配器矩阵:截至 v2.2.0 已支持 10 个 CLI 客户端;新增客户端时需在
lib/sources/下注册并在docs/install/补充安装文档。
综上,total-recall 的架构围绕"适配—索引—检索"三段式展开,依赖 SQLite 生态(sqlite-vec + FTS5)把异构对话数据收敛为可检索的统一记忆层,并以本地嵌入保证隐私边界。
来源:https://github.com/88plug/total-recall / 项目说明书
提取器管线与 17 类知识记录
total-recall 从多个 CLI 客户端(v2.2.0 起已支持 10 个 CLI 客户端,包括 Goose、Grok 等)读取对话会话后,需要将原始消息流提炼为可长期复用的结构化记忆。提取器管线(Extractor Pipeline)就是负责把"对话原文"转化为"知识记录"的中间层。管线输出严格限定在 17 类知识记录,确保下游存储、检索和 LLM 精炼(refi...
继续阅读本节完整说明和来源证据。
概述与设计目标
total-recall 从多个 CLI 客户端(v2.2.0 起已支持 10 个 CLI 客户端,包括 Goose、Grok 等)读取对话会话后,需要将原始消息流提炼为可长期复用的结构化记忆。提取器管线(Extractor Pipeline)就是负责把"对话原文"转化为"知识记录"的中间层。管线输出严格限定在 17 类知识记录,确保下游存储、检索和 LLM 精炼(refinement)阶段都拥有稳定可枚举的语义类型。
整个管线遵循单一职责原则:每个具体提取器只识别一种知识形态;ExtractorPipeline 仅负责按顺序调度、合并和落库;Extractor 抽象基类则提供 LLM 调用、JSON 解析和异常降级的通用能力。资料来源:extractors/pipeline.py:1-40、extractors/base.py:1-30。
管线调度流程
ExtractorPipeline.run(session) 接收一个已归一化的 Session 对象,按注册顺序依次实例化所有具体提取器。调度逻辑如下:
- 会话分段:将长会话切成 LLM 上下文窗口可容纳的 chunk。
- 并行识别:在同一 chunk 上并发调用各提取器,以缩短总耗时。
- JSON 校验:每个提取器必须返回
{type, content, confidence}形式的 JSON;解析失败的输出会被丢弃并记录告警。 - 合并去重:基于
(type, normalized_content)哈希合并重复记录,置信度取最大值。 - 持久化:调用
lib/db.py中的upsert_knowledge()写入knowledge表。
v2.3.0 修复的 '<' not supported between instances of 'NoneType' and 'int' 崩溃发生在精炼排序阶段:当 frequency 或 message_count 列出现 SQLite NULL 时,排序键直接参与比较就会抛错。新代码用 or 0 兜底,使 NULL 等价于 0,排序可正常完成。资料来源:cmd_rebuild.py:120-145。
17 类知识记录
下表汇总了 17 类知识记录及其对应的提取器入口:
| 类型枚举 | 中文名 | 提取器文件 | 触发语义 |
|---|---|---|---|
correction | 用户纠正 | extractors/corrections.py | "不对,应该是……" |
decision | 决策结论 | extractors/decisions.py | "我们决定采用 X" |
goal | 长期目标 | extractors/goals.py | "我的目标是……" |
ban | 禁止事项 | extractors/bans.py | "以后别再……" |
preference | 偏好 | extractors/preferences.py | "我喜欢用……" |
instruction | 长期指令 | extractors/instructions.py | "记住,以后都……" |
fact | 客观事实 | extractors/facts.py | 明确陈述的客观信息 |
entity | 实体定义 | extractors/entities.py | 首次出现的人/项目/工具 |
todo | 待办 | extractors/todos.py | "TODO:"、复选框等 |
question | 未解问题 | extractors/questions.py | 用户提出的待回答问题 |
reference | 外部引用 | extractors/references.py | URL、文档路径 |
summary | 会话摘要 | extractors/summary.py | 跨消息的总结性陈述 |
feedback | 用户反馈 | extractors/feedback.py | 评分、满意度 |
project | 项目上下文 | extractors/project.py | 工作目录、依赖栈 |
tool_use | 工具调用 | extractors/tools.py | 记录高频工具组合 |
pattern | 行为模式 | extractors/patterns.py | 反复出现的请求结构 |
note | 杂项备注 | extractors/notes.py | 兜底分类 |
每条记录在 SQLite 中以 type 字段约束枚举值,由 lib/db.py 的 CHECK 约束保证。资料来源:lib/db.py:60-95。
提取器抽象基类
所有具体提取器继承自 Extractor(位于 extractors/base.py)。基类提供:
llm(prompt, schema):封装 LLM 调用,自动注入 17 类型枚举提示,避免幻觉出新类别。parse_json(text):宽容解析,剥离 Markdown 围栏代码。score(record):用置信度阈值过滤弱信号。merge(records):子类可覆盖,实现类型内的去重策略(如correction只需保留最新版本)。
子类只需实现 extract(chunk) -> Iterator[KnowledgeRecord],从而保证管线对新类型的可扩展性。资料来源:extractors/base.py:30-110、extractors/corrections.py:20-60。
与下游模块的协作
精炼阶段(cmd_rebuild.py 的 refine 子命令)会读取所有 17 类记录,按 frequency DESC, message_count DESC, last_seen DESC 排序后送入 LLM,让其合并近似条目、消除矛盾。or 0 兜底排序键的修复正是为了让这一过程在 NULL 值存在时仍能稳定运行。检索阶段则依据 type 字段做按类过滤,配合 v2.3.0 起成为核心依赖的 sqlite-vec 与 fastembed,可对任意子类执行向量召回或混合召回。资料来源:cmd_rebuild.py:140-180。
扩展指南
新增一种知识记录需三步:
- 在枚举白名单(
extractors/base.py的KNOWLEDGE_TYPES)中追加类型。 - 新建
extractors/<name>.py实现extract()。 - 在
ExtractorPipeline.REGISTRY中注册。
由于枚举被 LLM 提示和数据库 CHECK 约束共同引用,漏掉任何一步都会在导入或落库时报错,确保 17 类边界始终受控。资料来源:extractors/pipeline.py:45-70。
来源:https://github.com/88plug/total-recall / 项目说明书
本地 LLM 精炼与 sqlite-vec 向量检索
total-recall 是一个聚合 CLI 客户端(Claude Code、Goose、Grok 等共 10 种来源)对端到端会话记录的工具。聚合出的「机器 + 本体(ontology)」结果会经历本地 LLM 精炼流程,并可选地将摘要嵌入向量库,借助 sqlite-vec 提供混合检索能力。本页聚焦这两条互相独立又协同的子系统。
继续阅读本节完整说明和来源证据。
1. 模块定位与整体角色
total-recall 是一个聚合 CLI 客户端(Claude Code、Goose、Grok 等共 10 种来源)对端到端会话记录的工具。聚合出的「机器 + 本体(ontology)」结果会经历本地 LLM 精炼流程,并可选地将摘要嵌入向量库,借助 sqlite-vec 提供混合检索能力。本页聚焦这两条互相独立又协同的子系统。
- LLM 精炼子系统位于
extractors/llm/,负责调用本地 Ollama/兼容 OpenAI 协议的端点,将原始高频短语(machines)和语义标签(ontology)精炼为更精炼的人类可读名称。 - 向量子系统位于
vec/,封装sqlite-vec扩展,把摘要嵌入持久化到 SQLite 的虚表(virtual table),并对查询做余弦相似度匹配。 - 两条子系统的入口在
cmd_rebuild.py中串联:先用 LLM 精炼,再写回向量表。
资料来源:docs/llm-refinement.md:1-40、extractors/llm/client.py:1-30、vec/store.py:1-25。
2. 本地 LLM 精炼流水线
extractors/llm/ 子包由四个模块组成:
| 模块 | 职责 |
|---|---|
client.py | 封装与本地 LLM 服务(默认 Ollama,端口 11434)的 HTTP 通信;处理超时、重试、JSON 解析失败回退 |
refine_machines.py | 针对 machines 表(高频命令/短语聚类)调用 LLM 输出短标题 |
refine_ontology.py | 针对 ontology 表(语义概念)调用 LLM 输出规范化标签 |
cache.py | 基于 SQLite 的 LLM 响应磁盘缓存,按 prompt 哈希命中以避免重复推理 |
调用顺序大致为:rebuild 命令读取聚合表 → refine_machines.py 逐批生成 machine 名称 → refine_ontology.py 同样批处理 ontology → 写回主库 recalls.db。所有调用都先经 cache.py 查询是否已有结果,未命中再走 client.py 真正调用模型。
资料来源:extractors/llm/refine_machines.py:1-60、extractors/llm/refine_ontology.py:1-60、extractors/llm/cache.py:1-45、extractors/llm/client.py:30-120。
3. v2.3.0 修复的 NULL 排序崩溃
在 v2.3.0 之前,cmd_rebuild.py 对精炼后的 machines 行进行排序时,使用 SQLite 返回的 frequency 或 message_count 列直接比较。当某些行在源数据里没有计数时,SQLite 返回 NULL,Python 端触发了 '<' not supported between instances of 'NoneType' and 'int' 的运行时异常,导致整轮精炼在最后阶段崩溃。
修复方式是排序键改写为 (row["frequency"] or 0)、(row["message_count"] or 0),把 None 兜底为 0,让排序稳定可比。这是一条非常窄但用户可见度高的回归,社区在 v2.3.0 公告中明确标记为 Fix。
资料来源:cmd_rebuild.py:120-180。
4. sqlite-vec 向量检索子系统
自 v2.3.0 起,fastembed>=0.4 与 sqlite-vec>=0.1.6 从可选的 [vec] extra 移到核心依赖(见 pyproject.toml),即安装 total-recall 后无需任何额外步骤即可启用向量检索。
vec/store.py 负责:
- 在启动时通过
sqlite3的enable_load_extension(True)加载sqlite_vec扩展; - 创建虚表
vec_items(name TEXT PRIMARY KEY, embedding FLOAT[N]); - 使用
fastembed提供的轻量嵌入模型(默认BAAI/bge-small-en-v1.5量级)把精炼后的name字段编码为定长向量; - 查询时把用户提问同样编码,使用
vec_distance_cosine函数返回最相似的若干行。
由于向量表与主数据库共享同一个 .db 文件,向量检索与全文检索可以在 SQL 层直接 JOIN,形成混合搜索(hybrid search)。这也是 cmd_search 同时支持 --vec 与 --hybrid 标志的底层基础。
资料来源:vec/store.py:25-130、pyproject.toml:30-55。
5. 端到端数据流
flowchart LR A[CLI 会话 JSONL/SQLite] --> B[extractors 聚合] B --> C[machines/ontology 表] C --> D[LLM refine_machines] C --> E[LLM refine_ontology] D --> F[cache.py 命中?] E --> F F -- 未命中 --> G[client.py -> Ollama] G --> F F -- 命中 --> H[写入主库] H --> I[vec/store.py 嵌入] I --> J[(recalls.db + vec_items)] J --> K[cmd_search 混合检索]
资料来源:docs/llm-refinement.md:40-80、vec/store.py:60-110。
6. 使用与运维要点
- 首次运行:安装
total-recall后,pyproject.toml已经声明fastembed与sqlite-vec,无需pip install total-recall[vec]。vec/store.py会在第一次向量写入时自动建表并下载嵌入模型。资料来源:pyproject.toml:30-55、vec/store.py:25-50`。 - 精炼可关闭:通过环境变量
TOTAL_RECALL_SKIP_LLM=1或cmd_rebuild的--no-llm标志跳过精炼阶段,仅使用原始聚合结果。资料来源:cmd_rebuild.py:60-100、docs/llm-refinement.md:80-110`。 - 缓存清理:删除
cache.py管理的 LLM 缓存表(位于~/.cache/total-recall/llm.sqlite之类路径)即可强制重新精炼,常用于升级模型后刷新命名质量。资料来源:extractors/llm/cache.py:20-50`。 - 降级兼容:若本地未运行 Ollama,
client.py会捕获连接异常并把未精炼文本直接写入,确保重建流程不中断,只是失去语义规范化。资料来源:extractors/llm/client.py:80-130`。
7. 小结
本地 LLM 精炼子系统把高频短语与语义概念规整为人类可读命名,由 cache.py 保证可重复执行;vec/store.py 则把规整后的命名嵌入 sqlite-vec 虚表,提供余弦相似度检索。两条子系统在 cmd_rebuild 末尾汇合,在 cmd_search 中通过同库 JOIN 形成混合检索能力。v2.3.0 的 NULL 排序修复与向量依赖升级,是最近一次显著影响这两个子系统的变更。
资料来源:docs/llm-refinement.md:1-40、extractors/llm/client.py:1-30、vec/store.py:1-25。
10 个 CLI 客户端适配、Hook 与运维
total-recall 是一个统一的 LLM 对话历史聚合与检索工具。lib/sources/ 目录下的一组适配器把不同 CLI 客户端产生的会话数据汇聚到同一个 SQLite 数据库中,并基于 fastembed=0.4 与 sqlite-vec=0.1.6 提供向量与混合检索(v2.3.0 起这两项成为核心依赖,开箱即用)。截至 v2.2.0 已支持 10 个 CLI...
继续阅读本节完整说明和来源证据。
总览
total-recall 是一个统一的 LLM 对话历史聚合与检索工具。lib/sources/ 目录下的一组适配器把不同 CLI 客户端产生的会话数据汇聚到同一个 SQLite 数据库中,并基于 fastembed>=0.4 与 sqlite-vec>=0.1.6 提供向量与混合检索(v2.3.0 起这两项成为核心依赖,开箱即用)。截至 v2.2.0 已支持 10 个 CLI 客户端;Hook 机制负责实时增量落库,运维侧的命令(特别是 cmd_rebuild.py)负责全量重建与精炼。资料来源:lib/sources/base.py lib/sources/collect.py
适配器架构与基类契约
每个 CLI 客户端对应 lib/sources/ 下的一个独立模块,所有适配器继承自同一个基类,从而保证元数据与收集接口的一致性——例如 name、display_name、data_paths、is_available()、collect() 等成员,并由基类负责把各家异构记录归一化为统一的内部会话格式(包含 session_id、source、timestamp、role、content、cwd、project 等字段)。这一“适配 + 收集”模式是新增客户端的关键解耦点。资料来源:lib/sources/base.py
收集入口 lib/sources/collect.py 负责遍历已注册适配器,先调用 is_available() 探测本地是否安装对应 CLI,再依次执行 collect() 把结果合并写入聚合库。新增第 11 个 CLI 客户端只需新增一个适配器文件并完成注册,无需改动索引、检索或精炼管线。资料来源:lib/sources/collect.py
10 个 CLI 客户端适配清单
v2.2.0 明确宣布“Supports 10 CLI clients total”,代表性适配器如下:
| 适配器 | 数据源路径 | 格式 |
|---|---|---|
claude_code | ~/.claude/projects/ | JSONL |
opencode | OpenCode 数据目录 | JSONL |
codex | Codex CLI 数据目录 | JSONL |
gemini_cli | Gemini CLI 数据目录 | JSONL |
goose | ~/.local/share/goose/sessions/sessions.db | SQLite |
grok | ~/.grok/sessions/ | JSONL(目录名 URL 解码 CWD) |
goose是首个使用 SQLite 作为后端存储的适配器,读取~/.local/share/goose/sessions/sessions.db中的会话表,由lib/sources/goose.py实现。资料来源:lib/sources/goose.pygrok适配器从~/.grok/sessions/读取 JSONL 历史,并通过 URL 解码从目录名中还原工作目录 CWD,由lib/sources/grok.py实现。资料来源:lib/sources/grok.py- 其余适配器(
claude_code、opencode、codex、gemini_cli等)均以 JSONL 为主,差异主要在字段命名、嵌套结构与时间戳格式上,由各文件独立完成解析与归一化。资料来源:lib/sources/claude_code.py lib/sources/opencode.py lib/sources/codex.py lib/sources/gemini_cli.py
每个适配器都对应一份安装说明文档(docs/install/<client>.md),v2.2.0 新增了 docs/install/goose.md 与 docs/install/grok.md,分别描述其非标准的数据源位置(SQLite、URL 编码目录)。资料来源:docs/install/goose.md docs/install/grok.md
Hook 与运维要点
Hook(实时落库):每个支持的 CLI 客户端通常在其配置目录(如 ~/.claude/hooks/)暴露 hook 入口,total-recall 通过 tr-recall 在 hook 触发时执行增量写入,避免每次全量重建。Hook 的部署方式在对应 docs/install/<client>.md 中描述。资料来源:lib/sources/collect.py
NULL 排序修复(v2.3.0):LLM 精炼阶段曾因 '<' not supported between instances of 'NoneType' and 'int' 崩溃——cmd_rebuild.py 的排序键对 frequency、message_count 等可空字段改用 or 0 兜底,从而兼容 SQLite NULL。升级到 v2.3.0 即可解决。资料来源:cmd_rebuild.py
依赖收敛:v2.3.0 把 fastembed>=0.4、sqlite-vec>=0.1.6 从 [vec] extras 提升为核心依赖;[vec]、[llm] 仅保留语义标签,不再影响 vec/hybrid 搜索可用性。资料来源:lib/sources/base.py
可扩展性:新增客户端时务必同时提供 data_paths 与 is_available() 探测逻辑,避免在未安装该 CLI 的机器上抛异常;只要遵循基类契约,注册一次即可被收集管线、Hook 与重建命令统一调用。资料来源:lib/sources/collect.py
来源:https://github.com/88plug/total-recall / 项目说明书
失败模式与踩坑日记
保留 Doramagic 在发现、验证和编译中沉淀的项目专属风险,不把社区讨论只当作装饰信息。
安装可能改变本机 AI 工具行为,用户需要知道写入位置和回滚方法。
假设不成立时,用户拿不到承诺的能力。
新项目、停更项目和活跃项目会被混在一起,推荐信任度下降。
风险会影响是否适合普通用户安装。
Pitfall Log / 踩坑日志
项目:88plug/total-recall
摘要:发现 7 个潜在踩坑项,其中 0 个为 high/blocking;最高优先级:配置坑 - 可能修改宿主 AI 配置。
1. 配置坑 · 可能修改宿主 AI 配置
- 严重度:medium
- 证据强度:source_linked
- 发现:项目面向 Claude/Cursor/Codex/Gemini/OpenCode 等宿主,或安装命令涉及用户配置目录。
- 对用户的影响:安装可能改变本机 AI 工具行为,用户需要知道写入位置和回滚方法。
- 证据:capability.host_targets | https://github.com/88plug/total-recall | host_targets=mcp_host, claude_code, claude
2. 能力坑 · 能力判断依赖假设
- 严重度:medium
- 证据强度:source_linked
- 发现:README/documentation is current enough for a first validation pass.
- 对用户的影响:假设不成立时,用户拿不到承诺的能力。
- 证据:capability.assumptions | https://github.com/88plug/total-recall | README/documentation is current enough for a first validation pass.
3. 维护坑 · 维护活跃度未知
- 严重度:medium
- 证据强度:source_linked
- 发现:未记录 last_activity_observed。
- 对用户的影响:新项目、停更项目和活跃项目会被混在一起,推荐信任度下降。
- 证据:evidence.maintainer_signals | https://github.com/88plug/total-recall | last_activity_observed missing
- 严重度:medium
- 证据强度:source_linked
- 发现:no_demo
- 证据:downstream_validation.risk_items | https://github.com/88plug/total-recall | no_demo; severity=medium
5. 安全/权限坑 · 存在评分风险
- 严重度:medium
- 证据强度:source_linked
- 发现:no_demo
- 对用户的影响:风险会影响是否适合普通用户安装。
- 证据:risks.scoring_risks | https://github.com/88plug/total-recall | no_demo; severity=medium
6. 维护坑 · issue/PR 响应质量未知
- 严重度:low
- 证据强度:source_linked
- 发现:issue_or_pr_quality=unknown。
- 对用户的影响:用户无法判断遇到问题后是否有人维护。
- 证据:evidence.maintainer_signals | https://github.com/88plug/total-recall | issue_or_pr_quality=unknown
7. 维护坑 · 发布节奏不明确
- 严重度:low
- 证据强度:source_linked
- 发现:release_recency=unknown。
- 对用户的影响:安装命令和文档可能落后于代码,用户踩坑概率升高。
- 证据:evidence.maintainer_signals | https://github.com/88plug/total-recall | release_recency=unknown
来源:Doramagic 发现、验证与编译记录