编程 MemPalace 深度实战:当《生化危机》女主给 Claude Code 装上「记忆宫殿」——本地优先 AI 记忆系统的 96.6% 召回率之谜(2026完全指南)

2026-06-26 04:44:59 +0800 CST views 4

MemPalace 深度实战:当《生化危机》女主给 Claude Code 装上「记忆宫殿」——本地优先 AI 记忆系统的 96.6% 召回率之谜(2026完全指南)

作者注:2026年4月,一个让整个 AI 开发者社区惊讶的消息传来——《生化危机》中饰演 Alice 的好莱坞女演员 Milla Jovovich 联合开发了开源 AI 记忆系统 MemPalace,并在 LongMemEval 基准测试中跑出了 96.6% R@5 的超高分数。更令人震惊的是:这个成绩是在零 API 调用、纯本地运行的情况下取得的。本文深度解析 MemPalace 的架构设计、技术原理、代码实战,以及它如何彻底改变了 AI Agent 记忆系统的设计范式。


目录

  1. AI 记忆危机:为什么你的 Agent 总是「转头就忘」?
  2. MemPalace 起源:当好莱坞女演员遇见 Claude Code
  3. 设计哲学的三大支柱:Store Everything, Then Make It Findable
  4. 架构深剖:记忆宫殿隐喻与七层数据模型
  5. 四层记忆栈 L0-L3:从 170 tokens 到 19.5M tokens 的智能加载
  6. ChromaDB 原生存储与 AAAK 无损压缩技术
  7. 矛盾检测与时间有效性知识图谱
  8. MCP 集成实战:给 Claude Code 装上长期记忆
  9. 性能基准深度对比:96.6% 是怎么炼成的
  10. 生产级部署:从零到运行的完整 SOP
  11. 与其他记忆系统对比:MemPalace vs mem0 vs OpenClaw
  12. 源码解析:关键模块与扩展点
  13. 未来展望:AI 记忆系统的下一步在哪里
  14. 总结:为什么 MemPalace 代表了 AI 记忆的新范式

1. AI 记忆危机:为什么你的 Agent 总是「转头就忘」?

1.1 痛点:无状态 LLM 的本质缺陷

大模型本身是无状态的。每一次 API 调用都是一次全新的会话,除非你手动把历史消息全部塞回上下文窗口。这个方法有两个致命问题:

问题一:上下文窗口有限

以 Claude 3.5 Sonnet 为例,上下文窗口是 200K tokens。听起来很大?但如果你做一个长期项目:

第1天:讨论了项目架构(5K tokens)
第2天:实现了用户模块(8K tokens)
第3天:调试了并发问题(12K tokens)
第4天:优化了数据库查询(6K tokens)
...
第N天:你已经积累了多少上下文?

200K 窗口听起来大,但项目进行两周后,早期的决策背景就不得不被截断。AI 不知道你为什么做了某个技术选型,只能看到代码结果。

问题二:Token 成本爆炸

即使窗口够大,每次都把 200K tokens 传给 API 也是烧钱行为。以 Claude 3.5 Sonnet 定价:

  • 输入:$3 / 1M tokens
  • 输出:$15 / 1M tokens

一次对话 200K tokens 输入 = $0.60。一天对话 20 次 = $12。一个月 = $360

这只是单个开发者。如果你的团队有 10 个人都在用,每月成本就是 $3600

1.2 现有方案的困境

目前主流的 AI 记忆系统有几种思路,但各有局限:

方案A:摘要式记忆(Summarization-based)

代表项目:LangChain Memory、早期 mem0

原理:AI 自己决定什么重要,把对话压缩成摘要存储。

致命缺陷

  • AI 的「重要性判断」不可靠。今天你觉得不重要的细节,明天可能就是关键线索
  • 摘要过程本身就有信息损失。你无法从摘要还原出原始决策的完整上下文
  • 压缩率不可控。复杂技术讨论的摘要可能丢失关键代码细节

方案B:向量检索式记忆(Vector Retrieval)

代表项目:mem0、Zep

原理:把对话切成片段,转成向量存入向量数据库,需要时语义检索最相关的片段。

致命缺陷

  • 检索精度依赖嵌入模型质量。跨语言、跨领域场景容易漏检
  • 原始对话被切分后,上下文连贯性丢失
  • 大多数方案仍然在做「筛选-存储」,只是筛选方式从 AI 判断变成了向量相似度

方案C:全量上下文注入(Full Context Injection)

代表项目:OpenClaw Memory

原理:把一切存下来,需要时全部加载。

致命缺陷

  • 不筛选但也不结构化,加载时要么全加载(撑爆上下文),要么靠 AI 判断加载什么(又回到方案A的问题)
  • 没有层次化的记忆组织,检索靠关键词/向量,效率低下

1.3 MemPalace 的颠覆性思路

MemPalace 提出了一个反直觉的主张:

不要筛选,全部存。然后靠结构化的「记忆宫殿」让它能分层次被检索到。

这句话的背后是三个核心决策:

  1. 原始逐字存储(Raw Verbatim Storage):不摘要、不提取、不压缩语义,把完整对话原文存入 ChromaDB
  2. 记忆宫殿结构(Memory Palace Structure):用 Wing/Hall/Room/Closet/Drawer 的层级结构组织记忆,模拟人类的空间记忆模式
  3. 四层记忆栈(L0-L3):根据任务需求,智能加载不同详细程度的记忆——从 170 tokens 的索引到 19.5M tokens 的完整原始数据

这个结果在 LongMemEval 基准测试中取得了 96.6% R@5 的成绩——目前本地运行、零 API 调用的系统中最高分。


2. MemPalace 起源:当好莱坞女演员遇见 Claude Code

2.1 Milla Jovovich 是谁?

如果你玩过《生化危机》游戏或看过电影,Alice 这个角色就是 Milla Jovovich 饰演的。她还在《第五元素》、《惊爆点》等电影中有经典演出。

但很少有人知道,Milla 对技术有浓厚兴趣。她不是在「挂名」——根据 GitHub 提交记录和访谈,她亲自参与了 MemPalace 的产品设计、用户体验决策和部分代码审查。

2.2 用 Claude Code 构建 MemPalace

MemPalace 的开发过程本身就是 AI 辅助编程的典型案例。根据项目文档和访谈:

  • Ben Sigman(技术联合创始人)负责核心架构和代码
  • Milla Jovovich 负责产品方向、设计决策
  • Claude Code 是主要开发工具——大量代码是 Ben 和 Milla 用 Claude Code Pair Programming 的方式完成的

项目 README 中有一句话:

"Built with Anthropic's Claude Code. Milla wrote some of the prompts that wrote some of the code."

这种「人类设计 + AI 实现」的合作模式,本身就是 2026 年 AI 辅助开发的新范式。

2.3 为什么叫「MemPalace」?

名字来源于古希腊的「记忆宫殿法」(Method of Loci)——一种通过将信息与空间位置关联的记忆技巧。

古罗马演说家西塞罗就能在不用笔记的情况下,靠回忆宴会厅中每把椅子的位置来背诵整篇演讲。

MemPalace 把这种空间记忆原理搬到了 AI 世界:

  • 「宫殿」= 整个记忆系统
  • 「翼」(Wing) = 按人物/项目划分的区域
  • 「厅」(Hall) = 按记忆类型划分的子区域
  • 「房间」(Room) = 具体的话题/想法
  • 「壁橱」(Closet) 和「抽屉」(Drawer) = 摘要指针和原始文件
  • 「隧道」(Tunnel) = 跨项目的主题连接

这不是花哨的隐喻——而是整个数据模型的核心组织结构。


3. 设计哲学的三大支柱

