mempalace

命令行工具

MemPalace/mempalace

本地优先的 AI 记忆系统,支持语义检索。

项目简介

MemPalace 将对话历史原样存储,并通过语义搜索进行检索。它使用可插拔后端(默认 ChromaDB),提供命令行工具进行内容挖掘和搜索。设计为本地优先,保护隐私,同时支持可选的外部后端。

README 预览

\n\n\n\n# MemPalace\n\nLocal-first AI memory. Verbatim storage, pluggable backend, 96.6% R@5 raw on LongMemEval — zero API calls.\n\n[![][version-shield]][release-link]\n[![][python-shield]][python-link]\n[![][license-shield]][license-link]\n[![][discord-shield]][discord-link]\n\n\n\n> [!CAUTION]\n> **Beware of impostor sites.** MemPalace has no other official websites. The **only** official sources are this **[GitHub repository](https://github.com/MemPalace/mempalace)**, the **[PyPI package](https://pypi.org/project/mempalace/)**, and the docs at **[mempalaceofficial.com](https://mempalaceofficial.com)**. Any other domain (including `.tech`, `.net`, or other `.com` variants) is an impostor and may distribute malware. Details and timeline: [docs/HISTORY.md](docs/HISTORY.md).\n\n> [!IMPORTANT]\n> **Claude Code sessions expire in 30 days without auto-save hooks wired.** [Read this →](https://github.com/MemPalace/mempalace/discussions/1388)\n>\n> Need the shortest recovery/setup path? Use the [Claude Code retention setup checklist](https://mempalaceofficial.com/guide/claude-code-retention.html).\n\n---\n\n## What it is\n\nMemPalace stores your conversation history as verbatim text and retrieves\nit with semantic search. It does not summarize, extract, or paraphrase.\nThe index is structured — people and projects become *wings*, topics\nbecome *rooms*, and original content lives in *drawers* — so searches\ncan be scoped rather than run against a flat corpus.\n\nThe retrieval layer is pluggable. The current default is ChromaDB; the\ninterface is defined in [`mempalace/backends/base.py`](mempalace/backends/base.py)\nand alternative backends can be dropped in without touching the rest of\nthe system.\n\nNothing leaves your machine unless you opt in.\n\nArchitecture, concepts, and mining flows:\n[mempalaceofficial.com/concepts/the-palace](https://mempalaceofficial.com/concepts/the-palace.html).\n\n---\n\n## Install\n\nMemPalace ships a CLI, so install it in an isolated envi

常见问题 (5)

故障排除
为什么 `--llm-rerank` 不会改变 LoCoMo 的平均召回率?

LoCoMo 在 llm_rerank_locomo() 运行之前,会将融合后的候选列表截断至 top-k。因此,重排序器只是对同一组 ID 重新排序,而 compute_retrieval_recall 仅评估证据集合的成员归属,忽略顺序。这就是为什么即使发起了 LLM 调用,使用与不使用 --llm-rerank 的运行也会产生完全相同的召回率。

解决方法:在仅测试召回率的 LoCoMo 运行中省略 --llm-rerank,例如:

python benchmarks/locomo_bench.py --granularity session --top-k 10 --mode hybrid

该标志会影响 LongMemEval 上的 R@k,因为那里的重排序发生在截断之前。要使其影响 LoCoMo 的召回率,需要对更大的候选池进行重排序,并在截断之前选出最终的 top-k 项。简单的“从候选池中挑选 k 个”提示词可能会降低多证据召回率,因此可以考虑保留融合结果的头部,仅让 LLM 填充尾部位置。CLI 帮助和 benchmarks/BENCHMARKS.md 已更新,以记录此限制。

来源 Issue #2164
故障排除
为什么 Mempalace 矿井极其缓慢,并且每条隧道都会重写 tunnels.json?

在 3.8.0–3.10.0 版本以及 develop 分支中,mempalace/palace_graph.py 中的 create_tunnel() 会为每个隧道加载、扫描并重写整个 tunnels.json 文件。挖掘循环会反复调用它,导致时间复杂度呈平方级增长并产生过多的磁盘写入。

通过批量创建隧道来修复此问题:

1. 添加一个线程本地的 tunnel_batch() 上下文管理器,它会获取 mine 锁,仅加载一次 tunnels.json,并构建一个 id -> tunnel 索引。
2. 让 create_tunnel() 检测同一文件是否存在处于活动状态的批次,并通过索引以 O(1) 时间进行插入/更新,且每次调用后不再保存。
3. 在批次上下文退出时仅保存一次完整的隧道列表,最好放在 finally 中。
4. 用 with tunnel_batch(config): 包裹 entity_tunnels_for_wing()、compute_topic_tunnels() 以及 topic_tunnels_for_wing() 中的循环。
5. 在批次之外保留现有的"加载-修改-保存"路径,使 MCP 工具和用户显式创建的隧道行为保持不变。

参考实现:

_TUNNEL_BATCH = threading.local()

