☰
【Agent Memory篇】05:MemPalace 整体架构拆解——宫殿隐喻与四层记忆栈如何用 TaoToken 统一接入
2026/10/17 13:36:11 网站建设 项目流程

1. 从一次“记忆失忆”说起:MemPalace 到底解决什么问题

如果你正在给 Agent 加长期记忆,大概率踩过这个坑:昨天聊过的技术选型,今天再问它,它一脸茫然;或者更糟,它记得“用户偏好 Postgres”,却完全忘了你当时解释“为什么不用 MySQL”的那段上下文。这不是模型不行,而是记忆架构的取舍问题。

MemPalace 是一个用“记忆宫殿”隐喻组织的 Agent Memory 框架,核心口号只有一句:Store everything, then make it findable(把一切都存下来,再让它可被检索)。它适合谁?适合正在搭建个人助手、开发者 sidekick、小团队知识库的工程师,尤其是对“原始对话丢失”敏感、又不想每次写入都烧一遍 LLM token 的人。

它和 Mem0、LangMem、OpenClaw 那一派最大的区别在于:别人让 LLM 决定“什么值得记”,抽取成一条条 fact,然后丢掉原文;MemPalace 反过来,原文一字不动进 ChromaDB,靠 wing/room 两层 metadata 过滤 + 语义检索来找回。它在 LongMemEval R@5 上跑出 96.6% 的 raw-mode 成绩,而且零 API key、零云、纯本地。

这一篇是 MemPalace 子系列的开篇,我先把整体骨架铺清楚:宫殿隐喻怎么映射到数据模型、四层记忆栈 L0–L3 各自职责、数据怎么从对话流进 ChromaDB,以及怎么用 TaoToken 统一 Key/API 通道把 embedding 与检索链路接起来。挖矿流水线、AAAK、知识图谱、MCP 实战分别留给后面几篇。

我试过把这套结构拆开重装一遍,最深的感受是:它的“空间隐喻”不是文学修辞,而是真实可执行的 metadata 过滤。下面从设计哲学讲起。

2. 宫殿隐喻与四层记忆栈:MemPalace 整体架构拆解

2.1 三大设计支柱

第一根支柱是记忆宫殿隐喻(Method of Loci)。古希腊演说家把论点放进想象中的建筑房间,走一遍建筑就能想起内容。MemPalace 把这套映射到数据库 schema:Wing(翼)是人和项目,Hall(大厅)是记忆类型,Room(房间)是具体话题,Closet(壁橱)是摘要指针,Drawer(抽屉)是原文 chunk,Tunnel(隧道)是跨 Wing 的同话题连接。

第二根支柱是本地优先、零 API key。它的依赖只有两行:chromadb>=0.5.0,<0.7和pyyaml>=6.0。没有 openai、没有 anthropic,embedding 走 ChromaDB 内置的 ONNX 模型,装完即用,没有一行代码会往外发数据。

第三根支柱是原始存储(Raw Verbatim Storage)。对话切成 chunk,打上 wing/room metadata,直接 embed 存进 ChromaDB,不做 extract、不做 summary。代价是存储体积大,收益是细节 100% 保留。

2.2 数据模型:逻辑 ≠ 物理

整个 ChromaDB 只有一张表mempalace_drawers,每条记录是{id, embedding, document(原文), metadata},metadata 里关键字段是{wing, room, hall, importance, source_file}。所谓“宫殿”在物理上只是 metadata 字段,查询时靠 where 过滤“走”出来。

隐喻对应作用
Wing第一层分区一个人 / 一个项目 / 一个主题
Hall横切维度记忆类型(facts/events/advice/preferences/discoveries)
Room具体话题auth-migration、graphql-switch
Closet摘要指针指向原始文件的摘要卡片
Drawer原始 chunk原文一字不动
Tunnel跨 Wing 连接把不同 Wing 里的同名 Room 连起来

2.3 四层记忆栈 L0–L3

这是整套架构的核心。动机很直白:把所有聊天历史塞进 context 是 19.5M tokens,不可能;每次用 LLM 做 summary 再 load 回来约 650K tokens,一年约 $507。MemPalace 的 wake-up 只要约 170 tokens,加 5 次检索约 13,500 tokens,一年约 $10。差了约 3800 倍。

