我们如何构建 Agent Builder 的记忆系统
URL: https://blog.langchain.com/how-we-built-agent-builders-memory-system/
我们如何构建 Agent Builder 的记忆系统
记忆系统是 Agent Builder 的核心组成部分。在本文中,我们将阐述优先开发记忆系统的理由、技术实现细节、构建过程中的经验教训、记忆系统带来的能力,并讨论未来的工作方向。
阅读时间: 9 分钟
发布日期: 2026 年 2 月 21 日
上个月我们推出了 LangSmith Agent Builder,这是一个无需编码即可构建代理 (agent) 的平台。记忆系统是 Agent Builder 的关键组成部分。在本文中,我们将阐述优先开发记忆系统的理由、技术实现细节、构建过程中的经验教训、记忆系统带来的能力,并讨论未来的工作方向。
什么是 LangSmith Agent Builder
Section titled “什么是 LangSmith Agent Builder”LangSmith Agent Builder 是一个无需编码的代理构建工具。它基于 Deep Agents harness 构建,是一个面向技术背景较弱的公民开发者 (citizen developers) 的托管 Web 解决方案。在 LangSmith Agent Builder 中,构建者可以创建代理来自动化特定工作流程或日常任务的一部分。例如:邮件助手 (email assistant)、文档助手 (documentation helper) 等。
早期我们就有意识地选择将记忆作为平台的核心功能。这并不是一个显而易见的选择——大多数 AI 产品最初发布时都没有任何形式的记忆功能,即使后来添加了记忆功能,也尚未像一些人预期的那样为产品带来变革。我们之所以优先考虑记忆功能,是基于用户的使用模式。
与 ChatGPT、Claude 或 Cursor 不同,LangSmith Agent Builder 不是通用代理。它专门设计用于让构建者为特定任务定制代理。在通用代理中,用户执行的任务种类繁多且可能完全不相关,因此一次会话中的学习成果可能不适用于下一次会话。而当 LangSmith Agent 执行任务时,它会一遍又一遍地执行相同的任务。一次会话中的经验教训会以更高的比率转化到下一次会话中。事实上,如果没有记忆功能,用户体验会很糟糕——这意味着你不得不在不同会话中反复向代理重复相同的信息。
在思考记忆对 LangSmith Agents 究竟意味着什么时,我们参考了第三方的记忆定义。COALA 论文将代理记忆分为三类:
- 程序性记忆 (Procedural): 可应用于工作记忆以确定代理行为的一组规则
- 语义记忆 (Semantic): 关于世界的事实
- 情景记忆 (Episodic): 代理过去行为的序列
我们如何构建记忆系统
Section titled “我们如何构建记忆系统”在 Agent Builder 中,我们将记忆表示为一组文件。这是一个有意的选择,目的是利用模型擅长使用文件系统 (filesystems) 这一特性。通过这种方式,我们可以轻松让代理读取和修改其记忆,而无需提供专用工具——我们只需赋予它访问文件系统的权限即可!
在可能的情况下,我们尽量使用行业标准。我们使用 AGENTS.md 来定义代理的核心指令集 (instruction set)。我们使用代理技能 (agent skills) 为代理提供针对特定任务的专门指令。虽然没有子代理 (subagent) 标准,但我们使用了与 Claude Code 类似的格式。对于 MCP 访问,我们使用自定义的 tools.json 文件。之所以使用自定义的 tools.json 而不是标准的 mcp.json,是因为我们希望允许用户只给代理提供 MCP 服务器中的一部分工具,以避免上下文溢出 (context overflow)。
实际上,我们并不使用真实的文件系统来存储这些文件。而是将它们存储在 Postgres 中,并以文件系统的形式暴露给代理。这样做是因为 LLM 非常擅长处理文件系统,但从基础设施角度来看,使用数据库更简单、更高效。这种”虚拟文件系统”由 DeepAgents 原生支持——并且完全可插拔,因此你可以使用任何存储层(如 S3、MySQL 等)。
我们还允许用户(以及代理本身)将其他文件写入代理的记忆文件夹。这些文件可以包含任意知识,代理在运行时可以参考。代理会在工作过程中编辑这些文件,即在”热路径”(in the hot path) 中进行编辑。
之所以能够在没有任何代码或领域特定语言 (DSL) 的情况下构建复杂的代理,是因为我们在底层使用了像 Deep Agents 这样的通用代理 harness。Deep Agents 抽象掉了许多复杂的上下文工程(如摘要 summarization、工具调用卸载 tool call offloading 和规划 planning),让你能够通过相对简单的配置来引导代理。
这些文件很好地映射到了 COALA 论文中定义的记忆类型。程序性记忆——驱动核心代理指令的内容——是 AGENTS.md 和 tools.json。语义记忆是代理技能和其他知识文件。唯一缺失的记忆类型是情景记忆,我们认为对于这类代理来说,情景记忆不如其他两种类型重要。
文件系统中的代理记忆是什么样的
Section titled “文件系统中的代理记忆是什么样的”我们可以看看一个内部使用的真实代理案例——一个 LinkedIn 招聘代理,它基于 LangSmith Agent Builder 构建。
- AGENTS.md: 定义核心代理指令
- subagents/: 仅定义一个子代理
linkedin_search_worker: 主代理校准搜索后,会启动这个代理来寻找约 50 名候选人
- tools.json: 定义一个 MCP 服务器,可访问 LinkedIn 搜索工具
记忆中目前还有另外 3 个文件,代表不同候选人的职位描述 (JDs)。随着我们在这个搜索任务上与代理合作,它已经更新并维护了这些 JDs。
记忆编辑如何工作:具体示例
Section titled “记忆编辑如何工作:具体示例”为了让记忆的工作原理更加具体,我们来看一个说明性示例。
开始:
你从一个简单的 AGENTS.md 开始:
总结会议笔记。第 1 周:
代理生成段落式摘要。你纠正它:“使用项目符号而不是段落。“代理将 AGENTS.md 编辑为:
# 格式偏好用户更喜欢用项目符号进行总结,而不是段落。第 2 周:
你让代理总结另一个会议。它读取记忆并自动使用项目符号。无需提醒。在这次会话中,你要求它:“在末尾单独提取行动项。“记忆更新为:
# 格式偏好用户更喜欢用项目符号进行总结,而不是段落。在末尾单独提取行动项。第 4 周:
两种模式都自动应用。随着新的边缘情况出现,你继续添加改进。
第 3 个月:
代理的记忆包括:
- 不同文档类型的格式偏好
- 领域特定术语
- “行动项”、“决策”和”讨论要点”之间的区别
- 频繁参会者的姓名和角色
- 会议类型处理(工程 vs 规划 vs 客户)
- 通过积累使用得到的边缘情况修正
记忆文件可能如下所示:
# 会议总结偏好
## 格式- 使用项目符号,而不是段落- 在末尾单独提取行动项- 对决策使用过去时- 在顶部包含时间戳
## 会议类型- 工程会议:突出技术决策和理由- 规划会议:强调优先级和时间表- 客户会议:隐藏敏感信息- 短会议 (<10 分钟):只记录要点
## 人员- Sarah Chen (工程负责人) - 关注技术细节- Mike Rodriguez (产品经理) - 关注业务影响...AGENTS.md 通过纠正自行构建,而不是通过预先编写的文档。我们迭代地得出了适当详细的代理规范,而用户从未手动更改过 AGENTS.md。
构建此记忆系统的经验教训
Section titled “构建此记忆系统的经验教训”在此过程中,我们学到了几个教训。
最难的部分是提示词 (prompting)
Section titled “最难的部分是提示词 (prompting)”构建能够记忆的代理时,最难的部分是提示词。在几乎所有代理表现不佳的情况下,解决方案都是改进提示词。以下是一些通过这种方式解决的问题示例:
- 代理在应该记忆的时候没有记忆
- 代理在不应该记忆的时候记忆了
- 代理向
AGENTS.md写入了太多内容,而不是写入技能文件 - 代理不知道技能文件的正确格式
- ……还有很多其他问题
我们曾有一人全职从事记忆相关的提示词工作(这占了团队的很大比例)。
验证文件类型
Section titled “验证文件类型”几个文件需要遵循特定的模式 (schemas)(tools.json 需要有有效的 MCP 服务器,技能文件需要有正确的前端元数据 frontmatter 等)。我们发现 Agent Builder 有时会忘记这一点,从而生成无效文件。我们添加了一个步骤来明确验证这些自定义结构,如果验证失败,则将错误返回给 LLM,而不是提交文件。
代理擅长添加内容,但不擅长压缩
Section titled “代理擅长添加内容,但不擅长压缩”代理在工作时会编辑其记忆。它们相当擅长向文件添加具体内容。然而,它们不擅长意识到何时应该压缩学习成果。例如:我的邮件助手一度开始列出它应该忽略的所有具体供应商的冷邮件,而不是更新自己以忽略所有冷邮件。
作为最终用户,显式提示有时仍然有用
Section titled “作为最终用户,显式提示有时仍然有用”即使代理能够在工作过程中更新其记忆,但在某些情况下,作为最终用户,我们发现显式提示代理管理其记忆仍然很有用。一种情况是在工作结束时让代理反思对话并更新记忆,以补充可能遗漏的内容。另一种情况是提示代理压缩其记忆,以解决它记住具体案例但没有进行概括的问题。
人在回路 (Human-in-the-loop)
Section titled “人在回路 (Human-in-the-loop)”我们让所有记忆编辑都采用人在回路模式——也就是说,在更新之前需要明确的人工批准。这主要是为了最大限度地减少提示注入 (prompt injection) 的潜在攻击向量。我们确实提供了一种方式让用户关闭此功能(“yolo 模式”),适用于那些不太担心此问题的情况。
这带来了什么能力
Section titled “这带来了什么能力”除了更好的产品体验之外,以这种方式表示记忆还实现了许多功能。
无需编码的体验
Section titled “无需编码的体验”无需编码构建工具的一个问题是,它们要求你学习不熟悉的 DSL,而这种 DSL 无法很好地随复杂度扩展。通过将代理表示为 markdown 和 json 文件,代理现在采用了 (a) 对大多数技术背景较弱的人来说熟悉的格式,(b) 更具可扩展性的格式。
更好的代理构建体验
Section titled “更好的代理构建体验”记忆实际上允许更好的代理构建体验。代理构建是非常迭代的——在很大程度上是因为在尝试之前你不知道代理会做什么。记忆使迭代更容易,因为你无需每次都手动更新代理配置,只需以自然语言提供反馈,它就会自行更新。
可移植的代理
Section titled “可移植的代理”文件非常便于移植!这使你可以轻松地将 Agent Builder 中构建的代理移植到其他 harness(只要它们使用相同的文件约定)。出于这个原因,我们尽量使用尽可能多的标准约定。我们希望让在 Agent Builder 中构建的代理能够轻松用于 Deep Agents CLI,或者完全用于其他代理 harness,如 Claude Code 或 OpenCode。
有很多记忆功能的改进我们想要实现,但在发布之前我们没有足够的时间或信心来完成。
Agent Builder 缺少的一种 COALA 记忆类型是情景记忆:代理过去行为的序列。我们计划通过将之前的对话作为文件暴露在文件系统中来实现这一点,代理可以与这些文件交互。
后台记忆进程
Section titled “后台记忆进程”目前,所有记忆都在”热路径”中更新;也就是说,在代理运行时更新。我们想添加一个在后台运行的进程(可能是一个 cron 作业,每天运行一次左右),以反思所有对话并更新记忆。我们认为这将捕捉代理在当时未能识别的项目,并且对于概括具体的学习成果特别有用。
/remember 命令
Section titled “/remember 命令”我们想暴露一个显式的 /remember 命令,这样你就可以提示代理反思对话并更新其记忆。我们发现自己偶尔会这样做,并获得了很大的好处,因此想让这变得更容易、更受鼓励。
虽然能够使用 glob 和 grep 搜索记忆是一个很好的起点,但在某些情况下,允许代理对其记忆进行语义搜索会带来一些增益。
不同级别的记忆
Section titled “不同级别的记忆”目前,所有记忆都是特定于该代理的。我们没有用户级或组织级记忆的概念。我们计划通过向代理暴露代表这些记忆类型的特定目录来实现这一点,并提示代理使用和更新这些记忆。
如果构建具有记忆的代理听起来很有趣,请试用 LangSmith Agent Builder。如果你想帮助我们构建这个记忆系统,我们正在招聘。
加入我们的通讯
获取 LangChain 团队和社区的更新
(订阅表单略)