Cortex Memory 是一个基于Rust构建的高性能AI记忆框架,为AI智能体提供持久化、智能化的长期记忆能力。它采用独特的三层记忆架构(L0抽象 → L1概览 → L2细节),结合向量语义检索和智能知识提取,让AI真正"记住"用户。
⭐ GitHub : https://github.com/sopaco/cortex-mem 
本篇主题:深入剖析三层记忆架构(L0/L1/L2)的设计理念,探讨分层检索的精妙之处,以及"由粗到精"的检索策略如何平衡性能与精度。
好的架构不是设计出来的,而是从问题中生长出来的。
从一个检索问题说起
在设计Cortex Memory的早期,我们遇到了一个看似简单的问题:
"当用户问'上次讨论的那个微服务方案',系统应该如何找到相关记忆?"
最直观的方案是:把所有对话内容向量化,做语义搜索,返回最相关的几条。这看起来没问题,但实际效果却很糟糕。
问题出在哪?
精度与召回的矛盾。如果只搜索原始对话(L2细节层),相似度计算会被大量噪音稀释------一段关于微服务架构讨论的对话,可能90%的内容是闲聊,真正有价值的只有10%。如果阈值设高了,召回率低;设低了,噪音太多。
性能与成本的矛盾。完整对话的向量检索,意味着每次查询都要处理大量tokens。随着对话积累,这个开销会线性增长。
我们需要一个不同的思路。
图书馆的启示
有一天我在图书馆找一本书,突然意识到这个过程蕴含着精妙的架构智慧:
- 我不会一上来就去翻每本书的全文------那太慢了
- 我会先看分类目录,快速定位到相关区域
- 然后看几本书的目录或摘要,进一步缩小范围
- 最后才翻开具体的书页,确认是否是我要找的
这不是简单的"缩小范围",而是一个分层精炼的过程。每一层都有特定的信息密度和检索目的。
这个洞察成为了三层记忆架构的起点。
L0抽象层:快速定位的"书脊"
第一层,我们称之为L0抽象层(Abstract Layer)。
它的任务很简单:用最精炼的语言描述"这段对话大致是什么"。
L0 抽象层
原始对话
LLM提取
用户: 最近我们在做微服务拆分...
助手: 这是一个很好的方向...
用户: 但是团队担心一致性...
... (持续2小时对话)
讨论了微服务架构迁移的技术方案,
涉及服务拆分策略和分布式事务处理
L0层有几个关键特征:
- 信息密度极高:通常只有100-200 tokens,相当于一句话或一小段话
- 语义完整:虽然简短,但保留了对话的核心主题和意图
- 生成成本低:LLM一次调用即可生成,且token消耗很少
在检索时,L0层的作用是"快速筛选"。系统首先在L0层搜索,排除掉明显不相关的记忆,只保留候选集进入下一层。
这就好比在图书馆,你不会去翻每本书,而是先看书脊上的标题------几秒钟就能判断这本书是否相关。
L1概览层:上下文确认的"目录"
通过L0筛选后,候选集可能还有几十条记忆。这时候需要更详细的信息来判断相关性。
L1概览层(Overview Layer)就是为此设计的。
L1 概览层结构
对话概览 核心主题: 微服务架构迁移
关键决策: 采用事件溯源模式处理分布式事务
涉及实体: 订单服务, 支付服务, 消息队列
时间节点: 2024年Q3技术规划
L1层是一个结构化的摘要,包含:
- 核心主题:对话讨论的主要议题
- 关键决策:对话中达成的结论或决定
- 涉及实体:提及的人物、系统、产品等
- 时间节点:相关的时间信息
这些信息不是简单的关键词提取,而是LLM对对话内容的"理解"和"组织"。它提供了一个中粒度的上下文视图,足以让你判断这段记忆是否真的相关。
在检索流程中,L1层承担"上下文确认"的角色:
L2细节层 L1概览层 L0抽象层 查询 L2细节层 L1概览层 L0抽象层 查询 第一轮筛选完成 上下文确认完成 语义搜索 返回Top50候选 对候选进行二次搜索 返回Top10候选 精确匹配 返回最终结果
L2细节层:精确匹配的"正文"
当你确认某条记忆相关后,可能还需要查看原始对话的具体内容。
这就是L2细节层(Detail Layer)------完整的对话记录,未经压缩和抽象。
L2层有几个独特的设计考量:
存储格式。我们选择Markdown格式存储,而非JSON或二进制。原因很简单:Markdown是人类可读的,便于调试、审计和版本控制。当系统出问题时,你可以直接打开文件看到原始对话。
延迟加载。L2内容不会在每次检索时都加载。只有当L0和L1都确认相关后,系统才会去读取完整的L2内容。这大大减少了I/O开销。
哈希去重。相同内容的对话会产生相同的向量ID,避免重复索引。这在用户反复讨论同一话题时特别有用。
三层的协同检索
单个层次的设计只是故事的一半。三层如何协同工作,才是架构的核心。
我们设计了一个加权评分机制:
最终得分 = 0.2 × L0相似度 + 0.3 × L1相似度 + 0.5 × L2相似度
这个权重分配反映了各层的"可信度":
- L0权重最低(20%):因为它信息量最少,只能排除明显不相关的,不能确认相关性
- L1权重适中(30%):提供了足够的结构化上下文,但仍然有信息损失
- L2权重最高(50%):原始内容是最可靠的判断依据
加权协同检索
查询: 上次讨论的微服务方案
L0搜索结果
得分: 0.85
L1搜索结果
得分: 0.78
L2搜索结果
得分: 0.92
加权计算
0.2×0.85 + 0.3×0.78 + 0.5×0.92
= 0.851
最终得分: 0.851
排名: 第1位
实际效果如何?在我们的基准测试中,使用三层加权检索相比单层检索:
- Recall@1: 从67%提升到93%
- MRR(平均倒数排名): 从52%提升到93%
- 查询延迟: 降低约40%(得益于L0的快速筛选)
按需生成的智慧
三层架构还带来了一个意外的收益:存储成本的可控性。
最初我们担心,三层记忆会让存储需求膨胀三倍。但实际并非如此。
关键在于"按需生成"策略:
- L2层始终存在:这是原始对话,必须存储
- L0/L1层延迟生成:只有在首次检索时才生成,并缓存到文件系统
对话结束
首次检索触发
需要更多上下文
缓存完成
L2Only
L0Generated
L1Generated
大部分记忆
停留在这一层
只有被检索过的记忆
才会生成L0
这意味着,对于那些"存了但从未被查询"的对话,系统不会浪费资源去生成L0和L1。根据实际数据,大约60%的记忆只需要存储L2层。
这是一个典型的"空间换时间"与"时间换空间"的权衡。我们选择在检索时付出一点生成成本,换取存储成本的大幅降低。
层级之间的信息流动
三层不是孤立的,它们之间存在有机的信息流动。
自上而下的精炼:L2 → L1 → L0。每一层都是对下一层的抽象和提炼。LLM在这个过程中扮演"提炼器"的角色,从原始内容中抽取精华。
自下而上的检索:L0 → L1 → L2。检索过程则是反向的,从抽象到具体,逐步确认相关性。
检索收敛流
L0 快速筛选
L1 上下文确认
L2 精确匹配
信息精炼流
L2 细节层
原始对话
L1 概览层
结构化摘要
L0 抽象层
一句话大意
这种双向流动的设计,让系统既能高效存储,又能精准检索。
意图感知的阈值调整
三层架构的最后一块拼图是"意图感知"。
不同的查询意图,对精度的要求不同。比如:
- 查询"用户对Python的态度"------需要较高精度
- 查询"那个什么框架来着"------可以接受较低精度
系统会根据查询的语义特征,自动调整检索阈值:
查询意图检测
实体查询
如'张三'
阈值: 0.4
事实查询
如'什么时间'
阈值: 0.45
关系查询
如'谁和谁'
阈值: 0.45
一般查询
阈值: 0.5
这种动态调整让系统在不同场景下都能有较好的表现,而不是用一个固定阈值"一刀切"。
一个架构决策的背后
写到这里,我想分享一个设计决策背后的思考。
在三层架构确定后,我们面临一个问题:向量存储应该怎么组织?
方案一:每一层使用独立的Collection
方案二:三层共用一个Collection,用metadata区分
最终我们选择了方案二。原因有几个:
- 检索效率:单Collection查询避免了跨Collection的聚合开销
- 维护简单:不需要同步多个Collection的生命周期
- 租户隔离:可以基于Collection命名实现多租户隔离
但这也带来了一个挑战:向量ID的生成必须保证唯一性且可预测。我们采用了"URI + Layer"的组合哈希方案:
VectorID = Hash(Hash(URI) + Layer_Suffix)
这确保了同一记忆的不同层有不同的向量ID,且ID是确定性的------相同输入永远产生相同输出。
从架构到实践
三层记忆架构不是一蹴而就的。它是从实际问题出发,经过多次迭代才形成的。
早期的版本只有L2层,检索效果不佳。加入L0后,速度提升了,但精度反而下降------因为L0信息太少,容易误判。加入L1后,才找到平衡点。
这个过程中,最大的收获是认识到:架构不是画出来的,而是从问题中生长出来的。
当你面对一个复杂问题时,不要急着画架构图。先问自己:用户真正需要什么?系统真正要解决什么问题?然后让架构自然涌现。
三层记忆架构,就是这样生长出来的。
下一篇文章,我将通过具体案例展示:当AI拥有了真正的记忆能力,能为用户带来怎样的体验升级。