读 MemOS 源码:记忆的三态、相变,与一个诚实的占位符

把 MemTensor 的 MemOS(arXiv:2507.03724)克隆下来,对照论文精读约 9 万行 Python 源码。它的精髓可以压成一张相图:记忆有三态——明文(气态)、激活(液态)、参数(固态),'记忆操作系统'的职责是调度相变。审计结果:明文态是生产级,明文→激活这条相变真实且值钱(TTFT 最高降 94.2%),而参数态在源码里是一个 dump() 时写入 b"Placeholder" 的占位符。这篇按'表示—调度—相变—进化—数字'五层,把论文的承诺和代码的现实逐条对齐。

src/memos/memories/parametric/lora.py 是 MemOS 仓库里最短的核心文件之一。文件头写着”本文件目前是占位符,请勿当作功能模块使用”;它的 dump() 方法认真地创建目录、拼好路径,然后往文件里写入 11 个字节:b"Placeholder"。而在论文摘要里,这个模块对应的是三大支柱之一——“统一明文、激活、参数三种记忆的表示、调度与演化”。一边是论文最宏大的承诺,一边是源码最诚实的一行。这篇文章就在这两者之间做审计。

MemOS 是 MemTensor(记忆张量,上海)开源的”LLM 记忆操作系统”(MemTensor/MemOS,Apache-2.0),配套论文《MemOS: A Memory OS for AI System》(arXiv:2507.03724,39 位作者,v4 更新于 2025 年 12 月)。我克隆了仓库(commit 93e4082,2026-08-03,主版本号 2.0 “Stardust”),源码约 381 个 Python 文件、9 万行,对照论文逐个子系统读完。结论可以压成一句话:

MemOS 的精髓不是”给 LLM 加个记忆库”,而是一张相图:记忆有三种形态——明文(可编辑、可流动,像气态)、激活(KV 张量、可直接注入注意力,像液态)、参数(凝固进权重,像固态)——而”操作系统”的全部职责,是给每条记忆挂上生命周期元数据,然后调度它们在三态之间发生相变。源码审计的结果是:气态子系统是生产级的,气→液这条相变是真实且值钱的,固态今天还是一个占位符。

换句话说,论文卖的是完整的三态相图,代码交付的是”二态半”。但先别急着扣分——那条已经落地的相变,恰好是整张图里工程价值最高的一条。下面分五层拆。

一、同一个 OS 隐喻,在记忆栈上被下注了三次

“用操作系统的思想管理 LLM 的记忆”不是 MemOS 的发明。往下数,这个隐喻在记忆栈的三个层面各被下注过一次:

