4.2. Letta 将 MemGPT 从研究原型变成工程化框架

Letta 将 MemGPT 从研究原型变成工程化框架

2024 年 11 月,MemGPT 的研究团队宣布了一个重要决定:这个曾以“操作系统之于大模型”惊艳学界的项目,正式并入全新平台 Letta。在经历了一年多的论文引用、社区复现和原型打磨之后,MemGPT 不再只是一篇展示虚拟上下文管理思想的论文附件——它变成了一个可安装、可配置、可调用的工程化框架。

如果你读过上一章讨论的 MemGPT 论文,一定记得它用“分页”“中断处理”等 OS 概念管理 LLM 注意力的精妙想法。但论文里的 demo 是硬编码的,你需要手动管理上下文窗口,自行实现记忆换入换出,甚至连 agent 的状态持久化都要自己写。Letta 的使命就是把这种“上下文治理”的思维模型,包装成一组稳定的 SDK 与 API,让你像操作普通微服务一样,创建、恢复、调试一个拥有真实记忆能力的智能体。

这一章正好成为一个关键枢纽:我们不再只是理解概念,而是动手写出第一个用 Letta SDK 构建的、具备持久化状态和记忆块治理能力的 agent。本篇教程将带你从零走完安装、配置、记忆块设计与跨会话对话的全过程——如果你已经厌倦了“每次对话都失忆”的聊天机器人,是时候让它真正记住你了。


你需要什么

  • Python 3.10+ 环境(推荐使用虚拟环境)
  • 一个 OpenAI API Key(用于访问 GPT-4o-mini 等模型;也可换成其他兼容模型,但本教程以 OpenAI 为例)
  • 终端或 VSCode 等代码编辑器
  • 预计耗时:约 30 分钟

最终成果

你将得到一个能在多次独立 Python 会话中持续积累用户信息的 Letta agent。它会记住你的饮食偏好、工作习惯,甚至能从一段对话追踪到几天后,无缝延续上下文。更重要的是,你会理解这种能力背后的关键机制——Memory Blocks——并学会如何为不同的长对话场景划分记忆区。

为什么做这个?因为几乎所有复杂的 agent 应用,无论是客服机器人、个人知识管家还是协作式编程助手,都必须解决“跨交互记忆”问题。Letta 提供了迄今为止最干净、最显式的持久化记忆接口,而亲手实现一次,就会懂得“结构化上下文治理”远优于堆积消息历史。


一、安装、配置与第一段代码

步骤 1:安装 Letta SDK 与相关依赖

在终端中执行以下命令,确保拉取的是最新版本的 Letta(截至当前调研素材,SDK 已稳定可用)。

pip install letta-client

预期结果pip list 中应出现 letta-client 及其依赖项,例如 httpx, pydantic 等,无错误输出。

踩坑提示
有些环境可能需要先升级 pippip install --upgrade pip。此外,如果之前安装过 MemGPT 的旧包,建议删除虚拟环境后重新创建,避免命名冲突。

步骤 2:设置 OpenAI API Key

Letta SDK 默认使用 OpenAI 作为模型提供方,需要在环境变量中设置密钥。

export OPENAI_API_KEY="sk-你的密钥"

或者在 Python 项目入口处直接写(仅开发测试用,切勿提交到版本库):

import os
os.environ["OPENAI_API_KEY"] = "sk-你的密钥"

预期结果:下一步创建 agent 时不会出现 401 鉴权错误。

步骤 3:创建第一个带文件存储能力的 agent

现在我们利用 Letta Code SDK 创建一个 agent,并为其赋予文本文件记录能力。以下代码完整、可运行。

from letta_client import Letta, AgentCreate

# 初始化客户端(默认连接本地服务,也可以指向远程部署的 Letta Server)
client = Letta(base_url="http://localhost:8283")  # 本地 dev server 地址

# 定义 agent 的三个核心记忆块
agent = client.agents.create(
    AgentCreate(
        name="personal_butler",
        model="openai/gpt-4o-mini",             # 可使用任何支持的模型名称
        embedding="openai/text-embedding-3-small",
        context_window_limit=16000,             # 控制上下文总长度
        memory_blocks=[
            {
                "label": "persona",
                "value": "你是用户的私人管家,彬彬有礼且体贴。"
                         "你能记住用户的偏好和历史行为,并据此提供个性化建议。",
                "description": "定义 agent 的身份和语气,不要修改此块内容。",
                "limit": 500  # 该块最大 tokens 数
            },
            {
                "label": "human",
                "value": "用户叫托尼,刚搬来本市,正在适应新环境。",
                "description": "存储关于用户的信息、偏好和交互历史。",
                "limit": 1000
            },
            {
                "label": "scratchpad",
                "value": "",
                "description": "用作临时工作空间,存放当前任务所需的中间信息。",
                "limit": 800
            }
        ],
        enable_sleeptime=True,                  # 开启后台记忆整理
        tools=["send_message", "file_read"],     # 允许读文件(后面会演示文件存储)
    )
)

