第16章 MemGPT / Letta —— Agent 记忆的「操作系统」

论文: "MemGPT: Towards LLMs as Operating Systems" (2023)

项目: github.com/letta-ai/letta (原 MemGPT)

16.1 核心思想:把 LLM 当作操作系统

类比:计算机操作系统管理内存的方式

操作系统内存层次:

复制代码
┌──────────┐ 最快/最小

│ 寄存器 │

├──────────┤

│ L1/L2 │

│ Cache │

├──────────┤

│ RAM │ ← 主内存

├──────────┤

│ 磁盘 │ ← 持久化

└──────────┘ 最慢/最大

MemGPT 的内存层次:

复制代码
┌──────────────┐ 最快/在上下文中

│ Core Memory │ ← 当前对话 + 记忆块

│ (上下文内) │ (类似 RAM)

├──────────────┤

│ Recall Memory │ ← 完整对话历史

│ (上下文外) │ (磁盘,可检索)

├──────────────┤

│ Archival Memory│ ← 外部知识库

│ (向量数据库) │ (归档存储)

└──────────────┘ 最慢/最大

核心洞察:

  • LLM 的上下文窗口 = 操作系统的 RAM
  • 上下文有限,但通过「换页」技术可以管理无限容量的信息
  • OS 用虚拟内存换页 → MemGPT 用记忆管理工具换页

16.2 MemGPT 的内存管理机制

📊 架构示意

复制代码
┌────────────────────────────────────────────────────┐
│                 MemGPT Agent                         │
│                                                      │
│  ┌──────────────────────────────────────────────┐  │
│  │            Core Memory (上下文内)              │  │
│  │  ┌──────────┐ ┌──────────┐ ┌──────────────┐ │  │
│  │  │ System   │ │ Memory   │ │ Conversation │ │  │
│  │  │ Prompt   │ │ Blocks   │ │ History      │ │  │
│  │  └──────────┘ └──────────┘ └──────────────┘ │  │
│  └──────────────────────────────────────────────┘  │
│                       │                             │
│            ┌──────────┴──────────┐                  │
│            │   对话历史管理工具     │                  │
│            │  ─────────────────  │                  │
│            │  conversation_search │ ← 搜索完整历史    │
│            │  core_memory_append  │ ← 编辑记忆块      │
│            │  core_memory_replace │ ← 替换记忆块      │
│            └─────────────────────┘                  │
│                       │                             │
│  ┌────────────────────┴──────────────────────────┐ │
│  │              外部记忆(上下文外)                │ │
│  │  ┌────────────────┐  ┌──────────────────────┐ │ │
│  │  │ Recall Memory   │  │  Archival Memory      │ │ │
│  │  │ (对话历史全文)    │  │  (向量库 + 知识检索)   │ │ │
│  │  │ archival_memory │  │  archival_memory     │ │ │
│  │  │ _search        │  │  _insert             │ │ │
│  │  └────────────────┘  └──────────────────────┘ │ │
│  └──────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────┘

核心工具(Agent 通过 Function Calling 调用):

  • send_message: 向用户发送消息(MemGPT 唯一的消息出口)
  • core_memory_append: 向 Core Memory 块追加信息
  • core_memory_replace: 替换 Core Memory 块的内容
  • conversation_search: 搜索完整对话历史
  • archival_memory_insert: 将信息存入归档记忆(向量化)
  • archival_memory_search: 从归档记忆中检索

16.3 Heartbeat 机制 ------ Agent 的「自我意识」

传统 Agent 的问题:

工具调用 → 返回结果 → 给用户 → 结束

Agent 没有机会检查「我做得对不对」

Heartbeat 的解决方案:

每个工具的返回结果中包含一个 request_heartbeat 参数

如果设为 true → 工具执行完毕后,控制权交还给 Agent

Agent 可以:检查结果 → 发现错误 → 自动重试

代码示意:

工具返回结果

复制代码
{

"tool_call_id": "call_123",

"content": "搜索结果为空",

"request_heartbeat": true // ← 请求心跳!

}