层面项目借用的 OS 机制管理的对象
显存层vLLM PagedAttention(SOSP ‘23虚拟内存分页一次推理内的 KV block
上下文层MemGPT(arXiv:2310.08560内存分层 + 中断一个 Agent 的上下文窗口
知识生命周期层MemOS(arXiv:2507.03724资源调度 + 生命周期 + 权限治理跨用户、跨会话、跨模型的知识资产

三次下注的抽象层级逐层上移:vLLM 管的是”这 16 个 token 的 KV 放在哪块显存页”,MemGPT 管的是”哪段历史该换进上下文窗口”,MemOS 管的是”这条知识由谁产生、谁可读、何时过期、值不值得固化”。层级越高,隐喻越松,工程越难验证——这也是为什么这篇文章必须去读源码。

MemOS 还有一个直系的理论前身:同一批作者(IAAR 上海 + MemTensor,Memory³ 一作 Hongkang Yang 也在 MemOS 作者列表里)2024 年的 Memory³(arXiv:2407.01178)。那篇论文证明了一件事:在”参数记忆”和”外部检索”之间插入一层显式记忆(explicit memory,从模型激活里抽出的稀疏 KV 对),可以让一个 2.4B 的模型打过更大的模型,因为大量具体知识不必烧进参数、也不必每次推理都重新编码。Memory³ 给出了记忆分层的成本理论,MemOS 则把这个分层做成了系统——论文摘要开头引用的文献 [1] 正是它。

顺带一提:MemTensor 自己在 2026 年发布了这条路线的”反面”——把记忆做进前向计算的原生记忆基础模型 Metis(我在另一篇里拆过)。同一个团队同时押注”外挂 OS”和”原生内化”两条路线,这本身就是对”记忆问题还没有收敛答案”的最好注脚。

二、MemCube:表示层的赌注

拆任何 AI 系统,第一问都是知识被编码成了什么表示。MemOS 的回答是 MemCube:一个”记忆内容 + 三组元数据”的统一封装,论文把元数据分为三类——

  • 描述性标识:时间戳、来源签名(推理提取 / 用户输入 / 外部检索 / 参数微调)、语义类型;
  • 治理属性:访问控制、生命周期策略(TTL / 衰减规则)、优先级、合规与溯源(敏感度标签、水印、审计日志);
  • 行为使用指标:访问频率与新近度、上下文指纹、版本链。

这组设计的赌注是:记忆的价值不在内容本身,而在挂在内容上的管理性元数据。RAG 里一段被检索的文本是”死”的——没有出处、没有版本、没有权限、没有过期时间;MemCube 里同一段文本是一个有身份证、有户口、有信用记录的资产。论文说 RAG “本质上是无状态的即取即用拼接,而非有生命周期的记忆管理”,这句批评的分量全押在元数据上。

源码里这个赌注落在两处。第一处是 GeneralMemCubesrc/memos/mem_cube/general.py):一个 Cube 有四个插槽——text_memact_mempara_mempref_mem,每个插槽都可以配置为 "uninitialized" 跳过初始化,工厂模式按配置组装:

# src/memos/mem_cube/general.py(节选)
self._text_mem = (
    MemoryFactory.from_config(config.text_mem)
    if config.text_mem.backend != "uninitialized" else None
)
# act_mem / para_mem / pref_mem 同构

第二处、也是更能体现设计密度的一处,是明文记忆条目的 schema(src/memos/memories/textual/item.py)。每条记忆有四值生命周期状态(item.py:109):

status: Literal["activated", "resolving", "archived", "deleted"]

十种记忆类型(item.py:178-189):WorkingMemoryLongTermMemoryUserMemoryOuterMemoryToolSchemaMemoryToolTrajectoryMemoryRawFileMemorySkillMemoryPreferenceMemoryContext——注意工具 schema、工具调用轨迹和技能都被建模成了记忆类型,这是”一切皆记忆”的表示统一。以及一个非破坏性的版本链:记忆因冲突或重复被更新时,旧内容会被双份保留——轻量版进本节点的 history 字段(ArchivedTextualMemory,记录版本号、更新类型 conflict/duplicate/extract/unrelated/feedback 和时间),全量版(含 embedding 和来源)另存为独立节点、由 archived_memory_id 引用(item.py:49-58 的类文档写得非常清楚)。

一句话:表示层的完成度很高,论文吹的元数据在 schema 里基本都能找到对应字段。问题出在下一层——三个插槽的实现深度天差地别。

三、审计三态:一个”二态半”系统

记忆形态源码位置实现完成度
明文(气态)src/memos/memories/textual/naive(内存+JSON)、general(Qdrant 向量库)、tree(Neo4j 图数据库)三种后端生产级
激活(液态)src/memos/memories/activation/KVCacheMemory(transformers DynamicCache + pickle 持久化)、VLLMKVCacheMemory(vLLM 服务端预热)两种后端真实可用
参数(固态)src/memos/memories/parametric/LoRAMemoryload() 空实现,dump() 写入 b"Placeholder"占位符

明文态里最重的实现是 TreeTextMemory:记忆存成 Neo4j 图(节点带 embedding,边有 PARENTRELATES_TO 等类型),并按论文的记忆层级划出三个带容量上限的分区(tree_text_memory/organize/manager.py:74-80):

self.memory_size = {
    "WorkingMemory": 20,        # 工作记忆:当前会话的活跃集
    "LongTermMemory": 1500,     # 长期记忆
    "RawFileMemory": 1500,
    "UserMemory": 480,          # 用户画像/偏好
}

20 / 1500 / 480——这三个数字就是这个”操作系统”的物理内存布局:工作记忆是寄存器堆,容量极小、被高频替换;长期记忆和用户记忆是主存,靠相似度阈值(0.80 判重复、0.92 判可合并,同文件 61-62 行)做去重与冲突管理。

激活态是真东西。KVCacheMemory 用 transformers 的 DynamicCache 存真实的 KV 张量,from_textual_memory()activation/kv.py:130)一行调用 self.llm.build_kv_cache(mem.memory)llms/hf.py:354)把一条明文记忆预编码成 KV;多条缓存合并时逐层做单次 torch.cat_concat_cacheskv.py:200),而不是逐张量拼接——这是留过心的性能写法。

