MemPalace 架构与开发规范:从逐字记忆、AAAK 索引到 L0-L3 记忆唤醒栈
MemPalace 是一个以"逐字存储、100% 召回"为设计目标的本地优先 AI 记忆系统,其名字源自古老的"记忆宫殿"(method of loci)技术,并借鉴了 Zettelkasten 卡片盒的交叉引用思想。本文基于仓库中的 AGENTS.md 完整梳理 MemPalace 的设计使命、七条不可妥协的设计原则、Wing/Room/Drawer 宫殿结构与 AAAK 索引层架构、核心模块地图,以及本地开发环境搭建、测试与代码规范,并结合 mempalace/dialect.py、mempalace/layers.py、mempalace/backends/base.py 等源码印证关键设计,帮助读者快速理解该系统"为什么这样设计"以及如何在其之上开发。
设计使命:记忆即身份,而非搜索或 RAG
AGENTS.md 开篇即给出 MemPalace 的立场声明:它不是搜索引擎、不是 RAG 流水线、也不是向量数据库的包装器,而是一个记忆系统。其核心承诺可以归纳为三点:
- 逐字(Verbatim)存储:用户说过的每一句话都原样保存,系统"搜索索引、返回原文",从不做摘要或改写;
- 100% 召回是设计硬性要求:所有搜索路径都以"完整召回"为测量目标,低于它就意味着"遗忘",遗忘意味着一切推倒重来;
- 数据永不离开本机:所有提取、分块、嵌入与 LLM 辅助精炼默认在用户机器上完成。
这一使命直接体现在其"宫殿"组织结构上(详见 website/concepts/the-palace.md):
| 概念 | 作用 | 说明 |
|---|---|---|
| Wing(翼) | 大类 | 按人物、项目、主题划分,如"某个人""某个项目" |
| Room(房间) | 时间分组 | 按天、按会话等时间维度组织 |
| Drawer(抽屉) | 逐字内容 | 存放用户原话的完整文本块,是整个系统的存储原子单位 |
| AAAK 压缩 | 索引层 | 一种紧凑的符号化格式(由 dialect.py 实现),让 LLM 可以瞬间扫描数千条条目、精确判断该打开哪个抽屉 |
这套结构对应了 Zettelkasten 的"小卡片互相引用"与记忆宫殿的"把信息放进想象中的房间"两条思想脉络:抽屉是卡片本体,房间是房间,而 AAAK 索引层则是卡片背面的交叉引用标注。
值得特别指出 AAAK 的边界:从 mempalace/dialect.py 的模块文档可以看出,AAAK 不是无损压缩——它无法从 AAAK 输出还原原文。源码明确注释它是"结构化摘要层(closets),指向原始逐字内容(drawers)"。其格式在 mempalace/dialect.py 中有完整定义:
FORMAT:
Header: FILE_NUM|PRIMARY_ENTITY|DATE|TITLE
Zettel: ZID:ENTITIES|topic_keywords|"key_quote"|WEIGHT|EMOTIONS|FLAGS
Tunnel: T:ZID<->ZID|label
Arc: ARC:emotion->emotion->emotion
此外,dialect.py 还定义了通用情绪码表(vul=脆弱、joy=喜悦、fear=恐惧、trust=信任等二十余种,见 mempalace/dialect.py 的 EMOTION_CODES)与一组语义旗标(ORIGIN 起源时刻、CORE 核心信念、SENSITIVE 需谨慎处理、PIVOT 情绪转折点、DECISION 明确决策、TECHNICAL 技术细节等)。这一设计让"索引层"本身对任意 LLM 都是可直接阅读的纯文本——源码注释强调"任何 LLM 都能原生读懂,无需解码器",这与 AGENTS.md 中"AAAK 让 LLM 瞬间扫描数千条索引"的描述相互印证。
七条不可妥协的设计原则
AGENTS.md 将以下原则列为"non-negotiable"——每个 PR、每个功能、每次重构都必须遵守。理解它们是判断任何 MemPalace 变更是否合理的基准:
- Verbatim always(永远逐字):绝不摘要、改写或有损压缩用户数据。用户说了什么,就存什么。这是整个系统的基础承诺。
- Incremental only(只增量):初始构建之后采用 append-only 的摄取方式;绝不通过销毁现有数据来重建;操作中途崩溃时必须让既有宫殿保持原样。
- Entity-first(实体优先):一切以真实名称为键,并用出生日期、ID 或上下文消歧。"人比主题重要"。
- Local-first, zero external API by default(本地优先,默认零外部 API):提取、分块、嵌入、LLM 精炼默认全部在本机完成,使用 Ollama、LM Studio、llama.cpp、vLLM 等本地运行时。外部服务商(Anthropic、OpenAI、Google)仅通过 BYOK 支持,从不强制、从不静默启用;运行在 localhost 上的 Ollama 属于用户机器的一部分,不算外部 API;外部 BYOK 永远是用户的显式选择,既不是默认值,也绝不作为静默回退。
- Performance budgets(性能预算):Hook 执行小于 500ms,启动注入小于 100ms——"记忆应该感觉是瞬时的"。
- Privacy by architecture(架构即隐私):因为数据物理上从不离开用户机器,所以"不可能"被发送出去;无遥测、无回传、核心操作无外部服务依赖。
- Background everything(一切后台化):归档、索引、时间戳、流水线工作全部通过 hook 在后台完成,不打断用户对话,聊天窗口中不为记账工作消耗任何 token。
这些原则在贡献边界上有直接体现:项目不接受用户内容摘要、云存储/同步功能、遥测或分析、核心记忆依赖 API key 的功能,以及任何绕过逐字存储的捷径(AGENTS.md "Contributing" 一节)。
架构总览:数据流、宫殿结构与知识图谱
AGENTS.md 给出的架构蓝图如下,可直接作为理解整个代码库的地图:
User → CLI / MCP Server → Storage Backend (ChromaDB default, pluggable)
→ SQLite (knowledge graph)
Palace structure:
WING (person/project)
└── ROOM (day/topic)
└── DRAWER (verbatim text chunk)
Index layer (AAAK):
Compressed pointers → DRAWER locations
Scanned by LLM to find relevant drawers without reading all content
Knowledge Graph:
ENTITY → PREDICATE → ENTITY (with valid_from / valid_to dates)
逐层拆解:
- 入口层:用户通过 CLI(mempalace/cli.py)或 MCP Server(mempalace/mcp_server.py)访问系统。MCP Server 是全部读/写工具的宿主,源码中按 "READ TOOLS / WRITE TOOLS / SETTINGS TOOLS" 分区组织(mempalace/mcp_server.py 起)。
- 存储层:可插拔的存储后端,默认 ChromaDB。后端契约定义在 mempalace/backends/base.py 中,模块 docstring 明确其对应 docs/rfcs/001-storage-backend-plugin-spec.md(RFC 001):
BaseCollection是每个 collection 的读写接口(仅接受 kwargs),BaseBackend是按PalaceRef寻址的每宫殿工厂,QueryResult/GetResult是取代 Chroma dict 形状的规范返回类型,并配有统一的错误体系(如PalaceNotFoundError同时是FileNotFoundError子类以保持旧调用方兼容,见 mempalace/backends/base.py)。仓库中已有chroma、pgvector、qdrant、milvus、sqlite_exact等后端实现(mempalace/backends/)。 - 知识图谱层:SQLite 上的时序实体关系图(mempalace/knowledge_graph.py),以
ENTITY → PREDICATE → ENTITY三元组存储,并带valid_from/valid_to时间戳,即关系本身也是随时间演化的。 - 搜索路径:混合检索,BM25 + 向量。AGENTS.md 将 mempalace/searcher.py 标注为"Semantic search (hybrid BM25 + vector)";从源码看,其中
_bm25_scores()负责 BM25 打分(mempalace/searcher.py),对外入口为search()(mempalace/searcher.py),还存在仅走 SQLite 的 BM25 降级路径_bm25_only_via_sqlite()(mempalace/searcher.py)。
L0-L3 记忆唤醒栈:性能预算如何落地
AGENTS.md 中"启动注入小于 100ms、记忆应该瞬时"的性能预算,对应实现就是 mempalace/layers.py 的四层记忆栈(详见 website/concepts/memory-stack.md)。模块头部注释给出了精确的 token 预算(mempalace/layers.py):
Layer 0: Identity (~100 tokens) — Always loaded. "Who am I?"
Layer 1: Essential Story (~500-800) — Always loaded. Top moments from the palace.
Layer 2: On-Demand (~200-500 each) — Loaded when a topic/wing comes up.
Layer 3: Deep Search (unlimited) — Full ChromaDB semantic search.
Wake-up cost: ~600-900 tokens (L0+L1). Leaves 95%+ of context free.
从源码实现看各层的具体形态:
- L0 Identity:
Layer0类读取用户手写的~/.mempalace/identity.txt纯文本文件(约 100 token),文件不存在时返回占位提示而非报错(mempalace/layers.py); - L1 Essential Story:
Layer1类从 ChromaDB 中自动提取权重最高/最近的内容,常量约束非常具体——MAX_DRAWERS = 15(唤醒时最多 15 个片段)、MAX_CHARS = 3200(约 800 token 的硬上限)、MAX_SCAN = 2000(生成时最多扫描 2000 条,mempalace/layers.py)。正是这些硬上限保证了"启动注入"的耗时与 token 成本可控; - L2 On-Demand:当某个主题/翼被提及时才按需加载;
- L3 Deep Search:完整的 ChromaDB 语义搜索,无上限,兜底"100% 召回"目标。
这套分层把 AGENTS.md 中"Background everything"与"瞬时召回"两个原则变成了可度量的工程参数。
核心模块地图
AGENTS.md 的 "Project Structure" 一节是理解代码库组织方式的骨架。下表在其基础上补充了各模块在源码中的定位(mempalace/ 包内路径):
| 模块 | 职责 |
|---|---|
| mempalace/mcp_server.py | MCP Server,承载全部读/写工具 |
| mempalace/cli.py | CLI 分发器 |
| mempalace/config.py | 配置 + 输入校验 |
| mempalace/miner.py | 项目文件挖掘器 |
| mempalace/convo_miner.py | 对话记录挖掘器 |
| mempalace/searcher.py | 混合语义搜索(BM25 + 向量) |
| mempalace/knowledge_graph.py | 时序实体关系图谱(SQLite) |
| mempalace/palace.py | 共享宫殿操作 |
| mempalace/palace_graph.py | 房间遍历 + 跨翼隧道 |
| mempalace/backends/ | 可插拔存储后端(默认 ChromaDB);新后端实现 mempalace/backends/base.py 抽象接口 |
| mempalace/dialect.py | AAAK 压缩方言 |
| mempalace/normalize.py | 转录格式检测 + 归一化 |
| mempalace/entity_detector.py | 从内容自动识别人物/项目 |
| mempalace/entity_registry.py | 实体存储与消歧 |
| mempalace/layers.py | L0-L3 记忆唤醒栈 |
| mempalace/onboarding.py | 交互式首跑设置 |
| mempalace/repair.py | 宫殿修复与一致性检查 |
| mempalace/dedup.py | 去重 |
| mempalace/migrate.py | ChromaDB 版本迁移 |
| mempalace/spellcheck.py | 用户消息自动纠错 |
| mempalace/exporter.py | 宫殿数据导出 |
| mempalace/hooks_cli.py | Hook 管理 CLI |
| mempalace/query_sanitizer.py | 提示词污染防护 |
| mempalace/split_mega_files.py | 拆分拼接型大转录文件 |
| mempalace/version.py | 版本号的单一事实来源 |
Hook 脚本位于 hooks/ 目录,AGENTS.md 中特别点名了两个核心脚本:
- hooks/mempal_save_hook.sh:绑定 Stop 事件,触发日记保存;
- hooks/mempal_precompact_hook.sh:绑定 PreCompact 事件,在上下文压缩前保存状态。
这两条 hook 正是"Background everything"原则的执行点——在用户对话结束或即将被压缩的时机,后台完成归档,而对话窗口本身不为记账消耗 token。仓库中还提供 Cursor、Antigravity 等 IDE 的 hook 变体(hooks/cursor/、hooks/antigravity/)。
本地开发流程:环境搭建、测试与命令
AGENTS.md "Setup" 与 "Commands" 两节给出了完整的开发者命令序列,原样如下(推荐 uv 工作流):
# 环境搭建(推荐)
uv sync --extra dev
# 或使用 pip
pip install -e ".[dev]"
# 运行测试
uv run pytest tests/ -v --ignore=tests/benchmarks
# 带覆盖率的测试
uv run pytest tests/ -v --ignore=tests/benchmarks --cov=mempalace --cov-report=term-missing
# Lint
uv run ruff check .
# 格式化
uv run ruff format .
# 格式检查(CI 模式)
uv run ruff format --check .
几个值得注意的细节:
- 测试命令统一
--ignore=tests/benchmarks,基准测试(tests/benchmarks/)与单元/集成测试分离,避免 CI 被性能基准拖慢; - 依赖管理以
uv.lock锁定版本(uv.lock、pyproject.toml),与uv sync配套使用。
代码规范(Conventions)
AGENTS.md 的 "Conventions" 一节定义了所有贡献必须遵守的编码约定:
- Python 风格:函数/变量用
snake_case,类用PascalCase; - Linter:ruff,启用 E/F/W 规则集;
- Formatter:
ruff format,双引号风格; - 提交信息:conventional commits(
fix:、feat:、test:、docs:、ci:); - 测试组织:
tests/test_*.py,fixture 放在 tests/conftest.py; - 覆盖率门槛:85%;Windows 上因 ChromaDB 文件锁清理差异放宽到 80%。
输入校验:Verbatim 原则的边界
"Verbatim always" 并不意味着"任何输入都原样入库"。AGENTS.md 将输入校验指向 mempalace/config.py 中的两个函数,源码确认了它们的签名:
sanitize_name()(mempalace/config.py):校验名称类字段(如 wing/room 名);sanitize_content()(mempalace/config.py):校验内容,带max_length: int = 100_000的默认长度上限。
即:内容层面严格逐字,但字段层面做格式与长度约束——这与"prompt 污染防护"(mempalace/query_sanitizer.py)共同构成写入侧的安全边界。
面向常见任务的关键文件指引
AGENTS.md 最后给出了一张"按任务找文件"的速查表,这是对该仓库最实用的入口指南,逐条补充源码级说明:
- 新增一个 MCP 工具:改 mempalace/mcp_server.py——添加 handler 函数,并在 TOOLS 字典中登记。源码中读/写/设置工具按
# ==================== READ TOOLS ====================等分区注释组织(mempalace/mcp_server.py),写工具还受_MUTATING_TOOLS等 frozenset 的权限分组约束(mempalace/mcp_server.py),新增写工具时需确认其是否应纳入变更工具集合。 - 修改搜索逻辑:改 mempalace/searcher.py,入口函数为
search()(mempalace/searcher.py)。 - 修改挖掘逻辑:项目文件改 mempalace/miner.py;对话转录改 mempalace/convo_miner.py。
- 新增存储后端:继承 mempalace/backends/base.py 的抽象接口,并注册到 mempalace/backends/init.py。base.py 的错误类型契约要求严格——例如"静默丢弃未知 where 算子"被 RFC 001 §1.4 明确禁止,后端应抛出
UnsupportedFilterError(mempalace/backends/base.py);维护能力(run_maintenance)必须先声明后实现,否则视为一致性失败(mempalace/backends/base.py)。 - 输入校验:mempalace/config.py 的
sanitize_name()/sanitize_content()。 - 测试:在
tests/下按源码结构镜像命名,即tests/test_<module>.py。
总结
MemPalace 的 AGENTS.md 实质上是一份"架构宪法":它以"记忆即身份"为使命,用"永远逐字、只增量、实体优先、本地优先、性能预算、架构即隐私、一切后台化"七条原则划定了设计边界,再用 Wing/Room/Drawer 的宫殿结构 + AAAK 索引层 + SQLite 时序知识图谱 + L0-L3 唤醒栈给出了完整的技术落地路径。对贡献者而言,开发闭环是:uv sync --extra dev 建环境、uv run pytest tests/ --ignore=tests/benchmarks 跑测试、uv run ruff 保证风格、按"按任务找文件"速查表定位改动点、并以 85% 覆盖率门槛守住质量线。任何新功能在提交前,都应先对照七条原则自检——尤其是"是否绕过了逐字存储""是否引入了静默的外部 API 调用"这两条一票否决项。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00