Agent 收到心跳后

→ "搜索结果为空?我换一个关键词试试"

→ 再次调用 search("换一个搜索词")

→ 这次找到了!

这类似于:

你在命令行执行 ls 之后,shell 不会直接退出

而是等你输入下一个命令

Heartbeat 就是给 Agent 的「命令提示符」

16.4 Sleep-Time Compute ------ 后台记忆整理

MemGPT v2 的创新(2025年)。

核心思想:

Agent 在「休眠」时(用户不互动时),启动一个后台 Agent 来整理记忆。

做什么?

  • 浏览最近的对话历史
  • 提取重要信息 → 写入 Core Memory
  • 删除过时的信息
  • 更新用户画像
  • 合并重复的记忆

类比:

就像你睡觉时,大脑在整理白天的记忆

不重要的事情遗忘,重要的事情巩固

效果:

Letta 在 LoCoMo 基准测试上达到 74.0% 准确率

(仅用文件系统 + GPT-4o-mini)

16.5 Letta Filesystem ------「文件系统 = 最好的记忆工具」

2025年8月,Letta 发表了一篇震撼论文:

"Is a Filesystem All You Need?"

→ LoCoMo 基准测试 74.0%(超过专用记忆工具 Mem0 的 68.5%)

为什么文件系统做记忆效果惊人?

  • 文件操作是 LLM 最熟悉的范式

  • grep / open / close / search_files

  • 这些工具在 LLM 训练数据中大量出现

  • Agent 对它们的使用非常自然

  • 简单即正确

  • 专用记忆工具引入额外复杂性

  • 多了出错的环节

  • Agent 需要理解专用工具的特殊语义

  • 无需学习新范式

  • 每个记忆工具(Mem0/Zep/LangMem)都有自己的 API

  • LLM 需要「学会」每个新工具

  • 而文件操作是 LLM 的「母语」

核心启示(面试金句!):

"Agent 的记忆质量取决于 Agent 管理上下文的能力,而不是记忆工具的复杂度。文件系统 + 好用工具 = 最佳记忆方案。"

16.5.1 Heartbeat 与 Sleep-Time 的工程实现 ------ 不只是「定时器」

▍ Heartbeat 的成本计算 ------ 「自我纠错」要花多少钱?

面试官问:「Heartbeat 每次都要多调一次 LLM,成本怎么控制?」

每次 Heartbeat 触发时,Agent 要把「当前状态 + 工具结果」发给 LLM,让 LLM 判断是否需要进一步行动。这确实增加了成本。

控制策略:

a) 选择性 Heartbeat ------ 不是每个工具都请求心跳,只在「关键工具」(数据库写入、API 调用)执行后触发

b) 使用廉价模型做 Heartbeat ------ Heartbeat 判断是「是否需要继续」,不要求深度推理 → 用 gpt-4o-mini 就够

c) Heartbeat 超时限制 ------ 最多触发 3 次心跳,避免死循环

d) Token 预算驱动 ------ 累计消耗超过阈值 → 停止心跳,直接总结

经验数据:合理的 Heartbeat 机制增加 15-25% 的额外成本,但减少 30-50% 的错误率 → ROI 是正的。

▍ Sleep-Time Compute 的调度设计

Letta 的 Sleep-Time Compute 不是简单的 cron 任务。它需要判断:

  • 什么时候触发?(用户 5 分钟无交互?1 小时?)
  • 处理哪些记忆?(最近 10 轮对话?所有对话?)
  • 用什么模型?(整理记忆不需要 GPT-4o,小模型够用)

工程实现建议:

  • 用户交互结束后启动一个 30 秒的倒计时
  • 倒计时内用户再次交互 → 取消倒计时
  • 倒计时结束 → 启动 Sleep-Time Agent(用廉价模型)
  • Sleep-Time Agent 扫描本轮对话 → 提取要点 → 更新 Core Memory
  • 完成后 Agent 回到待机状态

