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 “本质上是无状态的即取即用拼接,而非有生命周期的记忆管理”,这句批评的分量全押在元数据上。
源码里这个赌注落在两处。第一处是 GeneralMemCube(src/memos/mem_cube/general.py):一个 Cube 有四个插槽——text_mem、act_mem、para_mem、pref_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):WorkingMemory、LongTermMemory、UserMemory、OuterMemory、ToolSchemaMemory、ToolTrajectoryMemory、RawFileMemory、SkillMemory、PreferenceMemory、Context——注意工具 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/ | LoRAMemory:load() 空实现,dump() 写入 b"Placeholder" | 占位符 |
明文态里最重的实现是 TreeTextMemory:记忆存成 Neo4j 图(节点带 embedding,边有 PARENT、RELATES_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_caches,kv.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_memory(mem_scheduler/base_mixins/memory_ops.py)的五个步骤,是全仓库最像操作系统教科书的一段:过滤快速模式的临时记忆(清脏页)→ 按查询历史重排新旧候选(LRU + 相关性评分)→ 过滤与近期查询无关的条目(智能淘汰)→ 把结果转成监控项(更新页表元数据)→ 原子替换那个容量为 20 的工作记忆分区(换页落盘,最终调到 textual/tree.py:116)。配套的 monitor 为每条工作记忆维护 keywords_score、sorting_score、recording_count 三个指标——对应页表里的访问位与引用计数。
拿它和 MemGPT 对照会看到一个有意思的分歧。MemGPT 的赌注是模型自治:LLM 自己就是内核,通过 function call 决定什么内容换进换出上下文。MemOS 的实现恰恰相反,是系统代管:换页决策由外部的启发式规则 + 专用 prompt 完成,模型只消费调度结果。吊诡的是,MemOS 论文自己预言”记忆管理将从人类定义走向模型定义(model-defined)“——按这个标准,它今天的调度器还站在自己预言的对立面。这不是缺陷,是当下的务实选择:外置调度可审计、可控、不烧主模型的 token;但值得记住,论文的终局愿景和代码的当前形态,方向并不相同。
五、唯一落地的相变:明文 → 激活,TTFT 最高降 94%
论文的 Figure 5 画了好几条记忆形态转化路径:高频明文可以预编码成激活模板;稳定知识可以蒸馏进参数;过时参数可以卸载回明文。源码里真正走通的只有第一条——但这条恰好直接回应了当下 LLM 服务最硬的瓶颈之一:重复 prefill 烧掉的算力和首 token 延迟。
机制全链路:MemScheduler 持续监控哪些明文记忆被高频访问且语义稳定 → ActivationMemoryManager 把它们按模板拼成文本(MEMORY_ASSEMBLY_TEMPLATE,templates/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-8B | 6064 tok | 167 tok | 0.12 s | 2.04 s | 94.2% |
| Qwen2.5-72B | 6064 tok | 167 tok | 0.15 s | 1.79 s | 91.4% |
| Qwen3-8B | 583 tok | 952 tok | 0.39 s | 0.51 s | 23.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 |
|---|---|---|---|---|---|---|
| Mem0 | 73.33 | 58.75 | 52.34 | 45.83 | 64.57 | 43.46 |
| Zep | 66.23 | 52.12 | 54.82 | 33.33 | 59.22 | 41.23 |
| Memobase | 73.12 | 64.65 | 81.20 | 53.12 | 72.01 | 50.18 |
| MemOS-1031 | 81.09 | 67.49 | 75.18 | 55.90 | 75.80 | 45.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 的三层精髓:
- 表示之问:一条记忆是什么数据结构?有没有出处、版本、权限、过期时间这些管理性字段?没有元数据的记忆库只是换了名字的向量检索。
- 调度之问:谁、依据什么信号,决定哪些记忆占据上下文窗口和 KV cache?是每次全量检索(无调度),还是有访问频率、新近度驱动的换入换出?
- 相变之问:系统支持记忆在明文、KV、参数三态之间的哪几条转化路径?每多一条真实落地的路径,它就离”数据库”更远、离”操作系统”更近一步。
拿这三问去量 MemOS 自己:表示层答卷完整,调度层有真实的消息驱动内核,相变层落地了一条、许诺了三条。这个成绩在今天的开源记忆系统里已是第一梯队——而它许诺未兑现的部分(Mem-training:让知识以显式记忆单元的形态持续演化,作为预训练/后训练之后的第三个训练阶段,参见其论文 Figure 4)如果兑现,那才是把”OS”这个词从隐喻变成事实的时刻。在那之前,MemOS 最准确的描述不是记忆操作系统,而是:一个带调度器的、可治理的记忆资产管理系统,附赠一条真正省钱的 KV 相变通道。
参考来源
工程实践:
- MemTensor/MemOS 源码仓库(本文基于 commit
93e4082,2026-08-03,v2.0 “Stardust”;文中所有src/memos/...路径与行号均指向该版本) - MemOS 官方文档
- vLLM PagedAttention 博客
- 本站相关:Metis:记忆的内化 · vLLM 自动前缀缓存源码拆解
arXiv 论文:
- MemOS: A Memory OS for AI System(arXiv:2507.03724) — 本文主论文,长版
- MemOS: An Operating System for Memory-Augmented Generation in LLMs(arXiv:2505.22101) — 前置短版/愿景版
- Memory³: Language Modeling with Explicit Memory(arXiv:2407.01178) — 同团队理论前身:显式记忆作为第三种记忆形态
- MemGPT: Towards LLMs as Operating Systems(arXiv:2310.08560) — 上下文层的 OS 隐喻,模型自治路线
- Efficient Memory Management for LLM Serving with PagedAttention(arXiv:2309.06180) — 显存层的 OS 隐喻