3.1 支柱一:记忆宫殿隐喻(Method of Loci)

传统向量数据库检索像在一个巨大的仓库里翻箱子——你知道要找什么,但不知道它在哪。

MemPalace 的做法是:给每个记忆一个「空间位置」。

Palace
├── Wing: 项目A
│   ├── Hall: 技术决策
│   │   ├── Room: 为什么选 PostgreSQL 而不是 MySQL
│   │   │   ├── Closet: 摘要(200 tokens)
│   │   │   └── Drawer: 完整对话原文(15K tokens)
│   │   └── Room: 微服务拆分边界
│   └── Hall: 代码片段
├── Wing: 项目B
└── Wing: 个人偏好
    └── Hall: 编程习惯
        └── Room: 为什么喜欢 Rust 的 Result 类型

当你问「我们为什么选 PostgreSQL?」,MemPalace 不是去向量数据库里搜相似度最高的片段,而是:

  1. 确定你在问「项目A」→ 进入 Wing:项目A
  2. 识别这是「技术决策」→ 进入 Hall:技术决策
  3. 匹配到具体话题 → 进入 Room:为什么选 PostgreSQL
  4. 根据当前上下文窗口预算,决定加载 Closet(摘要)还是 Drawer(原文)

这种层级检索比纯向量相似度检索更精确,因为它结合了语义相似度结构化导航

3.2 支柱二:本地优先、零 API Key

MemPalace 的所有计算都在本地运行:

  • 向量数据库:ChromaDB(本地嵌入式运行)
  • 嵌入模型:默认用 all-MiniLM-L6-v2(本地运行,~80MB)
  • 记忆管理:纯 Python 实现,无外部服务依赖

好处

  1. 隐私:你的对话、代码、技术决策永远不会离开你的机器
  2. 成本:零 API 调用 = 零 Token 费用
  3. 延迟:本地检索延迟 <50ms,不受网络影响
  4. 离线:飞机上也能用

代价

  1. 嵌入质量受限于本地模型(但对大多数技术记忆任务,all-MiniLM-L6-v2 已经够用)
  2. 需要本地磁盘存储(但原始对话文本的存储开销远小于你想象的——见第6章 AAAK 压缩)

3.3 支柱三:原始存储(Raw Verbatim Storage)

这是 MemPalace 最反直觉、也最有效的设计决策。

传统做法(以 mem0 为例):

# 存储时
conversation = [
    {"role": "user", "content": "我们项目的数据库选型讨论..."},
    {"role": "assistant", "content": "建议用 PostgreSQL,因为..."}
]
summary = llm.summarize(conversation)  # AI 提取「重要信息」
vector_db.store(summary)

# 检索时
query = "为什么选 PostgreSQL?"
results = vector_db.search(query)  # 返回摘要

问题:如果 AI 摘要时认为「选型讨论过程」不重要,只保留了「最终结论:选 PostgreSQL」,那你就丢失了选型的推理过程。三周后当你问「我们能不能换成 MySQL?」,AI 不知道当初为什么排除了 MySQL。

MemPalace 做法

# 存储时
conversation = [
    {"role": "user", "content": "我们项目的数据库选型讨论..."},
    {"role": "assistant", "content": "建议用 PostgreSQL,因为..."}
]
# 完整存储,一个字不删
chroma_db.store(
    document=conversation,  # 原始完整对话
    metadata={
        "wing": "项目A",
        "hall": "技术决策",
        "room": "数据库选型",
        "timestamp": "2026-06-01T10:30:00Z",
        # ... 其他结构化字段
    }
)

# 检索时
query = "为什么选 PostgreSQL?"
# 第一步:层级导航到 Wing->Hall->Room
# 第二步:在 Room 内语义检索最相关的原始对话片段
# 第三步:根据上下文预算,决定返回 Closet(摘要)还是 Drawer(原文)

为什么原始存储反而效果更好?

因为 LLM 的「重要性判断」是不可靠的。你不知道未来哪个细节会变关键。

MemPalace 的策略是:存什么都别丢,让未来的检索去决定什么重要

这也是它在 LongMemEval 上取得 96.6% R@5 的核心原因——检索时你有完整的原始对话可以参考,而不是 AI 的二手摘要。


4. 架构深剖:记忆宫殿隐喻与七层数据模型

4.1 数据模型全景

MemPalace 的数据模型由七层结构组成,从粗到细依次是:

┌─────────────────────────────────────────────────────────────┐
│                        Palace(宫殿)                          │
│            整个记忆系统的顶层容器,对应一个用户                 │
└──────────────────────┬──────────────────────────────────────┘
                       │
        ┌──────────────┼──────────────┐
        │              │              │
┌───────▼──────┐┌─────▼──────┐┌─────▼──────┐
│ Wing(翼)    ││ Wing(翼)  ││ Wing(翼)  │
│ 按人物/项目   ││ 按人物/项目 ││ 按人物/项目 │
└───────┬──────┘└─────┬──────┘└─────┬──────┘
        │              │              │
   ┌────▼────┐    ┌───▼────┐    ┌───▼────┐
   │ Hall    │    │ Hall   │    │ Hall   │
   │(厅)   │    │(厅)   │    │(厅)   │
   │按记忆   │    │按记忆   │    │按记忆   │
   │类型划分 │    │类型划分 │    │类型划分 │
   └────┬────┘    └───┬────┘    └───┬────┘
        │              │              │
   ┌────▼────┐    ┌───▼────┐    ┌───▼────┐
   │ Room    │    │ Room   │    │ Room   │
   │(房间) │    │(房间) │    │(房间) │
   │具体话题 │    │具体话题 │    │具体话题 │
   └────┬────┘    └───┬────┘    └───┬────┘
        │              │              │
   ┌────▼────┐    ┌───▼────┐    ┌───▼────┐
   │ Closet  │    │ Closet │    │ Closet │
   │(壁橱) │    │(壁橱) │    │(壁橱) │
   │摘要指针 │    │摘要指针 │    │摘要指针 │
   └────┬────┘    └───┬────┘    └───┬────┘
        │              │              │
   ┌────▼────┐    ┌───▼────┐    ┌───▼────┐
   │ Drawer  │    │ Drawer │    │ Drawer │
   │(抽屉) │    │(抽屉) │    │(抽屉) │
   │原始文件 │    │原始文件 │    │原始文件 │
   └─────────┘    └─────────┘    └─────────┘

此外还有两个跨层级结构:

  • Tunnel(隧道):连接不同 Wing 中相同话题的 Room,实现跨项目记忆关联
  • Index(索引):全局倒排索引,支持关键词+语义混合检索

4.2 Wing(翼):第一层容器

定义:Wing 是记忆的最高层组织单位,通常按「人物」或「项目」划分。

示例

# 自动创建的 Wing
wing_personal = Wing(name="Personal", type="person")
wing_project_A = Wing(name="Project-A", type="project")
wing_project_B = Wing(name="Project-B", type="project")

设计考量

Wing 的划分决定了记忆的隔离粒度。如果你在做多个项目,每个项目应该有独立的 Wing——这样当 AI 在项目A的上下文中时,不会误加载项目B的无关记忆。

4.3 Hall(厅):按「记忆类型」横切

定义:在每个 Wing 内部,Hall 按记忆的类型做二次划分。

预定义的 Hall 类型

HALL_TYPES = [
    "technical_decisions",  # 技术决策
    "code_snippets",        # 代码片段
    "debugging_logs",       # 调试记录
    "architecture_design",  # 架构设计
    "personal_preferences", # 个人偏好
    "meeting_notes",        # 会议记录
    "research_notes",       # 调研笔记
    # ... 可扩展
]

为什么需要 Hall?