参数态就是导语里那个占位符。论文里”稳定知识蒸馏进参数模块”、“冷参数卸载为明文”这些说法,在代码里只有配置类的契约(allowed_backends 里注册了 "lora"),没有任何与 LoRA adapter 或权重注入的实际集成。

灰度判断:这不算欺骗,算路线图超前于实现——开源项目常态。但读者应该知道:今天引用 MemOS 时,“统一三种记忆”描述的是它的类型系统,不是它的运行时能力。运行时能力是二态半:成熟的明文态、可用的激活态、加上明文态里那套确实在运转的生命周期管理(算半态)。

四、MemScheduler:真正配得上”操作系统”四个字的部分

如果只允许保留 MemOS 的一个子系统来支撑”OS”这个词,我会选 mem_scheduler。它是一套消息驱动的异步调度器,架构上和论文的对应关系最扎实。

主链路是这样闭合的:用户调 MOS.chat(query),主线程同步做检索、拼 prompt、生成回答;同时把一条打着 QUERY_TASK_LABEL 的消息塞进调度器的队列(mem_os/core.py:286,队列支持本地内存和 Redis 两种后端)。调度器的 dispatcher(默认 30 个工作线程,按 (user_id, mem_cube_id) 分组隔离,task_schedule_modules/dispatcher.py:53)把消息路由给对应 handler,QueryMessageHandler 处理完立刻再发一条 MEM_UPDATE_TASK_LABEL 消息(handlers/query_handler.py:54),触发下一阶段——工作记忆重排。

graph LR
  U["用户 query"] --> C["MOS.chat()<br/>同步:检索 + 生成"]
  C -.->|"QUERY 消息"| Q["消息队列<br/>(本地 / Redis)"]
  Q --> H1["QueryHandler"]
  H1 -.->|"MEM_UPDATE 消息"| Q
  Q --> H2["MemoryUpdateHandler"]
  H2 --> R["replace_working_memory()<br/>重排 + 过滤 + 原子替换"]
  H2 --> A["ActivationMemoryManager<br/>KV 预热"]

replace_working_memorymem_scheduler/base_mixins/memory_ops.py)的五个步骤,是全仓库最像操作系统教科书的一段:过滤快速模式的临时记忆(清脏页)→ 按查询历史重排新旧候选(LRU + 相关性评分)→ 过滤与近期查询无关的条目(智能淘汰)→ 把结果转成监控项(更新页表元数据)→ 原子替换那个容量为 20 的工作记忆分区(换页落盘,最终调到 textual/tree.py:116)。配套的 monitor 为每条工作记忆维护 keywords_scoresorting_scorerecording_count 三个指标——对应页表里的访问位与引用计数。

拿它和 MemGPT 对照会看到一个有意思的分歧。MemGPT 的赌注是模型自治:LLM 自己就是内核,通过 function call 决定什么内容换进换出上下文。MemOS 的实现恰恰相反,是系统代管:换页决策由外部的启发式规则 + 专用 prompt 完成,模型只消费调度结果。吊诡的是,MemOS 论文自己预言”记忆管理将从人类定义走向模型定义(model-defined)“——按这个标准,它今天的调度器还站在自己预言的对立面。这不是缺陷,是当下的务实选择:外置调度可审计、可控、不烧主模型的 token;但值得记住,论文的终局愿景和代码的当前形态,方向并不相同。