@contextmanager
def tunnel_batch(config=None):
    config = config or MempalaceConfig()
    tunnel_file = _get_tunnel_file(config)
    outer = getattr(_TUNNEL_BATCH, "state", None)
    if outer is not None and outer["file"] == tunnel_file:
        yield outer
        return
    with mine_lock(tunnel_file):
        tunnels = _load_tunnels(config)
        state = {
            "file": tunnel_file,
            "tunnels": tunnels,
            "index": {t.get("id"): t for t in tunnels if isinstance(t, dict)},
        }
        _TUNNEL_BATCH.state = state
        try:
            yield state
        finally:
            _TUNNEL_BATCH.state = outer
            _save_tunnels(state["tunnels"], config)

通过此更改,之前需要执行约 8.8k 次完整加载/保存循环的刷新操作变为一次加载和一次保存;报告中的示例耗时降至约 8 秒,其中主要耗时来自单次 JSON 转储。

来源 Issue #2683
故障排除
如何修复当 FTS 匹配行数超过 500 时 Mem Palace 联合搜索遗漏最佳词法匹配的问题?

该 bug 位于 backends/chroma.py 中的 ChromaCollection._lexical_search_via_sqlite。未过滤的 SQLite FTS5 查询在没有 ORDER BY 的情况下应用了 LIMIT,因此 SQLite 按 rowid/归档顺序返回匹配项。随后只对前 500 个匹配项进行 BM25 评分,而归档在后面的更优匹配项永远不会被纳入考量。

通过在应用数量上限之前对 FTS 候选结果进行排序来修复这个受限的查询:

SELECT e.id, e.embedding_id
FROM embedding_fulltext_search
JOIN embeddings e ON e.id = embedding_fulltext_search.rowid
WHERE embedding_fulltext_search MATCH ? AND c.name = ?
ORDER BY bm25(embedding_fulltext_search)
LIMIT ?;

对于 SQLite FTS5,bm25() 值越低越好。这与其他地方已经使用的 FTS 排名排序方式一致。然后对返回的窗口进行评分,并保留排名前 n_results 的结果。

针对受影响版本(如 v3.10.0)的即时临时解决方案:

1. 升级到包含 PR fix(search): rank the unfiltered lexical window before capping it 中修复的版本。
2. 如果在本地打补丁,请在 LIMIT 之前添加上述 ORDER BY bm25(...)。
3. 作为临时解决方案,传递一个 where 过滤器;该代码路径已经移除了 500 行的上限。
4. 或者,移除或增大 max_candidates=500,但这会增加查询开销,并且只有在新的上限能够覆盖所有匹配项时才有效。

增大 max_distance 并无帮助,因为缺失的抽屉从未进入词汇候选集或向量候选集。

来源 Issue #2667
故障排除
如何修复 mempalace repair 在提取和备份后因 MineAlreadyRunning 错误而失败的问题?

在 mempalace 3.9.0/3.10.0 中,旧版 mempalace repair 路径在执行 _extract_drawers、check_extraction_safety 和 copy_palace_dir 期间不会持有 mine_palace_lock。由钩子启动的 mempalace mine 进程可以在该窗口期内获取此锁,导致修复随后以 MineAlreadyRunning 失败,并丢弃已提取/备份的数据。

永久修复方案:安装包含旧版修复补丁的版本,或修补 cli/cmd_repair.py,使从 backend.get_collection 到 _post_rebuild_cleanup 的整个操作都在以下代码块内运行:

with mine_palace_lock(palace_path):
    ...

它应在提取开始前捕获 MineAlreadyRunning,报告锁持有者并以非零状态码退出。Chroma 后端中按批次获取锁的操作在同一线程上是可重入的。

受影响版本上的即时解决方法:禁用启动 miner 的 Claude Code 钩子,确保没有 miner 正在运行,然后运行修复:

pkill -f 'mempalace mine' || true
mempalace repair --yes

如果无法禁用该钩子,则在整个修复过程中持续阻止 miner:

while true; do
  pkill -f 'mempalace mine' || true
  sleep 2
done &
killer=$!

mempalace repair --yes
rc=$?

kill "$killer"
exit "$rc"

残留的 locks/mine_palace_<hash>.lock 文件通常是无害的,因为 flock 会在进程退出时释放。只有在确认没有修复或 miner 进程仍在运行、且新的 miner 仍被拒绝启动之后,才可删除该文件。

来源 Issue #2562
故障排除
如何阻止 MemPalace MCP 在每次 add_drawer 或 check_duplicate 调用后重新加载 HNSW 索引?

将 MemPalace 升级到包含 PR #2593 的版本。此问题影响 MemPalace 3.9.0:MCP 服务器缓存的 ChromaDB 客户端在服务器自身写入后未更新其 chroma.sqlite3 的新鲜度时间戳,因此 mempalace_add_drawer、mempalace_check_duplicate 和 mempalace_diary_write 错误地将自身的写入视为外部更改,并重新加载了 HNSW 索引。使用 pip install --upgrade mempalace 进行更新,然后通过 pip show mempalace 确认已安装的版本包含 #2593。如果尚无可用的修复版本,请从 PR #2593 安装修补后的构建版本。搜索调用不受影响,因为 ChromaBackend 路径已在 #2307 中获得了自身写入的重新标记;应用补丁后,来自其他进程的写入仍会在下一次变更时被检测到。

来源 Issue #2535