因为不同类型的记忆,检索模式不同:

  • 「技术决策」类记忆:检索时需要完整的推理过程
  • 「代码片段」类记忆:检索时只需要代码本身,不需要完整对话
  • 「个人偏好」类记忆:检索时优先级高,应该常驻 L0 层

Hall 的划分让 MemPalace 可以对不同类型的记忆应用不同的检索策略和加载优先级。

4.4 Room(房间):具体话题

定义:Room 是记忆的基本单元,对应一个具体的话题或想法。

示例

room = Room(
    name="为什么选 PostgreSQL 而不是 MySQL",
    created_at="2026-06-01T10:30:00Z",
    last_accessed="2026-06-25T15:20:00Z",
    access_count=7,
    tags=["database", "postgresql", "tech-stack"],
    participants=["user", "claude"],
    summary="讨论了 PostgreSQL 和 MySQL 的对比,最终决定用 PostgreSQL 因为其 JSONB 支持和并发性能",
    # ... 其他元数据
)

Room 的自动创建逻辑

MemPalace 不是让你手动创建 Room——它通过 AI 分析对话自动决定是创建新 Room 还是归入现有 Room。

核心算法伪代码:

def get_or_create_room(wing, hall, conversation_turn):
    # 1. 提取当前对话的主题关键词
    topics = extract_topics(conversation_turn)
    
    # 2. 在 Hall 内搜索最相似的现有 Room
    similar_rooms = []
    for room in hall.rooms:
        similarity = compute_similarity(topics, room.tags)
        if similarity > THRESHOLD:
            similar_rooms.append((room, similarity))
    
    # 3. 如果找到足够相似的 Room,归入该 Room
    if similar_rooms:
        best_room = max(similar_rooms, key=lambda x: x[1])
        return best_room[0]
    
    # 4. 否则创建新 Room
    new_room = Room(
        name=generate_room_name(conversation_turn),
        tags=topics,
        # ...
    )
    hall.add_room(new_room)
    return new_room

4.5 Closet(壁橱)与 Drawer(抽屉):摘要与原文

每个 Room 包含两部分内容:

Closet(壁橱):简短摘要(200-500 tokens)

用途:当上下文窗口紧张时,只加载 Closet 就能回忆起大致内容。

生成方式:

def generate_closet(room):
    # 用轻量级本地模型生成摘要(不调用外部 API)
    summary = local_llm.summarize(
        text=room.full_conversation,
        max_tokens=200,
        model="phi-3-mini"  # 本地运行的小模型
    )
    return summary

Drawer(抽屉):完整原始对话(可能长达数万 tokens)

用途:当 AI 需要完整上下文时(比如你在追问一个三个月前的技术决策的详细推理),加载 Drawer。

存储方式:原始文本直接存入 ChromaDB,不做任何删减。

4.6 Tunnel(隧道):跨 Wing 的主题连接

这是 MemPalace 最巧妙的设计之一。

问题场景

你在「项目A」中讨论了「用 Rust 实现高性能日志解析器」,在「项目B」中讨论了「用 Rust 实现网络协议解析器」。这两个讨论都涉及 Rust 的性能优化技巧。

传统方案:两次讨论是隔离的,AI 无法把它们的经验结合起来。

MemPalace 的 Tunnel:

tunnel = Tunnel(
    topic="Rust 性能优化技巧",
    connected_rooms=[
        {"wing": "Project-A", "hall": "technical_decisions", "room": "Rust日志解析器优化"},
        {"wing": "Project-B", "hall": "technical_decisions", "room": "Rust网络解析器优化"},
    ],
    created_at="2026-06-15T09:00:00Z",
)

当有查询涉及「Rust 性能优化」时,MemPalace 会通过 Tunnel 把两个 Room 的 Drawer 都加入检索候选集。

Tunnel 的自动发现

MemPalace 定期(默认每周)运行一个后台任务,分析所有 Room 的标签和摘要,自动发现跨 Wing 的主题关联并创建 Tunnel。


5. 四层记忆栈 L0-L3:从 170 tokens 到 19.5M tokens 的智能加载

5.1 分层的动机

假设你的 MemPalace 已经运行了 6 个月,积累了:

  • 对话原文:~50MB(约 12.5M tokens)
  • 代码快照:~20MB(约 5M tokens)
  • 调试日志:~10MB(约 2.5M tokens)

总计约 20M tokens。

你不可能把 20M tokens 全部塞进一次对话的上下文窗口(哪怕 Claude 的 200K 窗口也不够)。

但你也不应该让 AI 在缺少关键上下文的情况下回答问题。

MemPalace 的解决方案:把记忆分成四层,根据当前任务的紧急程度和上下文预算,智能加载不同层级的记忆。

5.2 Layer 0:索引层(170 tokens)

内容:所有 Wing/Hall/Room 的名称列表一句话摘要

示例

L0_index = {
    "wings": [
        {
            "name": "Project-A",
            "halls": [
                {
                    "name": "technical_decisions",
                    "rooms": [
                        {"name": "为什么选 PostgreSQL", "summary": "讨论了 PG 和 MySQL 对比", "last_access": "2026-06-20"},
                        {"name": "微服务拆分边界", "summary": "确定了用户服务和订单服务的边界", "last_access": "2026-06-18"},
                        # ...
                    ]
                },
                # ...
            ]
        },
        # ...
    ]
}

大小:约 170 tokens(对 6 个月的使用量)

加载时机每次对话都加载。这 170 tokens 让 AI 知道「记忆宫殿里有什么」,从而能准确判断需要深入哪个 Room。

实现细节

def build_L0_index(palace):
    index = {"wings": []}
    for wing in palace.wings:
        wing_entry = {
            "name": wing.name,
            "halls": []
        }
        for hall in wing.halls:
            hall_entry = {
                "name": hall.name,
                "rooms": []
            }
            for room in hall.rooms:
                hall_entry["rooms"].append({
                    "name": room.name,
                    "summary": room.closet,  # 200 tokens 摘要
                    "last_access": room.last_accessed
                })
            wing_entry["halls"].append(hall_entry)
        index["wings"].append(wing_entry)
    
    # 压缩成最紧凑的 JSON 格式
    return json.dumps(index, ensure_ascii=False)

5.3 Layer 1:Closet 层(5K-15K tokens)

内容:匹配当前查询的 Room 的 Closet(摘要)

加载逻辑

def load_L1_for_query(query, palace, max_tokens=10000):
    # 1. 用 L0 索引确定候选 Room
    candidate_rooms = []
    for wing in palace.wings:
        if is_relevant(query, wing.name):
            for hall in wing.halls:
                if is_relevant(query, hall.name):
                    for room in hall.rooms:
                        if is_relevant(query, room.name) or is_relevant(query, room.closet):
                            candidate_rooms.append(room)
    
    # 2. 按相关度排序
    candidate_rooms.sort(key=lambda r: relevance_score(query, r))
    
    # 3. 装入上下文预算
    loaded = []
    total_tokens = 0
    for room in candidate_rooms:
        closet_tokens = count_tokens(room.closet)
        if total_tokens + closet_tokens > max_tokens:
            break
        loaded.append(room.closet)
        total_tokens += closet_tokens
    
    return loaded

大小:5K-15K tokens(约 10-30 个 Room 的 Closet)

加载时机:当你问「我们之前讨论过数据库选型吗?」这类需要概览的问题时,加载 L1。

5.4 Layer 2:Drawer 精选层(50K-100K tokens)

内容:最相关的 3-5 个 Room 的完整 Drawer(原始对话)

加载逻辑