注意:Sleep-Time Compute 不是实时需求 → 可以异步执行,不阻塞用户的下一次交互。这也是为什么 Letta 使用后台 Agent 的原因。

▍ 记忆压缩的工程权衡 ------ 保留什么,丢弃什么?

MemGPT 记忆管理最核心的决策:「什么信息值得进入 Core Memory?」

面试可以主动分析:

值得保留 → 用户偏好(「我喜欢简洁回答」)、事实信息(「我是北京人」)、任务上下文(「正在做税务报表,已完成 Q1-Q3」)

可以丢弃 → 临时搜索词、失败的尝试、已过时的数据、与当前任务无关的闲聊内容

工业界的经验法则:Core Memory 控制在 2000 tokens 以内(留给对话历史足够的空间)。超过上限 → 触发记忆整理

(合并相似、删除过时、归档低频)

16.6 MemGPT 架构的简化实现

下面实现一套 MemGPT 风格的 Agent 记忆管理模拟器,核心三组件:

  • CoreMemoryBlock ------ 存储 Agent 的核心身份信息(人类/角色)
  • Heartbeat ------ 定时触发反思,模拟 Sleep-Time Compute
  • MemoryManager ------ 统一的记忆读写接口

这套架构的精髓在于:Agent 不是被动地被调用,而是像操作系统一样持续运行,

定期自我审视和整理记忆。

📝 对应的代码实现

core_memory_appendcore_memory_replacearchival_memory_insertarchival_memory_searchcompact_conversationheartbeat_should_continuedemo_memgpt_workflowMemGPTAgent

复制代码
from typing import Optional
import json
import time
import hashlib


class MemGPTAgent:
    """MemGPT 架构的简化实现 ------ 展示核心概念。

    实现:
      1. Core Memory 块(System + Human + Persona)
      2. 记忆编辑工具(core_memory_append/replace)
      3. Conversation Search(对话历史搜索)
      4. 上下文压缩(对话历史过长时自动压缩)
    """

    def __init__(self, system_prompt: str = ""):
        """初始化 MemGPT Agent。

        Args:
            system_prompt: 系统提示词。
        """
        # Core Memory 块
        self.core_memory = {
            "human": "用户偏好和背景信息",
            "persona": "AI助手,专业且友好",
            "system": system_prompt or "你是一个有用的 AI 助手。",
        }

        # 完整对话历史(Recall Memory)
        self.conversation = []

        # 归档记忆(简化版:字典存储)
        self.archival = {}

        # 已压缩的摘要
        self.compacted_summary = ""

    def _build_context(self, max_recent: int = 10) -> list[dict]:
        """构建发给 LLM 的上下文。

        包含:
          1. Core Memory 块
          2. 压缩摘要(如果有)
          3. 最近 N 条对话

        Args:
            max_recent: 最大保留的最近消息数。

        Returns:
            上下文消息列表。
        """
        context = []

        # Core Memory 组装
        core_text = "\n".join(
            f"### {key}\n{value}" for key, value in self.core_memory.items()
        )
        context.append({
            "role": "system",
            "content": f"<memory>\n{core_text}\n</memory>",
        })

        if self.compacted_summary:
            context.append({
                "role": "system",
                "content": f"<summary>\n{self.compacted_summary}\n</summary>",
            })

        # 最近 N 条对话
        recent = self.conversation[-max_recent:] if self.conversation else []
        context.extend(recent)

        return context

    def core_memory_append(self, block: str, content: str):
        """向 Core Memory 块追加内容。

        Args:
            block: 块名(human/persona)。
            content: 追加的内容。
        """
        if block in self.core_memory:
            self.core_memory[block] += "\n" + content

    def core_memory_replace(self, block: str, content: str):
        """替换 Core Memory 块的内容。

        Args:
            block: 块名。
            content: 新内容。
        """
        if block in self.core_memory:
            self.core_memory[block] = content

    def archival_memory_insert(self, key: str, content: str):
        """向归档记忆插入一条信息。

        Args:
            key: 检索键。
            content: 信息内容。
        """
        self.archival[key] = {
            "content": content,
            "timestamp": time.time(),
        }

    def archival_memory_search(self, query: str) -> str:
        """从归档记忆搜索(简化版关键词匹配)。

        Args:
            query: 搜索词。

        Returns:
            匹配的结果。
        """
        results = []
        for key, entry in self.archival.items():
            if query.lower() in key.lower() or query.lower() in entry["content"].lower():
                results.append(f"[{key}] {entry['content']}")
        return "\n".join(results[-5:]) if results else "未找到相关信息"

    def compact_conversation(self):
        """对话压缩:早期对话 → 摘要,放入 compacted_summary。

        这是 MemGPT 最核心的操作之一:
          当对话历史过长时,对早期部分做摘要压缩。
        """
        if len(self.conversation) <= 20:
            return

        # 取最早的 10 条对话做压缩
        old_part = self.conversation[:10]
        self.conversation = self.conversation[10:]

        # 简化的摘要生成
        topics = set()
        for msg in old_part:
            for word in msg.get("content", "").split():
                if len(word) > 3:
                    topics.add(word.lower())

        new_summary = (
            f"此前讨论了以下话题:{', '.join(list(topics)[:10])}..."
        )

        if self.compacted_summary:
            self.compacted_summary += "\n" + new_summary
        else:
            self.compacted_summary = new_summary

    def heartbeat_should_continue(self, last_tool_result: str) -> bool:
        """判断是否需要 Heartbeat(继续给 Agent 控制权)。

        如果工具调用失败或者返回不充分,
        返回 True 让 Agent 继续思考和行动。

        Args:
            last_tool_result: 上一步工具调用的结果。

        Returns:
            是否需要继续。
        """
        # 模拟判断:如果结果包含错误信号
        error_signals = ["未找到", "错误", "失败", "抱歉", "无法"]
        return any(sig in last_tool_result for sig in error_signals)


