内容
<div align="center">
<img src="https://raw.githubusercontent.com/kiraa06/sup-mem/main/docs/logo.png" alt="SupMem" width="380">
[](https://github.com/kiraa06/sup-mem/actions/workflows/ci.yml)
[](https://pypi.org/project/sup-mem/)
[](https://pypi.org/project/sup-mem/)
[](https://github.com/kiraa06/sup-mem/blob/main/LICENSE)
[](https://github.com/astral-sh/ruff)




</div>
**一款为 Claude Code、Codex 和 Gemini CLI 设计的自托管内存层,可在会话间保持上下文 — 与其他所有内存工具不同的是,它会**衡量其内存是否真正有所帮助**。
每个内存系统都存储和检索。sup-mem 还能闭环:
- **内存有盈亏。** 一个 `Stop` 钩子读取每个完成的响应并评估每个注入的内存:**被引用、被忽略或被矛盾**。`sup-mem roi` 显示每个内存的上下文令牌成本与贡献。
- **它根据证据进行调整。** 在开发过程中,sup-mem 的账本发现其自身的检索运行在 19% 的命中率(~65k 无效令牌在一个会话中),自动提高其阈值无损(0.35 → 0.80),并且每个内存的剪辑将注入成本 **86%** — 所有这些都是衡量出来的,而不是靠感觉。
- **默认零基础设施。** 一个命令:SQLite FTS5,无 Docker,无嵌入模型,<10 ms 检索。根据需要可切换到 Qdrant 向量搜索。
- **为事件人员构建。** 双时间回忆(`--as-of 2026-06-01`:*当时我们相信什么?*),防篡改溯源链(`sup-mem verify`),在上下文压缩过程中幸存的内存,以及基于证据的衰减,带有硬大小上限。

<sub>20 秒,真实二进制:`status`(所有连接)→ 注入内存到提示的钩子 → `recall --as-of --diff-now`(我们当时相信什么 vs 现在)→ `verify`(防篡改链)。使用 `vhs docs/demo.tape` 重新渲染到种子演示存储。</sub>
---
## 安装
选择与您的规模相匹配的行。**大多数人应该从默认开始。**
<table>
<tr><th>默认 — SQLite FTS(无 Docker,无模型)</th><th>规模 — 向量搜索(按需)</th></tr>
<tr valign="top"><td>
```bash
curl -LsSf https://raw.githubusercontent.com/\
kiraa06/sup-mem/main/install.sh | sh
```
零服务,零模型。关键词(BM25)检索在一个嵌入的 SQLite 文件中。几秒内即可准备就绪。
</td><td>
```bash
# 默认安装后:
sup-mem setup --backend qdrant --yes
```
启动 Qdrant(Docker),自动检测本地嵌入器,按**含义**排序。单行,非交互式。
</td></tr>
</table>
已经有 Python 工具管理器?相同的结果:
```bash
uv tool install sup-mem && sup-mem init # 或:pipx install sup-mem && sup-mem init
```
Homebrew:
```bash
brew tap kiraa06/sup-mem && brew install sup-mem && sup-mem init
```
> **哪一个?** 使用 **FTS 默认,直到您拥有 ~10k+ 内存**(或您特别想要语义/释义匹配)。然后使用 `sup-mem setup --backend qdrant --yes` 切换。您的 `remember`/`recall` 习惯不会改变 — 只有后端会改变。
> **哪个主机?** `sup-mem init` 自动检测已安装的主机(**Claude Code,Codex CLI,Gemini CLI**)并连接它们 — 钩子和 MCP 服务器,非覆盖,具有 `.bak`。范围限定为 `sup-mem init --client codex`(或 `--client all`)。存储还可以通过 `sup-mem` MCP 服务器从任何其他 MCP 客户端(Cursor,Zed,Claude Desktop)工作;结果循环需要三个连接的主机之一。
然后 **重启主机**(Claude Code / Codex / Gemini),以便它们加载新的钩子 + MCP 服务器。
---
## 循环 — 具有盈亏的内存(是什么让 sup-mem 与众不同)
其他所有内存层都是开环:存储 → 检索 → 注入 → *希望*。sup-mem **关闭循环**。一个 `Stop` 钩子在每次响应后读取会话记录,并将每个注入的内存归因于 **被引用**(答案实际使用了它),**被忽略**,或 **被矛盾**(您立即驳斥了它)。从这些证据中:
- **强化检索** — 被引用的内存获得有界的分数提升;反复被矛盾的内存被隔离(永远不会被删除,总是可逆)。
- **`sup-mem tune`** — 反事实重放记录的回合在其他阈值下,并推荐一个带有证据的阈值(“在 0.55 下,您将减少 78% 的被忽略注入并不会损失任何被引用的内存”)。`--apply` 将其写入配置文件。诚实设计:从未注入的候选者被报告为 *未知*,而不是被猜测。
- **`sup-mem roi`** — 每个内存的令牌盈亏:它在上下文中的成本 vs. 它贡献的价值。找到这个月消耗了 40k 令牌但从未被使用的内存。
所有这些都是建议性的、开放的和非热点路径的 — 每次提示的成本是一个索引的 SQLite 查询。规格:[docs/PHASE6-LOOP.md](docs/PHASE6-LOOP.md)。
**安装即忘记:** `sup-mem service install` 将日常维护任务安排为每天 — 日志轮换、存储备份(保留)、原生内存扫描、**无损自动调优**、溯源验证、失败时的桌面通知。`sup-mem status` 显示整个连接情况。
**设计边界:** 两个大小上限(配置文件中的 `[archival]`)使存储永远保持诚实。超过主上限,最无用的内存(账本证明:反复注入,从未被引用)移动到一个冷的 `archive.db` — 仍然可通过 `--as-of`、`restore` 和账本访问。超过存档上限,最旧的存档被永久删除(FIFO,链条审计;`0` 禁用)。证据决定,而不是年龄;`keep` 标签的内存不可触及。规格:[docs/PHASE9-ARCHIVAL.md](docs/PHASE9-ARCHIVAL.md)。
**时间旅行 + 防篡改证据:** 存储是附加和版本控制的 — 更改的事实 *取代* 其前身,而不是覆盖它,因此 `sup-mem recall --as-of 2026-06-01 "pod restart cause" --diff-now` 回答 *"我们当时相信什么,它与现在有何不同?"* — 内置事件 RCA。每次写入还会加入一个 HMAC 哈希链(`~/.sup-mem/key`);`sup-mem verify` 证明没有人编辑过您的内存(防篡改 *证据*,而非防篡改 — 攻击者持有密钥可以重新连接;参见 [docs/PHASE8-TEMPORAL.md](docs/PHASE8-TEMPORAL.md))。
---
## 它是如何工作的 — 两个前门,一个后端
检索通过 **相同** 的存储进行两次:一个自动钩子将上下文注入每个回合,MCP 工具允许 Claude 显式读写。
```
每次提示(READ,同步,延迟关键):
用户提示提交钩子(短寿命) ──► 层 0:固定的事实(始终)
──► 层 1:跳过琐碎的回合(问候/感谢)
──► 层 2:后端.search(查询,k,阈值)
│ (嵌入式数据库,或对热服务进行薄调用)
▼
stdout ─► 注入到 Claude 的上下文中
会话开始:
会话开始钩子 ──► manifest() ──► 紧凑、规模感知的主题索引注入
显式(WRITE + 故障转移 READ,Claude 发起):
MCP 服务器 ──► remember(text, …) → backend.store(…)
──► recall(query, k) → backend.search(…) [故障转移仅限]
共享后端(之一):
SqliteFtsBackend (默认:嵌入式文件,BM25,无模型,无 Docker)
QdrantBackend (按需:向量 kNN,自动检测嵌入器)
PgVectorBackend (存根 — 相同的接口)
```
钩子是一个 **短寿命进程,不会加载模型**:在 FTS 路径上,它只打开数据库文件;在向量路径上,它对一个热嵌入器进行薄调用(Ollama/TEI/托管)。如果唯一嵌入器在进程中(fastembed),钩子跳过自动检索 — MCP `recall` 工具仍然适用于热服务器。
### 在 Claude Code 中注册
`init`/`setup` 合并 — **永远不会覆盖** 您的 Claude Code 配置:
- **钩子** → `~/.claude/settings.json` 下的 `hooks`(一个 `UserPromptSubmit` 条目和一个 `SessionStart` 条目)。您的现有钩子和其他键值都保留;重新运行是幂等的,并且保留以前的文件作为 `settings.json.sup-mem.bak` 备份。
- **MCP 服务器** → 通过 `claude mcp add --scope user` 注册到 `~/.claude.json` 下的 `mcpServers`(如果 `claude` CLI 不在 PATH 上,则回退到直接、非覆盖合并)。
Claude Code 不会热加载配置 — **重启** `init`/`setup` 后。遵循 `CLAUDE_CONFIG_DIR`。
两个 MCP 工具(它们的描述是 Claude 确定的控制界面):
| 工具 | 何时调用 |
|---|---|
| `remember` | 用户声明一个持久的事实/决定/偏好/纠正(“记住……”,“我们决定……”,“从现在开始,始终……”)。 |
| `recall` | **故障转移** 仅当用户引用以前的工作但它不在自动注入的上下文中时(“我们做的修复”,“那个票据”)。 |
---
> ## ⚠️ 模型一致性契约(向量后端)
>
> **写入存储的嵌入模型和读取模型的嵌入模型必须相同。** 来自不同模型的向量是不可比较的 — 混合它们会静默返回垃圾。
>
> sup-mem 在存储中记录 `(provider, model, dim)` 并 **强制执行**:`sup-mem doctor` **退出非零** 出现不匹配时,并且 `store`/`search` 拒绝而不是损坏结果。如果您更改模型,请运行 **`sup-mem reindex`** 用新模型重新嵌入所有内容。 (不适用于默认的 SQLite FTS — 它不使用模型。)
---
## 调整
检索质量是 **您在您的数据上调整的拨盘** — 而我们为您提供调整所需的数据。
- **`retrieval.threshold`** (0..1,默认为 `0.35`):提高以注入更少(更高精度),降低以注入更多(更高召回率)。
- **`retrieval.k`** (默认为 `6`):每个回合注入/返回的最大内存数。
- **检索日志** (默认开启):每个回合将 `(query, injected_ids, scores, tier)` 追加到 `~/.sup-mem/retrieval.jsonl`。用它查看被注入的内容以及阈值应设置在哪里。关闭时设置为 `logging.retrieval_log = false`。
- **层 1 跳过/提示模式**:完全可配置的正则表达式列表 — 跳过门控在琐碎的回合中短路,但永远不会决定是否存在 **相关** 内存。
所有内容都位于 `~/.sup-mem/config.toml`(写入 `init`,每个旋钮都有文档)。随时使用 `sup-mem manifest` 检视主题索引;使用 `sup-mem doctor` 检查健康状况。
## 它与其他工具的比较
公平竞争:mem0/Zep/Letta 是优秀的多平台内存系统,而原生内存需要零设置。sup-mem 的赌注是与主机耦合,它们公开了循环所需的三个东西 — 每次提示的钩子、响应后的钩子和磁盘上的会话记录。截至 2026 年中,这是 **Claude Code、Codex 和 Gemini CLI**(CLI 生态系统与 Claude Code 的钩子合约汇聚 — Codex 直接克隆了它);存储本身适用于 **任何 MCP 客户端**。
| | sup-mem | mem0 / Zep / Letta | 原生内存 |
|---|---|---|---|
| 存储和检索内存 | ✓ | ✓ | ✓ |
| **衡量注入的内存是否有帮助**(结果账本) | ✓ | ✗ | ✗ |
| **根据证据自我调整检索**(无损自动调优) | ✓ | ✗ | ✗ |
| 每个内存的令牌 ROI(`roi`) | ✓ | ✗ | ✗ |
| 双时间回忆(`--as-of`:当时 vs 现在的信念) | ✓ | 部分(Zep:时间图) | ✗ |
| 防篡改溯源链(`verify`) | ✓ | ✗ | ✗ |
| 基于证据的衰减 + 硬大小上限 | ✓ | 部分 | ✗ |
| 在上下文压缩中幸存(PreCompact 捕获) | ✓ | ✗ | ✗ |
| 默认零基础设施(无 Docker,无模型) | ✓ | 变化(通常是无 Docker,无模型) | ✓ | 变化(通常托管/矢量) | ✓ |
| **完整的成果循环在** | Claude Code · Codex · Gemini CLI | ✗ | ✗ |
| 存储可从 | 任意 MCP 客户端(本地) | 托管 SaaS + SDK | 仅限其主机 |
| 语义(向量)检索 | 按需(Qdrant) | ✓ 默认 | ✗ |
| | SQLite FTS *(默认)* | Qdrant *(按需)* | pgvector *(存根)* |
| --- | --- | --- | --- |
| 设置成本 | **无** — 一个文件,无依赖 | Docker + 自动检测嵌入器 | Postgres + `pgvector` |
| 按以下方式排名 | 关键词(BM25) | **含义**(向量 kNN) | 含义(向量 kNN) |
| 适用于 | 最多 ~10k 内存 | 10k+ / 语义回忆 | 现有 Postgres 商店 |
| 需要模型 | 否 | 是(本地或托管) | 是 |
| 命令 | `sup-mem init` | `sup-mem setup --backend qdrant` | _接口保留_ |
**嵌入器**(`setup` 按优先级自动检测,或固定一个):Ollama · fastembed(本地 ONNX,CPU)· TEI · Voyage · OpenAI。托管选项会发出警告(网络 + 成本 + 数据离开您的盒子)。
> **嵌入器强度是召回率的下限。** 默认配置,无需配置,fastembed `bge-small-en-v1.5`(384 维,CPU),能准确匹配,但对 **抽象释义** (带有或没有共享词的查询)较弱。对于释义密集的召回,固定一个更强的模型 — 在配置文件中将 `model = "BAAI/bge-base-en-v1.5"` 设置为 `[embedding]` 下(768 维,仍然是本地 CPU;维度是自动检测的) — 或托管的嵌入器(`voyage-3`,`text-embedding-3-*`)。检索质量取决于模型,而不是后端。Qdrant 的分数与嵌入器的余弦完全匹配。
## 为优化而设计
除硬编码外,所有内容都位于 `config.toml` 中:
- **检索:** `retrieval.k`,`retrieval.threshold`
- **一级门控:** `tier1.skip_patterns`,`tier1.cue_patterns`
- **大规模 manifest:** `manifest.full_below`,`manifest.token_budget`,`manifest.max_topics`
- **FTS:** `fts.squash_midpoint`,`fts.squash_steepness`(BM25→0..1 旋钮)
- **Qdrant:** `qdrant.hnsw.m` / `ef_construct` / `ef`,`qdrant.quantization`
- **嵌入:** `embedding.provider`,`embedding.model`
构建的延迟预算:Tier-1 跳过 < 内存查询(10k)< 5 毫秒 · 热向量查询 < 50 毫秒 · 缓存的 manifest < 5 毫秒。
## CLI
| 命令 | 执行 |
|---|---|
| `sup-mem init` | 创建 SQLite FTS 存储,写入配置 + 固定的事实,连接探测到的主机上的钩子 + MCP 服务器(`--client claude\|codex\|gemini\|all`)。 |
| `sup-mem setup --backend qdrant [--yes]` | 启动 Qdrant,检测嵌入器,创建集合并注册。 |
| `sup-mem migrate-native [--dry-run]` | 将 Claude Code 的内置文件内存(`~/.claude/projects/*/memory`)复制到存储中。复制仅限,幂等 — 随时重新运行以捕获滞后者。 |
| `sup-mem tune [--apply]` | 反事实阈值重放针对记录的结果;推荐(并可选地应用)更好的阈值。 |
| `sup-mem roi` | 每个内存的令牌盈亏:注入,令牌,引用/忽略/矛盾,判决。 |
| `sup-mem recall "q" [--as-of WHEN] [--diff-now]` | 从 CLI 进行搜索; `--as-of` 返回存储在该时间点的信念(时间维度,sqlite 后端)。 |
| `sup-mem verify` | 验证防篡改溯源链 + 行哈希;如果有任何中断,则返回非零。 |
| `sup-mem archive [--dry-run\|--list]` | 基于证据的冷存储:过期/隔离的按计划移动;超过 `main_max_mb` 的无用衰减内存移动;超过 `archive_max_mb` 的旧存档 **永久删除**(FIFO,链条审计;`0` 禁用)。 |
| `sup-mem restore <id…>` | 将存档版本移回热存储,状态完整。 |
| `sup-mem-hook-precompact` | (钩子)就在 Claude Code 压缩会话之前,一个无头 `claude -p` 将记录提炼成持久内存,以便它们在压缩过程中幸存。成本为小模型调用;禁用 `[capture] enabled=false`。 |
| `sup-mem status` | 一眼看懂的连接检查:钩子,MCP 服务器,存储,账本活动,备份,服务 — 每个红线都有修复命令。 |
| `sup-mem maintain` | 维护:轮换检索日志(账本光标重基线),备份 + 真空存储,扫描原生内存,无损自动调优,健康检查。 |
| `sup-mem service install` | 安排每日 `maintain` — macOS LaunchAgent 或 Linux systemd 用户计时器,自动检测(也支持卸载/状态)。无调度器可用:打印 crontab 行。 |
| `sup-mem doctor` | 后端/服务健康;强制模型一致性契约。 |
| `sup-mem reindex` | 用当前模型重新索引存储(向量后端)。 |
| `sup-mem serve` | 运行长时间运行的 MCP 服务器。 |
| `sup-mem manifest` | 打印/刷新主题索引。 |
## 开发
```bash
uv sync # 基本(SQLite FTS)开发环境
uv run pytest -q # 快速套件
uv run ruff check . && uv run mypy
uv sync --extra qdrant # 添加向量路径;通过 docker-compose.qdrant.yml 运行 Qdrant
QDRANT_URL=http://localhost:6333 uv run pytest -q -m qdrant
```
查看 [HANDOVER.md](HANDOVER.md) 以获取完整的设计合同和不变式。
## 许可
MIT — 参见 [LICENSE](LICENSE)。
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器