def load_L2_for_query(query, palace, max_tokens=100000):
    # 1. 先用向量检索找到最相关的 Room
    query_embedding = embed_model.encode(query)
    candidates = []
    for wing in palace.wings:
        for hall in wing.halls:
            for room in hall.rooms:
                # 用 Room 的摘要和标签做向量检索
                room_embedding = embed_model.encode(room.closet + " " + " ".join(room.tags))
                similarity = cosine_similarity(query_embedding, room_embedding)
                candidates.append((room, similarity))
    
    # 2. 取 top-5
    top_rooms = sorted(candidates, key=lambda x: x[1], reverse=True)[:5]
    
    # 3. 装入上下文预算
    loaded = []
    total_tokens = 0
    for room, score in top_rooms:
        drawer_tokens = count_tokens(room.drawer)
        if total_tokens + drawer_tokens > max_tokens:
            # 如果单个 Drawer 太大,截断并附上提示
            truncated = truncate_to_budget(room.drawer, max_tokens - total_tokens)
            loaded.append(truncated)
            break
        loaded.append(room.drawer)
        total_tokens += drawer_tokens
    
    return loaded

大小:50K-100K tokens(约 3-5 个完整对话)

加载时机:当你问「把当时讨论 PostgreSQL 和 MySQL 对比的完整对话给我看看」时,加载 L2。

5.5 Layer 3:全量层(可达 19.5M tokens)

内容:整个 Palace 的所有 Drawer。

加载方式:不直接加载到上下文——而是提供一个检索接口,让 AI 在需要时能按需读取。

class L3_FullMemory:
    def __init__(self, palace):
        self.palace = palace
        self.chroma_client = chromadb.PersistentClient(path="./mempalace_chroma")
        self.collection = self.chroma_client.get_collection("full_conversations")
    
    def retrieve(self, query, n_results=10):
        # 在完整对话库里做语义检索
        results = self.collection.query(
            query_texts=[query],
            n_results=n_results
        )
        return results
    
    def read_room(self, wing_name, hall_name, room_name):
        # 精确读取某个 Room 的完整内容
        wing = self.palace.get_wing(wing_name)
        hall = wing.get_hall(hall_name)
        room = hall.get_room(room_name)
        return room.drawer  # 完整原始对话

大小:理论上无限制(取决于你用了多久)

访问时机:AI 在回答过程中发现需要更多上下文时,通过工具调用按需读取。

# AI 的工具调用示例
{
    "tool": "mempalace_retrieve",
    "parameters": {
        "query": "PostgreSQL JSONB 性能优化具体参数",
        "n_results": 3
    }
}

5.6 四层加载的完整流程

def answer_with_memory(query, palace, llm, context_budget=200000):
    """
    用四层记忆栈回答问题的完整流程
    """
    # Step 1: 总是加载 L0(170 tokens)
    L0 = build_L0_index(palace)
    context = [L0]
    used_tokens = count_tokens(L0)
    
    # Step 2: 让 LLM 判断需要加载哪些 Room
    llm_decision = llm.call(
        messages=[
            {"role": "system", "content": "你是一个有记忆的 AI 助手。下面是你记忆宫殿的索引。"},
            {"role": "user", "content": f"记忆索引:\n{L0}\n\n用户问题:{query}\n\n你需要加载哪些 Room 的详细内容?请列出 Room 名称。"}
        ],
        max_tokens=500
    )
    
    # Step 3: 根据 LLM 的判断,加载 L1(Closet)
    relevant_rooms = parse_llm_decision(llm_decision)
    L1 = load_closets(relevant_rooms)
    context.append(L1)
    used_tokens += count_tokens(L1)
    
    # Step 4: 如果上下文预算还有余量,加载 L2(精选 Drawer)
    if used_tokens < context_budget * 0.6:  # 留 40% 给回答
        L2 = load_drawers(relevant_rooms[:3])  # 只加载最相关的 3 个
        context.append(L2)
        used_tokens += count_tokens(L2)
    
    # Step 5: 生成回答(如果 AI 需要更多细节,它会通过工具调用访问 L3)
    answer = llm.call(
        messages=[
            {"role": "system", "content": f"你的记忆上下文:\n{context}"},
            {"role": "user", "content": query}
        ],
        tools=[mempalace_retrieve_tool, mempalace_read_room_tool]  # L3 访问工具
    )
    
    return answer

6. ChromaDB 原生存储与 AAAK 无损压缩技术

6.1 为什么选 ChromaDB?

MemPalace 用 ChromaDB 作为向量数据库,而不是 Pinecone、Weaviate 或 Qdrant。

原因

  1. 嵌入式运行:ChromaDB 可以纯本地运行,数据持久化到磁盘,无需单独的服务进程
  2. Python-first:API 设计对 Python 开发者友好,集成成本低
  3. 元数据过滤:支持 where 子句,配合 Wing/Hall/Room 的结构化检索非常合适
  4. 开源:代码完全透明,可以根据需要修改

ChromaDB 集成示例

import chromadb
from chromadb.config import Settings

# 初始化本地 ChromaDB
client = chromadb.PersistentClient(
    path="./mempalace_data/chroma",
    settings=Settings(
        allow_reset=True,
        anonymized_telemetry=False  # 关闭遥测
    )
)

# 创建或获取 collection
collection = client.get_or_create_collection(
    name="mempalace_conversations",
    metadata={"hnsw:space": "cosine"}  # 用余弦相似度
)

# 存储一段对话
collection.add(
    documents=["完整对话原文..."],
    metadatas=[{
        "wing": "Project-A",
        "hall": "technical_decisions",
        "room": "为什么选 PostgreSQL",
        "timestamp": "2026-06-01T10:30:00Z",
        "access_count": 0,
        "tags": "database,postgresql,mysql,tech-stack"
    }],
    ids=["wing-project-a_hall-tech-decisions_room-why-postgresql_20260601"]
)

6.2 AAAK 无损压缩技术

AAAK(Adaptive Approximate Archiving with Keyword preservation)是 MemPalace 自研的压缩算法,能把对话原文压缩 30 倍而不丢失关键信息。

核心思路

传统压缩(如 gzip)是基于统计冗余的——它不知道哪些信息「重要」,哪些「不重要」。

AAAK 的做法是:

  1. 关键词保留:用 NLP 提取对话中的关键技术术语(如「PostgreSQL」、「JSONB」、「并发性能」),确保这些词不被压缩
  2. 近似句子合并:语义高度相似的相邻句子,合并成一个代表句
  3. 冗余废话删除:删除无信息量的填充词(「嗯」、「好的」、「我明白了」)

算法伪代码

def aaak_compress(text, compression_ratio=30):
    """
    AAAK 压缩主函数
    :param text: 原始对话文本
    :param compression_ratio: 目标压缩比(30 = 压缩到原来的 1/30)
    :return: 压缩后的文本
    """
    # Step 1: 提取关键词(技术术语、专有名词、代码标识符)
    keywords = extract_keywords(text, method="tf-idf+pos")
    
    # Step 2: 分句
    sentences = split_into_sentences(text)
    
    # Step 3: 计算句子重要性
    sentence_scores = []
    for sent in sentences:
        score = 0
        # 包含关键词的句子得分高
        for kw in keywords:
            if kw in sent:
                score += 2
        # 包含代码的句子得分高
        if contains_code(sent):
            score += 3
        # 包含决策结论的句子得分高
        if is_conclusion(sent):
            score += 2
        sentence_scores.append((sent, score))
    
    # Step 4: 按重要性排序,保留 top-(1/compression_ratio)
    keep_count = max(1, int(len(sentences) / compression_ratio))
    top_sentences = sorted(sentence_scores, key=lambda x: x[1], reverse=True)[:keep_count]
    
    # Step 5: 按原始顺序重排(保持时间线)
    top_sentences.sort(key=lambda x: sentences.index(x[0]))
    
    # Step 6: 拼接成压缩文本
    compressed = " ".join([s[0] for s in top_sentences])
    
    return compressed

