Letta 深度拆解:Agent 怎样用 Git 维护自己的长期记忆?
以当前 Letta Code 为入口,追踪文件记忆、后台更新和上下文编译,理解“已经记下”与“本轮用上”之间的距离。
博客本文目录
Memory Agent 深度拆解 · 第 3 / 8 篇。查看系列目录。
你刚刚纠正了编程助手:“这个项目必须先跑迁移,再切流量。”
它回答“记住了”,还把这句话写进文件。下一轮发布,却依然按旧顺序执行。
这时最值得追问的,已经不是文件有没有保存,而是那份文件有没有进入下一次模型请求。
Letta 的记忆设计正好可以用来拆这个问题。它让 Agent 维护自己的上下文,并用 Git 管理这些内容,但保存、同步、重新编译和真正使用,各有边界。
本文以 2026 年 9 月 22 日的 letta-code 提交 1466db908532 为依据,未部署复测。历史 letta-ai/letta 仓库已将活跃源码指向这里,旧 V1 服务保留在 archive 分支。官方入口说明
1. 记忆属于 Agent,窗口只使用其中一部分
一段会话可以结束,但 Agent 的身份、偏好与工作经验需要继续存在。
当前 Letta 用 MemFS 组织这部分状态。记忆保存在 Agent 对应的 Git 仓库中,再投影到它正在工作的电脑上,成为能够用文件工具读写的实际 checkout。官方 MemFS 文档
这带来两个重要分离:Agent 与某一次聊天分离,长期记忆与当前上下文分离。
如果所有记忆每轮都完整塞入提示词,文件越来越多,输入也会越来越贵。于是记忆还要分成经常使用的核心内容与按需读取的资料。前者影响日常行为,后者保存细节。
当前概念文档用 system/ 目录说明常驻内容;源码中也存在不同运行形态的投影与提示词模板。理解设计时应抓住“核心与外部内容分层”,不要把一份示意目录当成所有版本的固定布局。
2. 核心记忆,实际上在参与提示词编译
本地 MemFS 提示词把 memory blocks 描述为可编辑的系统提示片段,也要求它们保持精简。可通过查历史或重读源文件得到的内容,不适合无限堆进核心块。源码:本地 MemFS 提示词
这里很像给长期同事准备两类材料:一张必须记住的工作说明,以及一个随时可以翻的项目档案柜。
“输出尽量用中文”“发布前必须检查迁移”适合进入简短工作说明;一次故障的几百行日志,适合留在外部资料里,并在核心内容中放置可用的检索线索。
这项选择直接决定记忆质量。放得太少,Agent 不知道什么时候该找;放得太多,最重要的规则会被大量细节稀释,还会增加每次调用的成本。
因此,记忆整理的目标不是让文件越来越长,而是让后续任务更容易找到正确的信息。
3. 修改文件之后,至少还有两个边界
源码提示明确区分本地修改、Git 提交与未来上下文生效。一次记忆编辑,并不会倒过来改变正在执行的这一轮模型输入。源码:记忆编辑约定
可以把链路理解成:
经验或用户纠正
↓
修改记忆文件
↓
提交为可追踪版本
↓
需要时同步到其他运行位置
↓
重新编译会话的系统提示
↓
后续模型请求采用新内容
上图是对几个状态边界的整理,不代表所有运行模式都调用同一段同步代码。
对于本地 Agent,提交保存在本机;云端支持的 Agent 还涉及远端仓库和其他机器的同步。Git 让“改了什么”可追踪,但不保证“每台机器此刻都在使用同一个提示词快照”。
排查遗忘时,我会依次核对文件内容、提交版本、同步结果、编译后的提示,以及最终请求。单看第一项,很容易误判为模型不听话。
4. 后台记忆整理结束,主 Agent 如何知道?
Letta 的后台记忆任务可以初始化或反思整理记忆。主流程中的 handleMemorySubagentCompletion() 会在成功且未跳过重编译时,重新编译对应会话的系统提示。源码:记忆任务完成处理
这段代码值得看一个并发细节:如果同一会话已有重编译正在进行,后来的完成事件不会各自无序启动一份,而是标记后续还需要再编译。当前过程结束后,检查标记并继续处理。
为什么需要这样做?设想第一次编译刚开始,第二个后台任务又提交了更新。如果简单地说“已经在编译,忽略”,第二份内容可能没有进入结果;如果所有编译并发完成,又需要面对结果覆盖顺序。
这个处理将“合并重复工作”与“保留最后一次更新需求”结合起来。仓库还包含测试,覆盖同会话连续完成事件和不同会话不应被合并等情况。上游测试代码
本文阅读了这些测试,没有执行它们。它们证明项目明确表达了这些预期,不等于本次已经验证所有运行路径。
5. 同步失败时,系统可能继续工作
监听模式下,MemFS 同步在需要时触发。ensureMemfsSyncedForAgent() 用 Agent ID 复用进行中的同步 promise;失败时记录警告、移除缓存,使下一轮有机会重试,并返回失败状态。源码:监听模式的记忆同步
这里的取舍很实际:记忆同步异常,不一定让整个助手彻底停止响应。
但应用必须能区分“正常带记忆工作”和“记忆不可用时继续响应”。否则用户看到的都是一条流畅回答,却不知道它已经换了一套上下文条件。
类似地,完成处理代码还会分别报告记忆任务结果与提示词重新编译失败。把两个阶段分开,可以避免一句“已记住”掩盖后续生效失败。
6. 用 Git 管记忆,得到什么,又没得到什么?
最直观的是差异与版本:一次规则为什么出现,什么时候被修改,可以沿提交追溯。不同运行位置也有统一的同步载体。
不过 Git 解决的是内容版本,不直接判断语义是否正确。它能记录 Agent 把“这次临时跳过检查”写成“以后都跳过检查”,却不会自动指出这是错误概括。
所以我会给经验记忆保留适用条件:在哪个项目、什么环境、什么证据下成立。将一次成功变成永久规则之前,还要检查它是不是偶然结果。
当前 MemFS 默认没有语义向量索引,普通文件工具是基础路径;可选搜索扩展与会话历史搜索有各自要求。官方检索说明
这说明文件组织能力本身就是检索质量的一部分。目录名、简短说明与入口索引写得好,Agent 才容易知道往哪里翻。
7. 自我修改,怎样避免越改越乱?
先把“记住用户偏好”与“改变长期工作规则”分开。前者更新一个事实,后者可能影响未来许多任务,需要更明确的依据和可撤回性。
再把“用户明确要求”与“Agent 自己归纳”分开。来自一次失败的经验,应标出条件和来源;敏感数据也不应因为便于回忆就写进可能同步的 Git 仓库。
我会用一个很小的验证流程:提出规则、检查提交、启动独立会话、查看实际提示、执行一个会触发该规则的任务,最后修改规则并重复。再加一组后台同时整理记忆的并发情况。
验收终点应该是行为有证据地变化,而不仅是界面里多了一条漂亮笔记。
8. Letta 适合什么样的长期工作?
它更适合需要连续身份、工作约定和可积累经验的 Agent,例如持续参与同一个项目的助手。接入时也意味着你要认真管理 Agent 状态、文件版本和上下文编译。
它给我的最大启发是:长期记忆可以成为一份持续维护、可追踪的工作上下文。 当助手说“下次会记得”,我们终于有了一条能检查的生效链路,而不必只相信这句承诺。
系列阅读
上一篇:Graphiti 深度拆解。
下一篇:OpenViking 深度拆解。