[!MemGPT 的 OS 类比词典]

  • main context: LLM 的 context window, 类比物理 RAM
  • external context: 向量库 / 关系库 / 日志, 类比 disk
  • function call: 模型主动触发的 I/O, 类比 system call
  • queue manager: FIFO 驱逐器, 类比 page replacement policy
  • heartbeat: 定时唤醒, 类比 timer interrupt

Context Window 的硬上限

现有商用 LLM 每次 forward 能看到的 token 数是有限的, 比如 GPT-4 turbo 是 128K, Llama-2-chat 是 4K; 一旦 input + output 超出这个窗口, 后续内容要么被截断, 要么只能靠外部检索 (classic RAG) 在推理前硬塞进去;

这带来两个根本问题:

  • 长对话: session 时间够长后, 早期轮次会被挤出窗口, 模型彻底"忘"掉用户之前说过的事实
  • 长文档分析: 单份文档已经接近或超过 context 上限, 跨章节的 key-value 关联几乎做不了

两个问题本质都是同一个: context 是昂贵的短期存储, 不能无限扩张, 需要一种外部存储 + 调度机制;

另外注意, 自注意力的计算量是 O(n2)O(n^2), 直接把窗口拉到无限大在工程上也不现实, 因此"扩窗口"这条路至少短期内是有上限的;

OS Memory Hierarchy 做类比

传统 OS 已经用几十年解决了完全同构的问题, MemGPT 的直觉就是照搬这套层级:

层级 容量 延迟 OS 中的角色 MemGPT 中的角色
Register / Cache 极小 极低 CPU 直接取用 attention 直接读取的 prompt token
RAM 有限 活跃程序的状态 main context — 本轮 forward 能看到
Disk / SSD 很大 非活跃程序 / 文件系统 external context — 向量库 / 对话历史 / KV store

OS 通过 MMU, page table, page fault handler 让程序看起来有一个几乎无限的虚拟地址空间; MemGPT 想复刻这一套, 让 LLM 看起来有一个几乎无限的上下文;

区别是: OS 里 page fault 由 MMU 硬件触发, 而 MemGPT 里 page fault 由模型自己通过 function call 主动触发, 所以模型本身扮演了一部分 kernel 的角色;

论文定位

MemGPT 不是一个新模型, 也不是一个新的 attention 变种, 而是一个围绕现有 function-calling LLM 搭建的 memory management system; 它的核心主张是:

  • 与其等 context length 继续增长, 不如让模型主动管理自己的 context
  • 让模型通过 function call 发起 “装入 / 驱逐 / 归档” 操作
  • 把 LLM 本身当成 kernel 的一部分, 决定什么该留在 RAM, 什么该换出

论文解决的核心问题

现有方案的不足:

  • 暴力拉大 context length: 吃 O(n2)O(n^2) 的 attention 成本, 而且窗口再大也仍然存在超出的风险, 并不是根治
  • classic RAG: 在一次 forward 之前做一次向量检索, 然后把结果塞进 prompt 头部; 问题是 retrieval 时机固定, 只能被动响应当前 query, 无法在对话中途决定"我现在需要回忆起三周前那句话"
  • sliding window / attention sink: 只是延缓遗忘, 没有持久化, 也没有"回忆"能力

MemGPT 试图回答:

  1. 能不能让 LLM 自己决定何时去 external storage 拉数据?
  2. 能不能让 LLM 自己决定把哪些 context 驱逐 / 归档?
  3. 能不能把上面两件事用统一的 function-call 接口暴露, 保持可观测和可扩展?

insight

Main context 的三段式分层

MemGPT 把 context window 不视作一块扁平的 prompt buffer, 而是切成三个区域:

分区 可写性 生命周期 类比
System instructions read-only 整个 session 常驻 OS kernel code
Working context read-write, 模型显式编辑 跨 turn 持续 进程 heap / 长期状态
Conversational context append-only, FIFO 驱逐 短期, 会被挤出 CPU 的 working set

其中 working context 是最关键的设计: 它是一个模型自己能写的小笔记本, 用于持久化那些"我希望一直记得的事实", 比如用户的名字, 偏好, 角色设定; 在 OS 类比里 working context 不是 buffer cache, 而是"进程自己持有的数据结构", 模型把它当作第一人称状态来编辑;

External context 的两种后端

超出 main context 的所有内容会流向 external context, 分两类:

  • Recall storage: 保存所有历史 event / message 的原始流水, 支持文本或向量检索; FIFO 驱逐下来的消息并不真的被删除, 而是进入 recall storage, 模型之后可以通过 conversation_search 拉回来
  • Archival storage: 模型主动写入的持久化知识库, 形式上是一组可检索的 document chunks; 比 recall 更结构化, 适合放笔记 / 摘要 / 总结

两者都只有通过 function call 才能被访问, 不会未经请求就被塞进 prompt; 这一点很重要, 它让 external context 的流量是受控的, 不会像 RAG 那样一上来就挤占 prompt 开头;

Function calls as system calls

MemGPT 依赖 function-calling LLM (原论文用的是 GPT-4), 给模型暴露一组统一接口, 每次 forward 可以输出若干 function call:

  • send_message: 正常回复用户
  • conversation_search, archival_memory_search: 从 external storage 拉取
  • core_memory_append, core_memory_replace: 编辑 working context
  • archival_memory_insert: 把新知识沉淀到 archival
  • pause_heartbeats: 暂停后台中断