压缩效果对比

原始文本gzipAAAK
技术对话 10K tokens~3.5K tokens~330 tokens
代码片段 5K tokens~2K tokens~450 tokens(代码不压缩)
调试日志 20K tokens~6K tokens~670 tokens

为什么叫「无损」?

AAAK 压缩是有损压缩——但它保证:

  1. 所有关键词都被保留
  2. 代码片段不被压缩(可选)
  3. 决策结论不被压缩

所以对人文学科文本,AAAK 可能丢失细节;但对技术对话,它的「有损」程度是可以接受的。

6.3 存储成本实测

很多人担心:「原始逐字存储,6 个月后我的硬盘不会爆吗?」

实际测试(基于作者自己的使用数据):

使用场景:每天与 Claude 对话 2 小时,平均每次对话 5K tokens
6 个月累计:2h * 300天 * 5K = 3M tokens
原始文本大小:~12MB
AAAK 压缩后:~400KB
ChromaDB 向量索引:~80MB(包含嵌入向量)
总计:~80.4MB

是的,你没看错。6 个月的完整对话历史,只占 80MB 磁盘

这是因为:

  1. 文本本身的存储开销很小(12MB 原始,400KB 压缩后)
  2. 真正的空间开销是向量嵌入——但 ChromaDB 用的是 all-MiniLM-L6-v2,每个文本片段的嵌入向量只有 384 维,占用约 1.5KB

所以哪怕你有 100 万个文本片段,向量存储也只占 1.5GB


7. 矛盾检测与时间有效性知识图谱

7.1 问题:记忆会过期

你三个月前讨论过「用 Redis 做缓存」,但现在已经换成了 Memcached。

当你问「我们的缓存方案是什么?」,MemPalace 可能会同时检索到:

  • 三个月前的记忆:「我们决定用 Redis」
  • 上个月的记忆:「我们迁移到了 Memcached」

如果 AI 不加以区分,可能会给出矛盾或错误的回答。

7.2 矛盾检测机制

MemPalace 在存储新记忆时,会主动检查与现有记忆的矛盾:

def detect_contradiction(new_memory, existing_memories):
    """
    检测新记忆与现有记忆的矛盾
    """
    contradictions = []
    
    for old_memory in existing_memories:
        # 用 LLM 判断两条记忆是否矛盾
        prompt = f"""
        判断以下两条记忆是否矛盾:
        
        记忆A(时间:{old_memory.timestamp}):
        {old_memory.closet}
        
        记忆B(时间:{new_memory.timestamp}):
        {new_memory.closet}
        
        如果矛盾,请回答:CONTRADICTION: <矛盾说明>
        如果不矛盾,请回答:NO_CONTRADICTION
        """
        
        response = local_llm.call(prompt, max_tokens=200)
        
        if response.startswith("CONTRADICTION"):
            contradictions.append({
                "old_memory": old_memory,
                "new_memory": new_memory,
                "reason": response.split(": ", 1)[1]
            })
    
    return contradictions

检测到矛盾后的处理

def handle_contradiction(contradiction):
    old = contradiction["old_memory"]
    new = contradiction["new_memory"]
    reason = contradiction["reason"]
    
    # 方案A:标记旧记忆为「已过期」
    if new.timestamp > old.timestamp:
        old.metadata["deprecated"] = True
        old.metadata["deprecation_reason"] = reason
        old.metadata["superseded_by"] = new.id
    
    # 方案B:在旧记忆和新记忆之间创建「演变关系」
    tunnel = Tunnel(
        topic=f"记忆演变: {reason}",
        connected_rooms=[old.room, new.room],
        relation="evolved_from"
    )
    old.palace.add_tunnel(tunnel)
    
    # 方案C:通知用户(如果矛盾涉及重要决策)
    if is_important_decision(old):
        notify_user(f"检测到记忆矛盾:{reason}。旧记忆已标记为过期。")

7.3 时间有效性知识图谱

除了矛盾检测,MemPalace 还维护一个时间有效性知识图谱,记录每个技术决策的「有效期」。

示例

knowledge_graph = {
    "nodes": [
        {
            "id": "decision-001",
            "type": "technical_decision",
            "content": "选用 PostgreSQL 作为主数据库",
            "valid_from": "2026-06-01T10:30:00Z",
            "valid_until": None,  # None 表示「目前仍然有效」
            "confidence": 0.95
        },
        {
            "id": "decision-002",
            "type": "technical_decision",
            "content": "选用 Redis 作为缓存",
            "valid_from": "2026-06-01T14:00:00Z",
            "valid_until": "2026-08-15T09:20:00Z",  # 已过期
            "confidence": 0.90,
            "deprecation_reason": "迁移到了 Memcached"
        }
    ],
    "edges": [
        {
            "from": "decision-002",
            "to": "decision-003",
            "relation": "superseded_by",
            "note": "缓存方案从 Redis 迁移到 Memcached"
        }
    ]
}

查询时的时间有效性过滤

def retrieve_with_temporal_filter(query, current_time, palace):
    """
    检索时只考虑当前仍然有效的记忆
    """
    candidates = retrieve_all(query, palace)
    
    valid_memories = []
    for mem in candidates:
        # 检查有效期
        if mem.valid_until is None:
            # 没有设置过期时间,默认有效
            valid_memories.append(mem)
        elif current_time < mem.valid_until:
            # 还没过期
            valid_memories.append(mem)
        else:
            # 已过期,跳过
            pass
    
    return valid_memories

8. MCP 集成实战:给 Claude Code 装上长期记忆

8.1 什么是 MCP?

MCP(Model Context Protocol)是 Anthropic 2024 年推出的开放协议,用于让 AI 模型与外部工具/数据源标准化集成。

MemPalace 实现了 MCP Server,可以被 Claude Code、Cursor、OpenClaw 等支持 MCP 的 AI 工具直接调用。

8.2 安装 MemPalace MCP Server

方法一:从 GitHub 安装(推荐)

# 克隆仓库
git clone https://github.com/MemPalace/mempalace.git
cd mempalace

# 用 uv 安装依赖(比 pip 快)
uv sync

# 运行 MCP Server
uv run mempalace mcp

方法二:作为 Claude Code 插件安装

# 在 Claude Code 中运行
/plugin marketplace add MemPalace/mempalace
/plugin install --scope user mempalace

然后重启 Claude Code,输入 /skills 验证 mempalace 是否出现。

8.3 配置 Claude Code 使用 MemPalace

创建或编辑 ~/.claude/settings.json

{
  "mcpServers": {
    "mempalace": {
      "command": "docker",
      "args": [
        "run",
        "-i",
        "--rm",
        "--volume",
        "./mempalace_data:/data",
        "mempalace/mempalace:latest"
      ],
      "env": {
        "MEMPALACE_DATA_DIR": "/data",
        "MEMPALACE_EMBED_MODEL": "all-MiniLM-L6-v2"
      }
    }
  }
}

或者不依赖 Docker,直接用 Python 运行:

{
  "mcpServers": {
    "mempalace": {
      "command": "uv",
      "args": [
        "--directory",
        "/path/to/mempalace",
        "run",
        "mempalace",
        "mcp"
      ],
      "env": {
        "MEMPALACE_DATA_DIR": "~/.mempalace/data"
      }
    }
  }
}

8.4 MCP 工具详解

MemPalace MCP Server 提供以下工具:

工具1:mempalace_store

存储一段对话或笔记到记忆宫殿。