def demo_memgpt_workflow():
    """演示 MemGPT 的完整工作流。"""
    print("=" * 60)
    print("  MemGPT 记忆架构演示")
    print("=" * 60)

    agent = MemGPTAgent()

    # Step 1: 展示初始 Core Memory
    print("\n  🧠 初始 Core Memory:")
    for key, value in agent.core_memory.items():
        print(f"    [{key}] {value}")

    # Step 2: Agent 学习用户信息 → 写入 Core Memory
    print("\n  📝 用户告诉 Agent 自己的偏好")
    agent.core_memory_append("human", "喜欢简洁的回答风格")
    agent.core_memory_append("human", "是Python开发者,经常需要代码帮助")
    agent.core_memory_replace("persona", "Python 专家助手,回答简洁专业")

    print("    更新后的 Core Memory:")
    for key, value in agent.core_memory.items():
        print(f"    [{key}] {value}")

    # Step 3: 模拟对话
    print("\n  💬 模拟对话")
    dialogs = [
        ("user", "帮我写一个快速排序算法"),
        ("assistant", "好的,这是一个 Python 快速排序实现:\ndef quicksort(arr):\n    if len(arr) <= 1:\n        return arr\n    pivot = arr[0]\n    left = [x for x in arr[1:] if x <= pivot]\n    right = [x for x in arr[1:] if x > pivot]\n    return quicksort(left) + [pivot] + quicksort(right)"),
        ("user", "谢谢!再帮我分析一下时间复杂度"),
        ("assistant", "快速排序的平均时间复杂度是 O(n log n),最坏情况是 O(n²)..."),
    ]
    for role, content in dialogs:
        agent.conversation.append({"role": role, "content": content})

    print(f"    当前对话数: {len(agent.conversation)}")

    # Step 4: 写入归档记忆
    print("\n  📦 归档记忆")
    agent.archival_memory_insert(
        "quicksort", "快速排序:O(n log n) 平均,O(n²) 最坏,非稳定排序"
    )
    agent.archival_memory_insert(
        "python_best_practice", "Python编码规范:PEP 8, 类型注解, 单元测试"
    )
    agent.archival_memory_insert(
        "user_preference", "用户偏好简洁代码,不喜欢过度注释"
    )
    print(f"    已存入 {len(agent.archival)} 条归档记忆")

    # Step 5: 搜索归档记忆
    print("\n  🔍 搜索归档记忆")
    result = agent.archival_memory_search("排序")
    print(f"    搜索「排序」→ {result}")

    # Step 6: 模拟对话压缩
    print("\n  🗜️ 对话压缩演示")
    # 模拟大量对话
    for i in range(50):
        agent.conversation.append({
            "role": "user",
            "content": f"问题{i}:这是一条测试消息",
        })
        agent.conversation.append({
            "role": "assistant",
            "content": f"回答{i}:这是一条回复",
        })

    print(f"    压缩前: {len(agent.conversation)} 条消息")
    agent.compact_conversation()
    print(f"    压缩后: {len(agent.conversation)} 条消息")
    print(f"    摘要: {agent.compacted_summary[:100]}...")

    # Step 7: Heartbeat 机制演示
    print("\n  💓 Heartbeat 机制演示")
    test_cases = [
        ("查询成功:找到 3 条结果", False),
        ("未找到相关数据,请更换搜索词", True),
        ("工具调用失败:API 超时", True),
        ("计算完成:结果为 42", False),
    ]
    for result, expected_heartbeat in test_cases:
        needs_heartbeat = agent.heartbeat_should_continue(result)
        status = "✅" if needs_heartbeat == expected_heartbeat else "❌"
        print(f"    {status} 「{result[:25]}...」→ 需要心跳: {needs_heartbeat}")

    # Step 8: 展示 LLM 上下文
    print("\n  📋 发给 LLM 的上下文结构")
    context = agent._build_context(max_recent=3)
    for i, msg in enumerate(context):
        role = msg["role"]
        content = msg["content"][:80].replace("\n", " ")
        print(f"    [{i}] {role}: {content}...")