五、唯一落地的相变:明文 → 激活,TTFT 最高降 94%

论文的 Figure 5 画了好几条记忆形态转化路径:高频明文可以预编码成激活模板;稳定知识可以蒸馏进参数;过时参数可以卸载回明文。源码里真正走通的只有第一条——但这条恰好直接回应了当下 LLM 服务最硬的瓶颈之一:重复 prefill 烧掉的算力和首 token 延迟

机制全链路:MemScheduler 持续监控哪些明文记忆被高频访问且语义稳定 → ActivationMemoryManager 把它们按模板拼成文本(MEMORY_ASSEMBLY_TEMPLATEtemplates/mem_scheduler_prompts.py:626),拼装结果与上次一致就跳过(去重)→ from_textual_memory() 预编码成 DynamicCache 并 pickle 落盘 → 下次推理时作为 past_key_values 直接注入,跳过这段内容的 prefill。

论文 Table 8 给了受控实验数字(HuggingFace transformers,单张 H800 80GB,KV 注入与 prompt 注入输出序列完全一致,即语义等价):

模型上下文查询KV 注入 TTFT直接 prompt TTFT降幅
Qwen3-8B6064 tok167 tok0.12 s2.04 s94.2%
Qwen2.5-72B6064 tok167 tok0.15 s1.79 s91.4%
Qwen3-8B583 tok952 tok0.39 s0.51 s23.0%

规律很清楚:上下文越长、查询越短,收益越大——因为省掉的正是”重复编码长上下文”这一段;而把明文转成 KV 的一次性 Build 成本(72B 上约 1.3 秒)会被多次复用摊薄。

一个持怀疑态度的读者会问:这不就是 prefix caching 吗?vLLM 的 APC 早就做了(我拆过它的源码)。答案是:机制同源,管理层级不同。APC 的缓存是推理引擎进程内的、匿名的、以块哈希为键的副产品,进程重启即失效,也无法指定”这段必须缓存”;MemOS 把同一份 KV 变成可命名、可落盘、可跨会话调度、由访问频率驱动预热的一等资产。用本文的相图说:vLLM 管的是液态的物理存放,MemOS 管的是什么时候把气态变成液态。这正是”记忆操作系统”比”推理优化”高一层的地方——也是三次 OS 隐喻里,MemOS 与 vLLM 真正接壤的一点。

六、“Self-evolving”审计:反馈闭环是真的,“做梦”还没醒

README 的自我介绍是 “self-evolving memory OS”。源码里的”自进化”分两块,成色不同。

成色足的:反馈驱动的版本演化。mem_feedback 实现了完整闭环:用户用自然语言纠正 → LLM 判断反馈类型(新增/更新/冲突/重复/无关)→ 检索受影响的旧记忆 → 逐条对比、检测矛盾 → 执行更新,旧版本按第二节说的双份归档机制保留。加上入库时的自动判重(0.80 阈值)与合并(0.92 阈值),记忆库确实在”被使用中变好”,且每一步可回溯。

成色欠的:Dream 模块。src/memos/dream/ 是一个”做梦”插件:把待处理记忆聚类成 Context 节点,用一段相当有文学性的 prompt(“Dream 的存在是为了做白天的 AI 做不到的事:解决用户未解决的问题……深度思考而非总结”)驱动 LLM 做离线反刍,产出洞察和假设性问题。但它默认关闭(要设 MEMOS_ENABLED_PLUGINS=dream)、触发信号只存进程内存(重启即丢)、洞察不写回记忆库——没有自动合并、归档或遗忘调度。换句话说,今天的 MemOS 是”反馈驱动的演进”(用户在环),不是”自主的记忆重组”(系统自治)。论文结论里的 “Self-Evolving MemBlocks” 被诚实地列在 future work,代码与之一致。

从反馈闭环这条原理看,这个排序是对的:先做可审计的用户在环演化,再做无监督的自主重组——后者一旦出错,污染的是整个记忆库。