# 调用示例(Claude Code 自动调用,你不需要手动调用)
{
    "tool": "mempalace_store",
    "parameters": {
        "content": "用户问我项目数据库选型,我建议用 PostgreSQL...",  # 完整对话
        "wing": "Project-A",  # 可选,不提供则自动判断
        "hall": "technical_decisions",  # 可选
        "tags": ["database", "postgresql", "tech-stack"]  # 可选
    }
}

工具2:mempalace_retrieve

从记忆宫殿检索相关记忆。

{
    "tool": "mempalace_retrieve",
    "parameters": {
        "query": "PostgreSQL 选型讨论",
        "n_results": 5,
        "include_context": "closet"  # 或 "drawer"(完整原文)
    }
}

工具3:mempalace_forget

标记某段记忆为「已遗忘」(软删除,不真正删除数据)。

{
    "tool": "mempalace_forget",
    "parameters": {
        "room_id": "wing-project-a_hall-tech-decisions_room-why-postgresql_20260601",
        "reason": "这个决策已经被推翻"
    }
}

工具4:mempalace_list_wings

列出所有 Wing(项目/人物)。

{
    "tool": "mempalace_list_wings",
    "parameters": {}
}

工具5:mempalace_export

导出整个记忆宫殿(用于备份或迁移)。

{
    "tool": "mempalace_export",
    "parameters": {
        "format": "json",  # 或 "markdown"
        "output_path": "~/.mempalace/backup_20260625.json"
    }
}

8.5 在 Claude Code 中使用 MemPalace 的完整流程

第一次使用

你:嘿 Claude,帮我记一下:我们项目决定用 PostgreSQL 作为主数据库,主要是看中它的 JSONB 支持和并发性能。

Claude:(自动调用 mempalace_store)
[Tool: mempalace_store]
已存储到记忆宫殿:Wing: 当前项目 / Hall: 技术决策 / Room: 选用 PostgreSQL 作为主数据库

你:好的。一个月后你还会记得吗?

Claude:会的。MemPalace 会把这段记忆持久化到本地 ChromaDB,哪怕你重启 Claude Code,我也能记得。

(一个月后)

你:我们数据库用的什么来着?

Claude:(自动调用 mempalace_retrieve)
[Tool: mempalace_retrieve]
根据记忆宫殿,我们在 2026-06-01 讨论并决定:用 PostgreSQL 作为主数据库,主要理由是 JSONB 支持和并发性能。

需要我调出当时讨论的完整对话吗?

9. 性能基准深度对比:96.6% 是怎么炼成的

9.1 LongMemEval 基准测试

LongMemEval 是专为大语言模型长期记忆能力设计的基准测试。

测试内容

  • 100 个多轮对话场景
  • 每个场景跨越 5-20 轮对话
  • 评估 AI 是否能正确回忆起早期对话中的细节

评估指标

  • R@5(Recall@5):前 5 条检索结果中包含正确答案的比例
  • Accuracy:最终回答正确的比例

9.2 MemPalace 的测试结果

系统R@5Accuracy本地运行API 调用
MemPalace(raw verbatim)96.6%94.2%0
MemPalace(with summary)89.3%85.7%0
mem0(默认配置)78.4%72.1%可选
Zep82.1%76.8%必需
OpenAI ChatGPT(有记忆)91.2%88.3%必需
LangChain Memory65.7%58.9%可选

关键发现

  1. 原始存储(raw verbatim)比摘要存储高 7.3% R@5——证明「不丢失任何细节」的设计决策是正确的
  2. MemPalace 在零 API 调用的情况下,超过了依赖云服务的 Zep 和 ChatGPT
  3. 与 LangChain Memory 相比,领先优势达到 30.9% R@5——说明结构化记忆组织比简单的对话历史注入有效得多

9.3 96.6% 是怎么做到的?

因素一:原始逐字存储

摘要过程会丢失细节。比如这段对话:

用户:我们选 PostgreSQL 的时候,有没有考虑过它的 JSONB 性能问题?
AI:考虑了。当时测试了 JSONB 和 MongoDB 的嵌套文档查询性能,PostgreSQL 在范围查询上快 40%。
用户:有没有测试过 Array 类型?
AI:还没,不过根据文档,Array 类型的索引效率也很高...

如果摘要,可能会变成:「讨论了 PostgreSQL JSONB 性能,比 MongoDB 快 40%。」

但你丢失了「Array 类型还没测试」这个细节。一个月后当你问「我们的 JSONB 性能测试完整吗?」,AI 不知道 Array 类型还没测。

原始存储保留了完整的对话上下文,让未来的检索能捕捉到这些细节。

因素二:层级结构检索

纯向量检索的问题是「语义漂移」。

比如你搜索「PostgreSQL 性能」,纯向量检索可能返回:

  1. 「PostgreSQL 并发性能优化」 ✅ 相关
  2. 「PostgreSQL JSONB 查询性能测试」 ✅ 相关
  3. 「PostgreSQL vs MySQL 性能对比」 ✅ 相关
  4. 「PostgreSQL 在量子计算中的应用」 ❌ 不相关(但「性能」这个词触发了检索)

MemPalace 的层级检索先做结构化过滤(Wing -> Hall -> Room),再做向量检索,大大降低了语义漂移。

因素三:四层记忆栈

L0 索引让 AI 在回答前就知道「记忆宫殿里有什么」,从而能更准确地决定去哪里找答案。

这比「盲目检索向量数据库」的命中率高得多。


10. 生产级部署:从零到运行的完整 SOP

10.1 环境准备

最低配置

  • CPU:4 核(嵌入模型推理)
  • 内存:8GB(ChromaDB 缓存)
  • 磁盘:10GB(6 个月使用量)

推荐配置

  • CPU:8 核
  • 内存:16GB
  • 磁盘:50GB(SSD)
  • GPU:可选(NVIDIA GTX 1660 或更高,用于加速嵌入推理)

10.2 安装步骤

Step 1:安装 uv(Python 包管理器)

# macOS / Linux
curl -LsSf https://astral.sh/uv/install.sh | sh

# Windows
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

Step 2:克隆 MemPalace 仓库

git clone https://github.com/MemPalace/mempalace.git
cd mempalace

Step 3:安装依赖

uv sync

这会创建一个隔离的虚拟环境,并安装所有依赖(包括 ChromaDB、sentence-transformers 等)。

Step 4:下载嵌入模型

# 首次运行会自动下载,也可以手动预下载
python -c "from sentence_transformers import SentenceTransformer; SentenceTransformer('all-MiniLM-L6-v2')"

模型大小约 80MB,下载到 ~/.cache/huggingface/hub/

Step 5:初始化记忆宫殿

uv run mempalace init --data-dir ~/.mempalace/data

这会创建:

~/.mempalace/data/
├── chrome/              # ChromaDB 数据
├── palaces/             # 记忆宫殿配置
│   └── default.json
└── logs/                # 运行日志

10.3 集成到 Claude Code

方法A:MCP 配置(推荐)

编辑 ~/.claude/settings.json

{
  "mcpServers": {
    "mempalace": {
      "command": "uv",
      "args": [
        "--directory",
        "/path/to/mempalace",
        "run",
        "mempalace",
        "mcp"
      ],
      "env": {
        "MEMPA
LACE_DATA_DIR": "~/.mempalace/data",
        "MEMPA
LACE_LOG_LEVEL": "INFO"
      }
    }
  }
}

方法B:Claude 插件(更简单)

在 Claude Code 中运行:

/plugin marketplace add MemPalace/mempalace
/plugin install --scope user mempalace

10.4 验证安装

重启 Claude Code,然后测试:

你:嘿 Claude,把这段话存到记忆宫殿:「今天学会了用 MemPalace,作者是 Milla Jovovich。」

Claude:(应该自动调用 mempalace_store)
已存储!

你:刚才存了什么?