16.7 本章总结

核心要点回顾:

  • MemGPT = 把 LLM 当操作系统管理

  • Core Memory = RAM(上下文中,快速访问)

  • Recall Memory = 磁盘(对话历史全文,可检索)

  • Archival Memory = 归档(向量库 + 外部知识)

  • Heartbeat 机制

  • 工具调用后把控制权交还给 Agent

  • Agent 可以检查结果、发现错误、自动重试

  • 类似命令行的「命令提示符」

  • Sleep-Time Compute (MemGPT v2)

  • Agent 休眠时后台整理记忆

  • 提取重要信息、删除冗余、更新画像

  • Letta Filesystem 的启示

  • 文件系统做记忆 = 简单 + 效果好

  • LoCoMo 74.0%(超过专用工具的 68.5%)

  • Agent 不需要复杂的记忆工具,需要好用的工具

面试速记:

"MemGPT 的核心创新是什么?"

→ OS 级内存管理:Core Memory + Recall + Archival

→ 通过工具让 Agent 自己管理自己的记忆

→ Heartbeat 机制实现自我纠错

→ 「文件系统就是最好的记忆工具」

📝 对应的代码实现

复制代码
from typing import Optional
import json
import time
import hashlib

if __name__ == "__main__":
    print("╔══════════════════════════════════════════════════════╗")
    print("║  第16章:MemGPT / Letta 记忆架构                        ║")
    print("║  Core Memory · Heartbeat · Sleep-Time · Filesystem   ║")
    print("╚══════════════════════════════════════════════════════╝")

    demo_memgpt_workflow()

    print("\n▶ MemGPT 内存层次对应关系")
    print("-" * 50)
    layers = [
        ("Core Memory", "RAM", "当前上下文 + 可编辑记忆块"),
        ("Recall Memory", "磁盘", "完整对话历史,可搜索"),
        ("Archival Memory", "归档", "向量数据库,外部知识"),
    ]
    for mem, os, desc in layers:
        print(f"  {mem:18s} ↔ {os:6s} → {desc}")

    print("\n✅ 第16章完成!")

复制代码