Letta (MemGPT) — 架构与原理
30 秒导读: Letta(原名 MemGPT)是一个有长期记忆的 LLM agent 框架。它的核心洞见:LLM 的上下文窗口有限,就像计算机内存有限——那就借操作系统的分页思路,让 agent 用工具自己编辑一块「常驻内存」,并把放不下的对话历史和知识换页到数据库里,需要时再搜回来。这样 agent 能跨会话记住你是谁、聊过什么。
1. 这是什么(零基础也能懂)
一句话定义: Letta 是一个让 LLM agent 拥有持久、可自我编辑的记忆的框架——agent 不是每次对话都从零开始,而是记得过去、并能主动整理自己的记忆。
它解决谁的什么问题。 普通聊天机器人有个硬伤:上下文窗口装满就忘。你今天告诉它「我叫 Timber、我养狗」,聊久了这句被挤出窗口,它就再也不知道了。Letta 要解决的就是:
- 怎么让 agent 跨会话记住关于你的事实?
- 上下文塞不下时,该丢什么、该留什么、丢掉的怎么找回来?
- 能不能让 agent 自己决定把哪条信息写进长期记忆?
核心类比(记住这一个就够了):把 LLM 当一台只有一点点内存的电脑。
| 计算机概念 | Letta 里对应什么 | 特点 |
|---|---|---|
| RAM(内存,快但小) | 上下文窗口里常驻的「记忆块」(core memory) | 模型每一步都能直接看到,但有字符上限 |
| 磁盘(慢但大) | 数据库里的归档记忆(archival)+ 历史消息(recall) | 装得多、永久存,但要用工具「搜」才能调进来 |
| 操作系统的换页 | Letta 的压缩(compaction)+ 搜索工具 | 把溢出的东西移到磁盘,需要时再读回内存 |
这就是 MemGPT 那篇论文的全部精髓:给 LLM 配一个「内存管理器」,让它像操作系统管理内存一样管理自己的上下文。
它能做什么:
- 维 护一块始终在上下文里的核心记忆(比如
human块记你是谁、persona块记 agent 自己的设定)。 - 让 agent 用工具自己改写这块核心记忆(发现新事实就 append、信息过时就 replace)。
- 把对话历史和长期知识存进数据库,用语义搜索找回来。
- 上下文快满时自动压缩旧消息成摘要,腾出空间继续聊。
用起来什么样。 创建 agent 时给它两块初始记忆,然后发消息——它会记住:
# 示意:来自 README 的真实用法
from letta_client import Letta
client = Letta(api_key=...)
agent = client.agents.create(
model="openai/gpt-5.2",
memory_blocks=[
{"label": "human", "value": "Name: Timber. 养了一条狗。"}, # 关于用户的常驻记忆
{"label": "persona", "value": "我是一个会自我改进的助手。"}, # 关于 agent 自己的常驻记忆
],
tools=["web_search", "fetch_webpage"],
)
# 之后发消息;agent 会把它学到的关于 Timber 的新事实写进 human 块,跨会话保留
client.agents.messages.create(agent.id, input="What do you know about me?")
这两个块(human / persona)会被渲染进系统提示、每一步都喂给模型——见 letta/schemas/memory.py:840-854(ChatMemory)。
本节不碰底层。记住一句话就行:Letta = 给 LLM 装一个内存管理系统,让它能记事、能自己整理记忆。
2. 顶层全景(它大概怎么转)
2.1 三层记忆 + 一个工具循环
Letta 的整个系统可以拆成两件事:记忆放在哪(三层存储) 和 agent 怎么动(工具驱动的循环)。
先看记忆的三层结构——这是「内存 vs 磁盘」类比的具体落地:
┌──────────────────────── 上下文窗口(模型每一步都能看到) ────────────────────────┐
│ │
│ 系统提示 = 基座指令 + <memory_blocks>(core memory) + <memory_metadata> │
│ ┌────────────── ┐ ┌──────────────┐ │
│ │ human 块 │ │ persona 块 │ ← 这就是「RAM」:常驻、可被 agent 改写 │
│ └──────────────┘ └──────────────┘ │
│ │
│ 最近的对话消息(recall 的「在上下文那部分」) │
└────────────────────────────────────────┬───────────────────────────────────────┘
│ 工具读 / 写 / 搜
┌─────────────────────────┼─────────────────────────┐
▼ ▼ ▼
┌───────────────┐ ┌────── ───────────┐ ┌──────────────────┐
│ recall memory │ │ archival memory │ │ core memory 工具 │
│ = 全部历史消息 │ │ = 长期知识(向量) │ │ 改写 RAM 的工具 │
│ (数据库) │ │ (数据库 + 嵌入) │ │ │
└───────────────┘ └─────────────────┘ └──────────────────┘
conversation_search archival_memory_search memory_replace / insert
(按文本+语义搜历史) (按语义+标签搜知识) core_memory_append ...
怎么读这张图(从上到下 = 从「内存」到「磁盘」): 上面方框是模型每一步实际看到的上下文;下面三个方框是数据库里的东西,模型看不到,只能通过箭头上的工具把它们搜进上下文。
2.2 三层记忆,一句话各是什么
| 记忆层 | 是什么 | 在不在上下文里 | 怎么访问 | 代码锚点 |
|---|---|---|---|---|
| Core memory(核心/常驻) | 一组「记忆块」(human/persona/自定义),每块有 label、description、value、字符上限 | 在(渲染进系统提示) | agent 用 memory_replace / memory_insert / core_memory_append 等工具自己改 | letta/schemas/memory.py:68(Memory)、schemas/block.py:67(Block) |
| Recall memory(回忆) | 这个 agent 的全部历史消息,存在数据库 | 只有最近一段在 | conversation_search(文本 + 语义混合搜) | functions/function_sets/base.py:87(conversation_search) |
| Archival memory(归档) | agent 主动写入的长期事实/知识,带向量嵌入和标签 | 不在(永远在数据库) | archival_memory_insert 写、archival_memory_search 按语义搜 | functions/function_sets/base.py:164,194 |
2.3 主线走一遍(高层,不进代码)
一条用户消息进来,大致经历:
- 组装上下文。 取出系统提示(里面已嵌入 core memory 块)+ 在上下文的历史消息 + 这条新消息。
- 调模型。 把上面这一大坨连同可用工具的定义发给 LLM。
- 模型回应。 它要么直接回话(发
send_message),要么调一个工具——比如archival_memory_search去翻长期记忆,或memory_replace去更新你的资料。 - 执行工具、决定是否继续。 关键规则(v3):调了工具就继续循环、没调工具就结束(
agents/letta_agent_v3.py:1979)。这样 agent 能连着搜记忆、改记忆、再回话。 - 持久化。 新产生的消息写库;若上下文逼近上限,触发压缩把旧消息换成摘要。
这个工具驱动的循环 + 三层记忆,就是 Letta 的「大盘」。
3. 顺着读下去
这是个大项目,下面按由浅入深拆成四章。建议顺序:
- 01-memory-tiers.md — 先彻底搞懂三层记忆:记忆块长什么样、怎么渲染进提示、agent 用哪些工具读写、归档的语义搜索怎么工作。这是 Letta 的灵魂。
- 02-agent-loop.md — 再看 agent 怎么动:v3 工具驱动主循环,以及它和老版 MemGPT「心跳(heartbeat)」模型的区别。
- 03-system-prompt-assembly.md — 深入系统提示是怎么一块块拼出来的,以及「只在记忆变化时才重建提示」这个性能优化。
- 04-compaction-and-internals.md — 上下文满了怎么办(压缩)、整套设计的巧妙之处、边 界、横向对比、代码地图。
如果你只想要一句话精华:Letta 把「上下文管理」从一个被动的截断问题,变成了一个 agent 能主动用工具操作的、像操作系统分页一样的系统。