print(f"Agent 创建成功,ID:{agent.id}")

预期结果:控制台打印出类似 Agent 创建成功,ID:agent-xxxx 的消息。至此,一个拥有 persona、human 和 scratchpad 三块结构化记忆,并且能读取文件的 agent 就在后端就绪了。

重要提示
必须将 agent.id 保存下来(例如写入本地文件或数据库),这个 ID 是恢复该 agent 全部状态的唯一凭证。丢失 ID 就等于丢失了 agent 积累的所有记忆。

步骤 4:发送第一条消息,观察记忆写入

# 使用 agent.id 恢复默认会话(SDK 会自动保持状态)
session = client.sessions.create(agent_id=agent.id)

# 发送一句话,其中隐含了用户偏好
response = client.sessions.send(
    session_id=session.id,
    messages=[
        {"role": "user", "content": "托尼喜欢在运动后喝椰子水,而且要冰的。"}
    ]
)

# 打印 agent 的回复,并检查 human 记忆块是否自动更新
print("Agent 回复:", response.messages[-1]["content"])

# 查看记忆块变化
agent_state = client.agents.get(agent.id)
for block in agent_state.memory.blocks:
    if block.label == "human":
        print("human 块当前内容:", block.value)

预期结果:Agent 会做出礼貌回应,并且在 human 块中,value 字段会新增一条关于“运动后喝冰椰子水”的信息。Letta 框架自动完成了从对话到结构化记忆的萃取与写入——这就是 MemGPT 操作系统思维的具体工程实现。


二、理解 Memory Blocks 的概念与运作

上面这段代码背后,藏着一个容易被忽视的设计抉择,它正是 Letta 与大多数 LLM 框架在上下文治理上的根本差异。

经典做法是把整个对话历史一股脑塞进 context window,然后祈祷模型能“通读并记住一切”。MemGPT 论文已经证明这条路在长程交互中不可靠。而 Letta 给出的方案是:把上下文拆成若干有名称、有用途、有生命周期的记忆块,让模型在每次推理时只加载相关块,并按照块的 description 来决定如何读写

在我们创建的 agent 里,三个块的角色分别是:

记忆块 用途 读写规则(由 description 引导)
persona 定义 agent 身份、语气和行为边界 不应被 agent 修改,保持稳定
human 存储从对话中学习到的用户信息 agent 应按需追加和更新
scratchpad 当前任务的工作记忆(类似草稿纸) agent 可自由读写,任务完成后可清空

这些块的写入和读取并非由开发者手动调用 API 完成,而是 agent 在对话循环中,根据每个块的 description 和自身推理结果,决定是否向某个块写入新内容、是否在生成回复前读取某个块的当前值。这意味着你对 description 的编写质量,直接决定了记忆系统的行为是否正确。

举个例子,如果你在 personadescription 中忘了写“不要修改”,agent 可能在长期交互中逐渐改变自己的角色定义;或者如果在 humandescription 中未明确“记录用户偏好”,agent 可能只是礼貌回应,却从不记录任何信息。这是从“代码逻辑治理上下文”向“声明式规则治理上下文”的一次关键跃迁。

另外,每个记忆块的 limit 参数相当于那本“记忆书”的厚度上限,当写入内容超过限制时,Letta 的底层机制会触发总结或遗忘策略,防止无限制膨胀。而 enable_sleeptime=True 会在对话间隙让 agent 自己整理记忆,合并冗余、提取要点,这恰好复现了 MemGPT 论文中的“后台进程”概念,只是这里被封装成了一个配置开关。

可解释性优势
在调试 agent 行为时,你可以随时调用 client.agents.get(agent.id) 查看所有记忆块的当前值,这与查看“系统配置文件”一样直观。不会有黑盒的向量检索结果混在上下文中,每一段记忆的来源和用途都一目了然。


三、使用 Letta API 进行持久化会话管理

了解记忆块机制后,我们来完成一个更接近真实应用的例子:实现一次跨多天的多轮对话,并验证 agent 的上下文无缝延续。

场景设计

假设托尼在周一向私人管家 agent 提出了一个旅行计划,周三他又回来继续讨论。

周一:提出旅行偏好
周三:直接问“我之前说的那件事怎么样了”,看 agent 是否回忆得起。

步骤 1:模拟第一天的对话(周一)

在 Python 脚本 monday.py 中写入:

from letta_client import Letta, AgentCreate
import json

client = Letta(base_url="http://localhost:8283")

# 创建 agent(同上,简化起见这里写固定代码,或用预先保存的 ID 恢复)
agent = client.agents.create(
    AgentCreate(
        name="travel_planner",
        model="openai/gpt-4o-mini",
        embedding="openai/text-embedding-3-small",
        context_window_limit=16000,
        memory_blocks=[
            {"label": "persona", "value": "你是一名细致周到的旅行规划助理。",
             "description": "定义角色,请勿修改。"},
            {"label": "human", "value": "托尼喜欢深度游,偏爱小众景点和本地美食。",
             "description": "学习并保存用户的旅行偏好和已确认的计划。"}
        ],
        enable_sleeptime=True,
    )
)

agent_id = agent.id
print("保存此 ID 以便后续恢复:", agent_id)

# 开始第一段会话
session = client.sessions.create(agent_id=agent_id)
response = client.sessions.send(
    session_id=session.id,
    messages=[{"role": "user", "content": "我想在三月去西班牙旅行,重点看高迪的建筑,并找到塞维利亚最好的 tapas。"}]
)
print(response.messages[-1]["content"])

# 将 agent_id 存储到文件,模拟“跨天”场景
with open("agent_id.json", "w") as f:
    json.dump({"agent_id": agent_id}, f)

预期结果:Agent 会给出旅行建议。在对话结束后,human 记忆块应已自动记录下“三月西班牙旅行、高迪建筑、塞维利亚 tapas”等关键信息。

步骤 2:模拟隔日恢复对话(周三)

在另一个脚本 wednesday.py 中写入:

import json
from letta_client import Letta

client = Letta(base_url="http://localhost:8283")

with open("agent_id.json", "r") as f:
    agent_id = json.load(f)["agent_id"]

# 直接恢复该 agent 的默认会话(也可创建新会话,但不影响记忆)
session = client.sessions.create(agent_id=agent_id)  # 创建新对话线程,但记忆块仍然持久化

# 发送上下文稀疏的消息,考验记忆
response = client.sessions.send(
    session_id=session.id,
    messages=[{"role": "user", "content": "上次说的那趟西班牙之旅,我想再加两天巴塞罗那的行程。"}]
)
print(response.messages[-1]["content"])

# 检查 human 块是否还记得周一的内容
agent_state = client.agents.get(agent_id)
for block in agent_state.memory.blocks:
    if block.label == "human":
        print("当前 human 块内容:\n", block.value)

预期结果:Agent 的回复会自然接续上周的对话,比如会说“好的托尼,我会在原有高迪建筑主题的基础上增加巴塞罗那两日行程。”查看 human 块,会发现“三月西班牙旅行、塞维利亚 tapas”等信息依然存在,并可能新增了“加两天巴塞罗那”。整个过程无需重传历史消息,也无需手动拼接上下文。

步骤 3:验证会话隔离

如果你在多用户或多话题场景下担心消息串话,可以主动创建多个会话(Conversation),并分别发送消息,记忆块的内容依然在所有会话间共享(除非你设计了不同的 agent 实例)。

session_a = client.sessions.create(agent_id=agent_id)
session_b = client.sessions.create(agent_id=agent_id)

# 两个会话并发发送消息,后端会自动管理上下文路由

预期结果:Agent 在处理 session_a 的消息时,会看到自己先前在 session_a 中的历史,不会混入 session_b 的消息,但共享的 human/persona 记忆块对所有会话统一生效。这恰好模拟了真实场景中一个用户多端登录的情况:底层记忆一致,上层对话隔离。

架构提示
这种设计意味着 agent_id 应和一个特定的用户或项目绑定,不要为每一个临时的帮助请求创建新 agent——agent 是记忆的持久化载体,不是无状态函数。如果你需要为一个全新用户服务,再创建另一个 agent。


回顾与行动清单

我们做了什么
在约 30 分钟内,从安装 Letta SDK 开始,创建了一个拥有 persona、human、scratchpad 三个记忆块的 agent,让它学会从对话中萃取用户信息并持久存储,然后分两次模拟了跨天的多轮对话,验证了上下文的无缝延续。最重要的是,我们理解了 Letta 如何将 MemGPT 的虚拟记忆管理思想工程化为清晰的配置项和 API。

花了多久:核心编码约 15 分钟,大部分时间用于理解记忆块设计和验证跨会话行为。

现在,你可以:

  1. 在项目里使用 Letta SDK 替代简单的消息历史拼接,实现真正“有记忆”的 agent。
  2. 根据业务需要,自行设计新的记忆块标签(例如 tasks 块用于跟踪待办列表,knowledge 块存放领域知识),并用 description 精细控制 agent 的读写行为。
  3. 实验 enable_sleeptime 和修改 context_window_limit,感受上下文回收的实际效果。
  4. 尝试接入本地模型而非 OpenAI,测试记忆块机制在低算力环境下的表现。

下一章——《Memory Blocks 是实现可解释记忆模块的终极方法》——将沿着本章开启的路径,深入拆解这些记忆块的内部结构与治理逻辑。我们会看到,当记忆被显式地结构化、可审计、可编辑时,agent 就不再只是“更长的上下文窗口”,而是真正向“可信赖的长期伙伴”迈出了实质性一步。别停下,我们马上进入下一段旅途。

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
讨论数量: 0
发起讨论 只看当前版本


暂无话题~