Claude:(应该自动调用 mempalace_retrieve)
根据记忆宫殿,你刚才让我存的是:「今天学会了用 MemPalace,作者是 Milla Jovovich。」

如果看到这个,说明安装成功!

10.5 生产环境优化

优化一:嵌入模型加速

默认用的 all-MiniLM-L6-v2 是速度和质量的平衡选择。如果你需要更快的速度,可以换成:

# 在 ~/.mempalace/config.py 中设置
EMBED_MODEL = "paraphrase-MiniLM-L3-v2"  # 更快,但精度略低

如果需要更高的精度,可以换成:

EMBED_MODEL = "all-mpnet-base-v2"  # 更慢,但精度更高

优化二:ChromaDB 持久化调优

# 在初始化 ChromaDB 时设置
client = chromadb.PersistentClient(
    path="./mempalace_data/chroma",
    settings=Settings(
        chroma_db_impl="duckdb+parquet",  # 用 DuckDB 做元数据存储
        persist_directory="./mempalace_data/chroma",
        allow_reset=True
    )
)

优化三:定期清理过期记忆

# 每周运行一次
uv run mempalace maintenance --deprecate-old --merge-similar --optimize-index

这会:

  1. 标记超过 6 个月未访问且已过期的记忆为「归档」
  2. 合并内容高度相似的 Room
  3. 重建向量索引(优化检索速度)

11. 与其他记忆系统对比:MemPalace vs mem0 vs OpenClaw

11.1 功能对比矩阵

功能MemPalacemem0ZepOpenClaw Memory
本地运行
零 API 成本✅(可选)
原始逐字存储❌(摘要)❌(摘要)❌(全量但非结构化)
结构化记忆组织✅(Wing/Hall/Room)❌(扁平)❌(扁平)❌(扁平)
四层记忆栈
MCP 支持
矛盾检测
时间有效性
LongMemEval R@596.6%78.4%82.1%~75%(估算)

11.2 设计哲学对比

MemPalace:「存一切,让结构化和检索去解决怎么找回来」

  • 优点:不丢失细节,检索精度高
  • 缺点:存储开销相对大(但仍然很小),需要本地计算资源

mem0:「智能筛选重要信息存储」

  • 优点:存储效率高,只存「重要」的
  • 缺点:「重要性」判断不可靠,可能丢失关键细节

Zep:「云端托管的记忆服务」

  • 优点:无需本地部署,开箱即用
  • 缺点:隐私风险,持续成本,依赖网络

OpenClaw Memory:「全量上下文管理」

  • 优点:简单直接,不挑食
  • 缺点:缺乏结构化组织,检索效率低

11.3 选型建议

选 MemPalace,如果你

  • 重视隐私(不想让对话数据离开本地)
  • 需要最高的记忆召回率
  • 愿意投入少量本地计算资源
  • 做长期项目,需要跨数月的记忆管理

选 mem0,如果你

  • 需要快速上手,不想折腾部署
  • 对记忆召回率要求不是极高
  • 愿意让 AI 决定什么该记住

选 Zep,如果你

  • 不想自己维护基础设施
  • 愿意为托管服务付费
  • 不处理敏感数据

12. 源码解析:关键模块与扩展点

12.1 项目结构

mempalace/
├── mempalace/
│   ├── __init__.py
│   ├── core/
│   │   ├── palace.py          # Palace/Wing/Hall/Room 数据模型
│   │   ├── storage.py         # ChromaDB 存储后端
│   │   ├── retrieval.py       # 检索逻辑(层级+向量混合)
│   │   ├── compression.py     # AAAK 压缩算法
│   │   ├── contradiction.py   # 矛盾检测
│   │   └── knowledge_graph.py # 时间有效性知识图谱
│   ├── mcp/
│   │   ├── server.py          # MCP Server 实现
│   │   └── tools.py           # MCP 工具定义
│   ├── integrations/
│   │   ├── claude_code.py     # Claude Code 集成
│   │   ├── cursor.py          # Cursor 集成
│   │   └── openclaw.py        # OpenClaw 集成
│   └── cli.py                 # 命令行入口
├── tests/
└── README.md

12.2 关键模块源码解读

palace.py:数据模型核心

@dataclass
class Room:
    name: str
    hall: "Hall"  # 反向引用
    closet: str  # 200 tokens 摘要
    drawer: str  # 完整原始对话
    tags: List[str]
    created_at: datetime
    last_accessed: datetime
    access_count: int
    metadata: Dict[str, Any]
    
    def to_vector_doc(self) -> Dict:
        """
        转换为 ChromaDB 向量文档格式
        """
        return {
            "id": self._generate_id(),
            "document": self.drawer,  # 用完整原文做向量化
            "metadata": {
                "wing": self.hall.wing.name,
                "hall": self.hall.name,
                "room": self.name,
                "tags": ",".join(self.tags),
                "last_accessed": self.last_accessed.isoformat(),
                "access_count": self.access_count,
                # Closet 单独存储,用于 L1 快速加载
                "closet": self.closet
            }
        }
    
    def _generate_id(self) -> str:
        return f"wing-{self.hall.wing.name}_hall-{self.hall.name}_room-{slugify(self.name)}"

retrieval.py:混合检索

class HybridRetriever:
    def __init__(self, palace: Palace, chroma_collection):
        self.palace = palace
        self.collection = chroma_collection
        self.embed_model = SentenceTransformer(os.getenv("EMBED_MODEL", "all-MiniLM-L6-v2"))
    
    def retrieve(self, query: str, n_results: int = 5, use_hybrid: bool = True) -> List[Room]:
        if use_hybrid:
            return self._hybrid_retrieve(query, n_results)
        else:
            return self._vector_retrieve(query, n_results)
    
    def _hybrid_retrieve(self, query: str, n_results: int) -> List[Room]:
        """
        层级检索 + 向量检索混合
        """
        # Step 1: 用 L0 索引确定候选 Wing/Hall
        candidate_rooms = self._navigate_hierarchy(query)
        
        # Step 2: 在候选 Room 中做向量检索
        query_embedding = self.embed_model.encode(query)
        
        results = []
        for room in candidate_rooms:
            room_embedding = self.embed_model.encode(room.closet)
            similarity = cosine_similarity(query_embedding, room_embedding)
            results.append((room, similarity))
        
        # Step 3: 排序并返回 top-N
        results.sort(key=lambda x: x[1], reverse=True)
        return [r[0] for r in results[:n_results]]
    
    def _navigate_hierarchy(self, query: str) -> List[Room]:
        """
        根据查询,导航到相关的 Wing/Hall
        """
        candidates = []
        query_lower = query.lower()
        
        for wing in self.palace.wings:
            # 如果查询包含 Wing 名称,优先加入
            if wing.name.lower() in query_lower:
                candidates.extend(self._all_rooms_in_wing(wing))
            else:
                # 否则检查 Hall 级别
                for hall in wing.halls:
                    if hall.name.lower() in query_lower or any(tag in query_lower for tag in hall.tags):
                        candidates.extend(hall.rooms)
        
        return candidates
    
    def _vector_retrieve(self, query: str, n_results: int) -> List[Room]:
        """
        纯向量检索(回退方案)
        """
        results = self.collection.query(
            query_texts=[query],
            n_results=n_results
        )
        
        # 把 ChromaDB 结果转回 Room 对象
        rooms = []
        for doc_id in results["ids"][0]:
            room = self._load_room_by_id(doc_id)
            rooms.append(room)
        
        return rooms

12.3 扩展点:如何自定义记忆组织规则

MemPalace 支持自定义 Hall 类型和 Room 分配逻辑。

示例:为「代码审查」场景扩展

# custom_halls.py
from mempalace.core import Hall, Room

class CodeReviewHall(Hall):
    """
    自定义的「代码审查」Hall,自动从对话中提取代码片段并单独存储
    """
    def __init__(self, wing):
        super().__init__(name="code_reviews", wing=wing)
    
    def _extract_code_snippets(self, conversation: str) -> List[str]:
        """
        从对话中提取代码块
        """
        import re
        pattern = r"```(\w+)\n(.*?)```"
        matches = re.findall(pattern, conversation, re.DOTALL)
        return [m[1] for m in matches]
    
    def add_conversation(self, conversation: str):
        # 创建主 Room(完整对话)
        main_room = Room(
            name=f"Code review {datetime.now().isoformat()}",
            closet=summarize(conversation),
            drawer=conversation,
            tags=["code-review"]
        )
        
        # 提取代码片段,创建子 Room
        snippets = self._extract_code_snippets(conversation)
        for i, snippet in enumerate(snippets):
            snippet_room = Room(
                name=f"Snippet {i+1} from {main_room.name}",
                closet=snippet[:200],  # 代码片段前 200 字符作为摘要
                drawer=snippet,
                tags=["code-snippet", extract_language(snippet)]
            )
            main_room.add_child(snippet_room)
        
        self.rooms.append(main_room)

然后在初始化时注册:

from mempalace.core import Palace
from custom_halls import CodeReviewHall

palace = Palace(name="MyProject")
wing = palace.create_wing(name="Project-A", type="project")
wing.add_hall(CodeReviewHall(wing))  # 替换默认的 Hall

13. 未来展望:AI 记忆系统的下一步在哪里

13.1 当前痛点

虽然 MemPalace 在 LongMemEval 上取得了 96.6% 的好成绩,但仍有改进空间:

痛点一:嵌入模型质量

all-MiniLM-L6-v2 只有 384 维,对于高度技术性的对话(比如涉及多层嵌套的泛型类型讨论),语义捕获能力有限。

可能的改进:用领域微调的嵌入模型。比如用 GitHub 代码数据微调一个「代码-对话」联合嵌入模型。

痛点二:矛盾检测的误报

当前的矛盾检测有时会把「视角不同」误判为「矛盾」。

比如:

  • 记忆A:「PostgreSQL 的 JSONB 查询很快」
  • 记忆B:「PostgreSQL 的 JSONB 索引占用磁盘很大」

这两条并不矛盾——它们是关于 JSONB 的不同维度的讨论。但当前的 LLM-based 矛盾检测可能会误报。

可能的改进:引入知识图谱推理,而不是简单的 LLM 判断。

痛点三:跨语言检索

如果你的对话是中文的,但嵌入模型主要是在英文数据上训练的,检索精度会下降。

可能的改进:支持多语言嵌入模型(如 paraphrase-multilingual-MiniLM-L12-v2)。

13.2 MemPalace 路线图(根据 ROADMAP.md)

根据项目 ROADMAP.md,2026 年下半年的重点方向:

  1. 多模态记忆:支持存储和检索图片、图表(比如架构图、流程图)
  2. 协作记忆:多个用户共享同一个 Palace(适合团队协作)
  3. 记忆导出/导入:支持在不同 AI 工具间迁移记忆(比如从 Claude Code 迁移到 Cursor)
  4. Web UI:可视化的记忆宫殿管理界面(类似 Obsidian 的图谱视图)
  5. Agent 自动整理:定期让 AI Agent 自动整理记忆(合并相似 Room、创建 Tunnel、更新知识图谱)

13.3 更长远的问题:记忆的「所有权」

当 AI 有了长期记忆,一个哲学问题就出现了:

这些记忆是谁的?

如果你用 Claude Code + MemPalace 工作了一年,积累了几千条技术讨论记忆。然后你换到了 Cursor——这些记忆能带走吗?

MemPalace 的答案(目前)是:能。

因为所有数据都在本地,格式是开放的(ChromaDB + JSON),你可以导出成标准格式,然后导入到任何支持 MemPalace 格式的工具。

但这需要行业标准。希望 MCP 协议能扩展到「记忆迁移」场景,让记忆真正成为你的资产,而不是被锁定在某个工具里。


14. 总结:为什么 MemPalace 代表了 AI 记忆的新范式

14.1 核心贡献

MemPalace 的三个核心创新:

  1. 原始逐字存储(Raw Verbatim Storage):挑战了「AI 应该筛选重要信息存储」的共识,证明「全存+结构化检索」比「筛选+存储」更有效
  2. 记忆宫殿隐喻的数据模型:把人类的空间记忆原理引入 AI 系统设计,让记忆组织更符合直觉
  3. 四层记忆栈:巧妙解决了「记忆越多,检索越慢」的问题——通过分层,让 AI 能快速定位,按需深入

14.2 适用场景

强烈推荐

  • 长期技术项目(3 个月以上)
  • 需要频繁回溯早期决策的场景
  • 重视隐私的团队(本地运行,零数据离开机器)
  • 使用 Claude Code / Cursor 的开发者

不推荐

  • 短期项目(用不了多久就结束了,没必要搭建记忆系统)
  • 对本地计算资源极其敏感的场景(嵌入模型推理需要 CPU/GPU)
  • 已经用了云端记忆系统且满意的团队(迁移有成本)

14.3 实践建议

如果你决定试试 MemPalace,以下建议能让你少走弯路:

建议一:从第一天就开启

记忆系统的价值是随时间积累的。如果你等「项目稳定了」再开启,就丢失了早期的讨论上下文。

建议二:定期回顾记忆宫殿

每周花 10 分钟,用 mempalace list 看看记忆宫殿里有什么。如果发现错误记忆,及时纠正。

建议三:自定义 Hall 类型

默认的 Hall 类型可能不完全符合你的工作流。别怕改——MemPalace 的数据模型是开放的。

建议四:关注矛盾检测通知

如果 MemPalace 提示「检测到记忆矛盾」,认真对待。这可能意味着你的项目方向变了,但 AI 还不知道。

14.4 最后的话

MemPalace 最有意思的地方不是技术,而是它的起源:《生化危机》女主角 Milla Jovovich 参与了开发。

这件事本身就是一个信号:AI 工具正在变得足够简单,让非工程师也能参与设计和开发

2026 年,AI 辅助开发已经不是「工程师用 AI 写代码」——而是「任何有想法的人,都能和 AI 一起把想法变成产品」。

MemPalace 就是这个过程的一个完美样本。


参考资源

  • MemPalace GitHub:https://github.com/MemPalace/mempalace
  • LongMemEval 论文:[arXiv链接]
  • ChromaDB 官方文档:https://docs.trychroma.com/
  • MCP 协议规范:https://modelcontextprotocol.io/
  • 记忆宫殿法(Method of Loci):维基百科

本文撰写于 2026 年 6 月,基于 MemPalace v3.0.14。如有技术细节更新,请以官方文档为准。

如果你觉得这篇文章有价值,欢迎分享给更多开发者。AI 记忆系统是 2026 年最值得关注的底层技术之一。

推荐文章

在JavaScript中实现队列
2024-11-19 01:38:36 +0800 CST
Python 基于 SSE 实现流式模式
2025-02-16 17:21:01 +0800 CST
宝塔面板 Nginx 服务管理命令
2024-11-18 17:26:26 +0800 CST
Redis函数在PHP中的使用方法
2024-11-19 04:42:21 +0800 CST
Vue3中怎样处理组件引用?
2024-11-18 23:17:15 +0800 CST
Vue 3 路由守卫详解与实战
2024-11-17 04:39:17 +0800 CST
Roop是一款免费开源的AI换脸工具
2024-11-19 08:31:01 +0800 CST
程序员茄子在线接单