七、数字的诚实读法

论文的主评测(全部 baseline 统一用 GPT-4o-mini 做底座)值得原样端上来,包括不利的部分。

LoCoMo(Table 3,LLM-Judge 分数):

方法单跳多跳时序推理开放域总分F1
Mem073.3358.7552.3445.8364.5743.46
Zep66.2352.1254.8233.3359.2241.23
Memobase73.1264.6581.2053.1272.0150.18
MemOS-103181.0967.4975.1855.9075.8045.27

MemOS 总分第一,单跳、多跳、开放域三项领先;但时序推理和 F1 都输给了 Memobase。LongMemEval 上总分 77.8(第二名 Memobase 72.4),其中”单会话偏好”一项 96.7 分、断层领先(次优 Zep 90 分档之下);PersonaMem 精度 61.2(次优 Memobase 58.9);PreFEval 个性化响应率 77.2%(裸 LLM 只有 9.6%),且”无视偏好”错误率 4.6% 全场最低。工程侧还有一组容易被忽略的数字:40 QPS 压力下增删查全部 100% 成功、检索平均延迟 613.8 ms,100 QPS 下仍是 100%——同表里 Mem0 在 40 QPS 时 add 成功率掉到 41.7%,MemU 掉到 7.5%。对生产系统,这张鲁棒性表可能比 LoCoMo 那张更该看。

两点保留:其一,评测的是 “MemOS-1031” 版本,README 上 2026 年 7 月已自报 LoCoMo 88.83、LongMemEval 89.20 的新数字,但那是未经同行评审的自报成绩;其二,所有记忆系统评测共享一个方法论弱点——benchmark 是静态对话回放,而记忆系统的真实价值在数月尺度的活跃使用里,这一点目前没有任何 benchmark 能覆盖。

八、什么时候用它,什么时候别用

  • 适合:多用户/多租户、需要审计与权限治理、要把多个知识库当作可组合资产管理(MemCube 挂载/共享)、有高频稳定的长上下文可吃 KV 预热红利、自托管(数据不出域)。
  • 不适合:只需要”记住用户偏好”的单体应用——为此搭 Neo4j + Redis + 30 线程调度器是杀鸡用牛刀,Mem0 一类轻量方案或者一个带 metadata 的向量库就够了;以及期待”装上就自进化”的场景——第六节说了,自治重组还不存在。
  • 中间地带:如果你已经在用 vLLM 的 prefix caching 且命中率满意,MemOS 激活态的增量收益主要在”跨会话持久化 + 按访问频率主动预热”,值不值得引入一整套系统,取决于你的会话生命周期有多长。

九、带走的模型:判断任何记忆系统的三问

这篇的可复用结论,是一套适用于所有”LLM 记忆”产品的审计框架——正好对应 MemOS 的三层精髓:

  1. 表示之问:一条记忆是什么数据结构?有没有出处、版本、权限、过期时间这些管理性字段?没有元数据的记忆库只是换了名字的向量检索。
  2. 调度之问:谁、依据什么信号,决定哪些记忆占据上下文窗口和 KV cache?是每次全量检索(无调度),还是有访问频率、新近度驱动的换入换出?
  3. 相变之问:系统支持记忆在明文、KV、参数三态之间的哪几条转化路径?每多一条真实落地的路径,它就离”数据库”更远、离”操作系统”更近一步。

拿这三问去量 MemOS 自己:表示层答卷完整,调度层有真实的消息驱动内核,相变层落地了一条、许诺了三条。这个成绩在今天的开源记忆系统里已是第一梯队——而它许诺未兑现的部分(Mem-training:让知识以显式记忆单元的形态持续演化,作为预训练/后训练之后的第三个训练阶段,参见其论文 Figure 4)如果兑现,那才是把”OS”这个词从隐喻变成事实的时刻。在那之前,MemOS 最准确的描述不是记忆操作系统,而是:一个带调度器的、可治理的记忆资产管理系统,附赠一条真正省钱的 KV 相变通道

参考来源

工程实践:

arXiv 论文: