用 Letta 构建具备持久记忆的工程助手
教程介绍如何利用 Letta 构建有状态工程助手,涵盖可检查的核心记忆、向量检索和多智能体共享状态。还涉及自定义工具、流式输出及可迁移工作流的组织方式。
AI 深度解读
工程 Copilot 最容易丢掉的,往往不是代码语法,而是项目已经做过的决定:哪个方案试过无效、当前 CI 是否通过、上线前要遵守什么规则。MarkTechPost 这篇 Letta 教程给出的思路,是把这些信息变成可检查、可修改、可共享的持久状态。
它的价值在于串起了一套完整流程:核心记忆保存当前事实,归档记忆检索历史材料,工具负责确定性计算,再用消息历史和文件导出延续工作。不过,示例能说明这些组件如何配合,还不足以证明它们在真实团队里更可靠。
记住聊天记录,为什么还不够
聊天历史记录「发生过什么」,核心记忆记录「现在应该知道什么」。两者不能互相替代。
教程虚构了一位工程师 Priya:她在 Northwind 负责 Rust 数据摄取服务 Halo,计划 Nov 14 发布,喜欢要点和代码,不喜欢长篇解释。项目的 Kafka consumer 在负载下约每 4 分钟发生一次 rebalance。随后,她补充了排查结果:max.poll.interval.ms 被设为 5s,慢批次提交阻塞了 poll loop;团队决定把提交移出循环,而且调高 session.timeout.ms 已经试过无效。
这些信息需要分开保存。个人偏好属于 human,助手的行为约定属于 persona,阻塞项与技术决定属于 project_state。其中 persona 的容量上限设为 3000。
核心记忆的作用,是让当前有效事实持续进入上下文,而不必每次重读整段对话。
作者还演示了从程序直接更新记忆:先通过 get_block 读取 project_state,再用 set_block 追加一条 CI 消息,记录 [CI 09:41Z] 的 main 状态为 RED,integration/kafka_rebalance_test 已经连续不稳定 3 次。下一轮对话时,助手就有机会结合外部系统刚写入的状态回答。
这里有个重要区别:记忆不再只由模型整理,应用代码也能同步事实。但示例采用「读取后追加再写回」,没有展示并发更新、过期信息清理或冲突处理。
多个 Agent 共用记忆,比互相转述更直接
教程让 Copilot 提方案,让 reviewer 审查,两者挂载同一个 team_scratchpad。Copilot 先写入最多 4 行的修复建议,reviewer 再读取并追加结论。
它们共享的是同一个 block,而非各自保存一份聊天摘要。team_scratchpad 的容量上限为 4000,描述中要求双方写入时标注名字。
同时,团队规则被放进另一个共享 block:
policies = client.blocks.create(
label="policies",
description="Non-negotiable engineering policies. Read-only: obey, never edit.",
value="1. No direct pushes to main.\n2. Every fix ships with a regression test.\n"
"3. Customer data never leaves EU regions.",
limit=2000,
read_only=True,
)
这段代码解决规则被 Agent 改写的问题,关键在 read_only=True。规则包括禁止直接推送 main、修复必须带回归测试、客户数据不得离开 EU 区域。
不过,只读规则不等于强制执行规则。 block 不能改,不代表 Git 推送权限和数据出口已经被限制。真实系统仍需要在仓库权限、工具权限和基础设施层执行约束。
长期资料则进入归档向量记忆,通过 client.agents.passages.insert 写入,再用 client.agents.passages.search 做语义检索。教程灌入了 6 条带标签的材料,包括:
- 2024-08-02 的事故记录:部署期间 rebalance 导致丢失 1.2M 条事件,原因涉及 poll loop 内的提交阻塞。
- ADR-017:采用 at-least-once,以
(tenant_id, event_id)做幂等写入;因吞吐考虑放弃 Kafka transactions 的 exactly-once 方案。 - 运维手册:先设
DRAIN=1,等待lag<1000,再发SIGTERM,不能直接SIGKILL。 - 2025-01 的基准材料:rdkafka 2.3、8 partitions、c6i.4xlarge,记录吞吐 240k events/s、p99 32ms,瓶颈为 JSON parsing。
- Priya 的目录偏好:按 bounded context 划分模块,集成测试靠近模块,避免巨大的
tests/目录。 - 2025-03-19 的事故记录:EU 租户数据被镜像到 us-east-1 持续 40 分钟,后来通过固定 DLQ 区域修复。
这些数字都是教程写入知识库的示例内容,不是 Letta 的性能测试结果。作者用「部署时丢消息」这样的自然语言查询验证检索路径,但没有提供命中率或检索质量评估。
工具负责算数,持久化负责接着干
模型适合理解问题,但需要稳定计算结果时,可以调用普通函数。教程注册了下面这个发布风险工具:
def estimate_release_risk(open_bugs: int, days_to_deadline: int, test_coverage: float) -> str:
"""Estimate the risk of shipping a release on time.
Args:
open_bugs (int): Number of open P0/P1 bugs.
days_to_deadline (int): Calendar days left before the ship date.
test_coverage (float): Line coverage as a fraction between 0.0 and 1.0.
Returns:
str: A risk score from 0-100 and a verdict.
"""
import math
pressure = open_bugs / max(days_to_deadline, 1)
score = max(0.0, min(100.0, 100 * (1 - math.exp(-1.4 * pressure)) * (1.3 - test_coverage)))
verdict = "SHIP" if score < 35 else ("SLIP ONE WEEK" if score < 70 else "DO NOT SHIP")
return f"risk_score={score:.1f} verdict={verdict}"
注意 score 和 verdict 两行:公式、系数与 35、70 两个阈值都由开发者设定。确定性只保证相同输入得到相同输出,不保证风险判断准确。
工具通过 client.tools.upsert_from_function 注册,再用 client.agents.tools.attach 挂载。作者输入 9 个未关闭的 P0/P1 bug、距离 Nov 14 还有 12 天、覆盖率 0.61,要求 Agent 调工具,并把结论写回 project_state。原文没有展示实际返回值,也没有解释公式如何校准。
后续能力围绕「任务如何延续」展开:client.agents.messages.stream 输出 reasoning_message、tool_call_message 和 assistant_message,方便观察执行过程;client.agents.messages.list 用 limit=8 读取服务端历史。新进程在配置好客户端后,可以凭 agent_id 继续调用 client.agents.messages.create。
导出则使用 client.agents.export_file,保存为 copilot.af;存在 /content 时写入 /content/copilot.af。教程声称文件携带记忆、工具和历史,并给出 client.agents.import_file(file=open(path,'rb')) 的导入方式,但没有展示导入后的验证过程。清理开关 CLEANUP=False,默认保留 Agent 和共享 block。
我的判断
我认同这套分层:把当前事实、历史资料、计算逻辑和交互记录分开,比不断往提示词里塞聊天摘要更容易检查,也更容易定位错误。
但它首先是一份能力串联教程。原文没有与普通聊天、摘要记忆或常见 RAG 方案比较,也没有给出成本、延迟、长期记忆准确率和多 Agent 协作收益。这些收益目前只能视为设计判断,不能当作实测结论。
代码的可复现性也要打问号。安装使用 pip install -q -U letta-client,没有锁定 SDK 版本;客户端通过 LETTA_API_KEY、可选的 LETTA_BASE_URL 配置,并尝试 api_key、token 两种参数。模型按 openai/gpt-4.1、openai/gpt-4o-mini、anthropic/claude-sonnet-4-20250514 的顺序选择,embedding 尝试使用 openai/text-embedding-3-small。
大量兼容分支通过 flex 捕获所有异常再尝试另一种调用。这能适配部分接口差异,也可能把鉴权或网络错误混进兼容问题。原文首段代码还在 human block 中途截断,缺少 dump_memory、get_block、set_block 的定义,不能直接拼起来运行。导出和流式接口同样被包在异常处理中,部署是否支持仍需确认。
另外,共享便签没有展示锁、版本控制或审阅状态;Agent 自动更新记忆,也没有展示错误事实如何撤销。相比单 Agent 加显式任务状态的方案,这套设计增加了协作能力,也增加了状态治理成本。
能拿来做什么
- 为日常 AI 编程助手先整理少量稳定事实,例如仓库约定、当前阻塞项和已经失败的方案,并让这些内容可以人工检查和纠正。
- 从 Node 服务或 CI 接入构建状态时,保留时间与来源,定义旧状态如何被替换,避免只向记忆无限追加日志。
- 把事故复盘、ADR 和 runbook 放入检索层,先用已有答案的问题验证召回效果,再考虑接入操作流程。
- 将发布检查中的确定性计算封装成工具,并把计算结果与发布权限分开处理;需要双 Agent 审查时,再补充共享状态的写入顺序与冲突规则。