关键不是压缩算法的胜利,而是**“只在需要时才加载”**的胜利。L0/L1 常驻,L2/L3 按需拉取。

  • Layer 0 — Identity(约 100 tokens,常驻):读~/.mempalace/identity.txt,一个用户手写的纯文本文件。必须是人类手写,因为这是 AI 的“出厂设置”,不能靠 hallucinate。token 估算用len(text)//4,粗糙但够用。
  • Layer 1 — Essential Story(约 500–800 tokens,常驻):自动生成。从 ChromaDB 拉所有 drawer,按 importance 排序取 top 15,按 room 分组,每条截断到 200 字符,总长 cap 在 3200 字符。超长时写... (more in L3 search),显式提示模型下一步动作。
  • Layer 2 — On-Demand(约 200–500 tokens/次):按 wing/room 过滤,用col.get()而非col.query(),纯 metadata 过滤,无 query embedding。本质是“文件夹 ls”,保证 recall = 1.0。
  • Layer 3 — Deep Search(约 300–800 tokens/次):真正的语义检索,col.query(query_texts=..., where=...),返回带 similarity 分数的结果。

MemoryStack是统一入口,三个 public 方法对应三种心智状态:wake_up()是“开机”,recall()是“我记得这个事”,search()是“我想起来好像有过这个”。

2.4 为什么 wing + room 过滤能涨 34% R@10

README 给了一张表:全库搜索 60.9% R@10,wing 内 73.1%(+12%),wing + hall 84.8%(+24%),wing + room 94.8%(+34%)。原因很简单:embedding 空间里“主题相似度”和“人/项目归属”是纠缠的。搜 “auth migration” 时,嵌入模型会把所有项目的 auth 讨论都拉近。metadata 过滤等于把候选集从“全库”缩到“某个 wing”,让相似度计算在更纯净的空间里发生。

作者自己在 4 月 7 日的公开信里承认:“Metadata filtering is a standard ChromaDB feature, not a novel retrieval mechanism.” 值得做、值得讲,但不是秘方。真正的秘方是“怎么把对话切成 wing/room 这两个标签”,那部分在convo_miner.py和room_detector_local.py里,留给第 06 篇。

3. 用 TaoToken 统一接入:ChromaDB 与四层栈的可复制配置

MemPalace 默认走本地 ONNX embedding,但当你需要更强的 embedding 模型、或者想让 Agent 的对话模型与记忆检索走同一条通道时,用 TaoToken 统一 Key/API 通道会省很多事。TaoToken 提供兼容 OpenAI 协议的 API 入口,Base URL 是https://taotoken.net/api,一个 Key 就能覆盖模型对话、embedding、coding plan 等场景。

3.1 安装与目录准备

# 建议用独立虚拟环境,避免和系统 Python 冲突 python3 -m venv ~/.venvs/mempalace source ~/.venvs/mempalace/bin/activate # 安装 MemPalace(v3.0.14) pip install "chromadb>=0.5.0,<0.7" "pyyaml>=6.0" # 初始化配置目录 mkdir -p ~/.mempalace/palace

3.2 配置 TaoToken 通道

MemPalace 的配置优先级是env > config.json > 默认值。我们把 TaoToken 的 Key 和 Base URL 写进环境变量,再落到~/.mempalace/config.json里,方便不同进程复用。

# 写入 shell 配置(以 zsh 为例) cat >> ~/.zshrc <<'EOF' export TAOTOKEN_API_KEY="sk-你的TaoToken密钥" export TAOTOKEN_BASE_URL="https://taotoken.net/api" export MEMPALACE_PALACE_PATH="$HOME/.mempalace/palace" EOF source ~/.zshrc

~/.mempalace/config.json内容如下,注意palace_path与上面环境变量保持一致:

{ "palace_path": "/Users/yourname/.mempalace/palace", "collection_name": "mempalace_drawers", "embedding": { "provider": "taotoken", "base_url": "https://taotoken.net/api", "api_key_env": "TAOTOKEN_API_KEY", "model": "text-embedding-3-small" }, "hall_keywords": { "technical": ["code", "python", "bug", "api", "database"], "events": ["meeting", "decided", "shipped", "rollback"] } }

如果你用 Claude Code 或 Cline 这类工具做 Agent 编排,可以在它们的 MCP 配置里把 MemPalace 的检索工具挂上,同时把模型通道指向 TaoToken。以 Cline 的 MCP 配置为例:

{ "mcpServers": { "mempalace": { "command": "python", "args": ["-m", "mempalace.mcp_server"], "env": { "MEMPALACE_PALACE_PATH": "/Users/yourname/.mempalace/palace", "TAOTOKEN_API_KEY": "sk-你的TaoToken密钥", "TAOTOKEN_BASE_URL": "https://taotoken.net/api" } } } }

这里三件套要写全:Base URL是https://taotoken.net/api,Key走TAOTOKEN_API_KEY环境变量,Model ID在 embedding 配置里指定为text-embedding-3-small。三者缺一,检索链路就会在初始化阶段报错。

3.3 写入身份文件

L0 是常驻层,必须手写。创建~/.mempalace/identity.txt:

I am Atlas, a personal AI assistant for Alice. Traits: warm, direct, remembers everything. People: Alice (creator), Bob (Alice's partner). Project: A journaling app that helps people process emotions.

3.4 初始化 wing 配置

~/.mempalace/wing_config.json定义你的翼:

{ "default_wing": "wing_general", "wings": { "wing_alice": {"type": "person", "keywords": ["alice"]}, "wing_driftwood": {"type": "project", "keywords": ["driftwood", "analytics", "saas"]} } }

配置就绪后,四层栈的加载顺序是:进程启动读 env → 合并 config.json → 缺省值兜底。MempalaceConfig同时支持MEMPALACE_PALACE_PATH和MEMPAL_PALACE_PATH两个前缀,后者是历史包袱,保留旧名字体现了对已部署用户的尊重。

4. 验证一次记忆写入与检索:确认四层栈协同工作

配置写完不算数,得跑一次真实的写入 + 检索,确认 L0–L3 都在工作。

4.1 写入一条记忆

import chromadb from pathlib import Path client = chromadb.PersistentClient(path=str(Path.home() / ".mempalace/palace")) col = client.get_or_create_collection("mempalace_drawers") col.add( ids=["drawer_001"], documents=[ "team decided to migrate auth to Clerk over Auth0 — pricing and DX won out. " "Kai recommended Clerk after the OAuth token refresh bug in January." ], metadatas=[{ "wing": "wing_driftwood", "room": "auth-migration", "hall": "hall_facts", "importance": 0.9, "source_file": "chat_2026-01-15.md" }] ) print("写入完成,当前 drawer 数量:", col.count())

4.2 验证 L0 + L1 唤醒

from mempalace.layers import MemoryStack stack = MemoryStack() wake = stack.wake_up() print(wake) print("---") print("wake-up 估算 tokens:", len(wake) // 4)

预期输出里能看到## L0 — IDENTITY段落,以及## L1 — ESSENTIAL STORY下按 room 分组的条目,形如:

## L1 — ESSENTIAL STORY [auth-migration] - team decided to migrate auth to Clerk over Auth0 — pricing + DX won out... (chat_2026-01-15.md)

4.3 验证 L2 结构化 recall

recall = stack.recall(wing="wing_driftwood", room="auth-migration", n_results=5) print(recall)

L2 走的是col.get(where={"$and": [{"wing": ...}, {"room": ...}]}),纯 metadata 过滤,不触发语义相似度计算,返回的是该房间下的全部条目。

4.4 验证 L3 语义检索

result = stack.search("为什么从 Auth0 换到 Clerk", wing="wing_driftwood", n_results=3) print(result)

预期输出带 similarity 分数:

## L3 — SEARCH RESULTS for "为什么从 Auth0 换到 Clerk" [1] wing_driftwood/auth-migration (sim=0.87) team decided to migrate auth to Clerk over Auth0 — pricing and DX won out... src: chat_2026-01-15.md

看到sim=0.87说明 embedding 通道正常,wing_driftwood/auth-migration说明 metadata 过滤生效。四层栈协同工作的证据链就齐了:L0 身份常驻、L1 自动生成故事、L2 结构化召回、L3 语义检索。

4.5 用 TaoToken 模型对话做一次端到端确认

如果你想让对话模型也走 TaoToken,可以这样验证通道:

curl https://taotoken.net/api/chat/completions \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-3-5-sonnet", "messages": [{"role": "user", "content": "用一句话总结 auth-migration 的决策"}] }'

返回正常即说明 Key 与 Base URL 都对。想直接在网页里试模型效果,可以走模型对话入口;长期编码或 Agent 场景建议用 Coding Plan。

5. 本篇常见报错排查

5.1 401 Unauthorized

最常见的原因是TAOTOKEN_API_KEY没导出到当前 shell,或者 config.json 里api_key_env写错了变量名。排查:

echo $TAOTOKEN_API_KEY # 应输出 sk- 开头的字符串,为空说明没 source 到

如果用了 Cline MCP,注意 MCP 进程的环境变量是独立继承的,需要在mcpServers.mempalace.env里显式写一遍,不能只依赖 shell。

5.2 local proxy failed / connection refused

这个报错通常出现在你把 Base URL 写成了https://taotoken.net/api/(多了尾斜杠)或写成了http://。正确写法是https://taotoken.net/api,不带尾斜杠。另外检查本机是否有其他进程占用了代理端口,MemPalace 本身不需要任何本地代理。

5.3 reading 'choices' of undefined

这是 OpenAI 兼容接口返回体解析失败的典型报错。原因通常是请求打到了错误的 endpoint,比如把 embedding 请求发到了/chat/completions。embedding 应该走/embeddings,对话走/chat/completions。检查 config.json 里embedding.base_url是否指向https://taotoken.net/api。

5.4 OAuth token refresh 相关报错

如果你在 Claude Code 里挂 MemPalace MCP,同时用了 OAuth 登录,可能出现 token 刷新冲突。解决方式是让 MCP 进程走独立的 API Key 通道,不要复用 Claude Code 的 OAuth 会话。在mcpServers.mempalace.env里单独指定TAOTOKEN_API_KEY即可。

5.5 ChromaDB SQLite variable limit

批量读取时如果一次col.get(limit=5000),在旧版 SQLite 上会炸,因为 prepared statement 参数上限是 999。MemPalace 的做法是_BATCH = 500分批取。你自己写代码时照抄这个 magic number,留 2x 安全系数。

5.6 Apple Silicon 段错误

在 M 系列 Mac 上,ONNX Runtime 的 CoreML provider 在向量查询时会 segfault。MemPalace 在__init__.py里加了一行:

if platform.machine() == "arm64" and platform.system() == "Darwin": os.environ.setdefault("ORT_DISABLE_COREML", "1")

用setdefault而非直接赋值,意味着用户自己设了就听用户的。如果你遇到段错误,确认这行代码在你的版本里存在;如果未来 CoreML 修好了,可以export ORT_DISABLE_COREML=0开回来。

5.7 三件套检查清单

任何接入类报错,先对照这张表:

项目正确值常见错误
Base URLhttps://taotoken.net/api多尾斜杠、写成 http
KeyTAOTOKEN_API_KEY环境变量未导出、变量名拼错
Model IDtext-embedding-3-small写成对话模型名

三件套写全,90% 的接入报错都能定位。

6. 把记忆系统拆成“常驻 + 按需”两部分

如果你正在写自家的 Agent Memory,这一篇最值得带走的一条可执行建议是:把你的记忆系统拆成“常驻 L0/L1 + 按需 L2/L3”两部分,给常驻部分定死字节预算,然后围绕这个预算倒推存储模型。

常驻预算控制在 1000 tokens 以内,单次检索预算也控制在 1000 tokens 以内,总预算目标不超过 context window 的 5%。这个“5% 预算”原则,比任何具体的 embedding 模型选型都更值得借鉴。记忆系统的首要指标是“占得少”,其次才是“记得准”。

MemPalace 的 96.6% R@5 来自 raw mode + wing/room 过滤,不是 AAAK,不是 rerank;34% 的提升来自标准 metadata filter,不是黑魔法。作者在 README 顶部那封 4 月 7 日的公开信,把 AAAK 的 12.4 个点回退、token 计数用len//3的粗糙估算、contradiction detection 没 wire 起来这些事全列清楚了。这种“对 benchmark 数字负责”的态度,决定了你读它源码时可以默认它不在糊弄你。

下一篇进入挖矿流水线:convo_miner.py、room_detector_local.py、entity_detector.py,看一份原始对话导出是如何被切成 chunk、打上 wing/room/hall metadata、最后写进 ChromaDB 的完整管线。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询