每个 function call 对应一次"系统调用", 返回结果会作为下一次 forward 的 observation 注入; 这种"模型输出 \rightarrow kernel 执行 \rightarrow observation 回注"的循环, 和 OS 里 user space \rightarrow kernel space \rightarrow 返回 user space 的切换几乎一一对应;

Queue manager 与 FIFO 驱逐

Conversational context 的容量有限, 当 token 使用率超过阈值 (论文里用 main context 容量的某个比例) 就触发 queue manager:

  • 最早一批消息从 conversational context 驱逐 (evict)
  • 同一份内容同时被归档到 recall storage, 保留可检索性
  • 模型可以通过 conversation_search 再把这些内容召回 (recall) 回 main context

这就是一个完整的 page-out \rightarrow page-in 周期; 与 OS 的 LRU/LFU 不同, 这里的驱逐策略显式采用 FIFO, 因为时间顺序本身就是对话语义的一部分, LRU 的"最近访问"在对话里反而不自然;

📝 关键区分
  • 驱逐不等于删除: 消息仍在 recall storage 里
  • 召回不等于重复: 召回后消息会被重新插入 main context, 但原位归档仍然保留

Heartbeat: 模型的自我中断

对话类场景有一个特殊问题: 模型只在用户有输入的时候才被调用; 但很多"记忆维护"动作其实希望在空闲时间做, 比如对一段长对话先做摘要再写入 archival;

MemGPT 的方案是 heartbeat events:

  • 系统会在某些条件下主动给模型一条空消息, 等价于一次硬件 timer interrupt
  • 模型也可以在本次 function call 中把 request_heartbeat=true, 表示"我还有后续工作要做, 请再给我一次机会"
  • 这让模型能做多步连续动作, 而不被"每轮只能输出一次"的默认 chat 协议卡住

这和 OS 中 中断 + syscall return 的组合极其相似: function call 返回后如果请求了 heartbeat, 等价于 schedule 一个后续 tick;

MemGPT vs Classic RAG

维度 Classic RAG MemGPT
检索时机 forward 之前, 固定一次 模型决定何时, 可以任意多次
检索对象 静态知识库 对话历史 + 模型自建 archival
写入能力 只读 模型可写 working / archival
驱逐策略 无 (每次都重构 prompt) FIFO, 保留原始归档
工作方式 reactive — 针对当前 query 拉文档 agentic — 主动管理自己的记忆
失败模式 retrieval miss 直接体现在答案里 模型可以察觉 miss 并重新检索

结论: RAG 是一次性 context 装填, MemGPT 是持续的 context 调度;

评估: 两个场景都是 context 上限放大器

Deep memory retrieval (多 session 对话)

设定: 给定先前 session 中提到过的事实, 在新 session 里正确引用;

baseline (固定 context) 在 session 数超过窗口容量后准确率断崖式下跌; MemGPT 依靠 conversation_search 主动查询早期 session, 能把准确率维持在高位;

关键启示: 让模型意识到自己应该去查, 比让模型天然记住更可扩展;

Nested key-value retrieval (长文档)

设定: 一个文档内部存在多层引用 (A \rightarrow B \rightarrow C \rightarrow …), 要求模型最终给出末端值;

固定 context 装不下整份文档, 每次跳转都失败; MemGPT 通过 archival_memory_search 做多次跳转式检索, 能显著延长可成功跳转的层数;

关键启示: 长文档不是一次性全部装入的问题, 而是如何做受控随机访问的问题;

四个工程化细节值得留意

  1. Prompt 里的 kernel 协议: 模型要能可靠调用 function, 必须在 system prompt 里用自然语言告诉模型自己是 MemGPT 的一部分, 把 OS 类比直接交代清楚; 这其实是一次 prompt-level 的 OS ABI 定义
  2. Function schema 严格校验: 非 function-calling 模型 (比如早期开源 7B) 很容易出现格式漂移, 论文里用的是 GPT-4, 下游实现需要考虑格式矫正层
  3. Recall 的检索不是向量必选: 论文强调支持文本检索 (BM25) 和向量检索的混合, 因为对话历史里大量是精确字面引用, 向量反而会 miss
  4. Archival 的写入是幂等式的: 同一段摘要可能被多次追加, 工程上要做 dedup / merge, 否则 archival 会退化成垃圾场

具体的开源落地在 Letta (原 MemGPT 项目) 里有进一步实现, 工程上的 schema / retry / 格式矫正这里先挖个坑, 后面单独开一篇笔记讨论;

📝 论文主流观点总结

如果按论文主线浓缩, MemGPT 的主流观点可以总结为:

  1. Context length 的物理上限短期内不会被彻底抹平, 因此必须引入分级存储思想
  2. LLM 本身已经足够强, 可以扮演 memory management 的决策者, 而不是被动的 context 消费者
  3. Function call 是一种天然的"用户态 \rightarrow 内核态"边界, 把它复用为 memory API 是最小侵入的做法
  4. 驱逐策略应当保留语义顺序 (FIFO), 而不是 OS 里常用的 LRU, 因为对话本身是时间敏感的
  5. Heartbeat 机制让 LLM 从"一次回复 = 一次决策"进化到"可以连续做多步内部任务", 为 agentic 行为打下协议基础
  6. MemGPT 不是 RAG 的替代品, 而是把 RAG 从一次性 retrieval 泛化为可调度的 memory subsystem