摘要

1) 一句话总结

LangSmith Agent Builder 通过构建基于 Postgres 的虚拟文件系统(使用 Markdown 和 JSON 文件),实现了一个允许 Agent 在交互中自动迭代和更新指令的无代码记忆系统。

2) 关键要点

  • 定位与框架:Agent Builder 是基于 Deep Agents 框架的无代码工具,专为特定任务定制,记忆功能可避免用户在不同会话中重复输入相同指令。
  • 存储机制:记忆被设计为“虚拟文件系统”,底层存储在 Postgres 数据库中,但对 Agent 暴露为文件,以充分利用 LLM 擅长处理文件系统的优势。
  • 文件规范:采用标准或类标准格式,包括 AGENTS.md(核心指令)、技能文件(特定任务指令)和自定义的 tools.json(控制 MCP 工具访问以防止上下文溢出)。
  • 记忆分类:当前系统实现了 COALA 论文中的程序性记忆(AGENTS.mdtools.json)和语义记忆(技能与知识文件),Agent 会在运行的“热路径(hot path)”上直接编辑这些文件。
  • 自动迭代体验:用户只需用自然语言提供反馈,Agent 即可自动将规则(如格式偏好、术语)写入记忆文件,无需用户手动修改底层配置或学习领域特定语言(DSL)。
  • 提示词工程投入:提示词编写是构建该系统最困难的部分,团队为此配备了全职人员,以解决 Agent “该记不记”、“记错地方”等问题。
  • 强制格式验证:系统增加了显式的文件格式验证步骤,若 Agent 生成的 JSON 或 Frontmatter 无效,会将错误直接抛回给 LLM 重新处理。
  • 高可移植性:基于文件系统的记忆设计使得 Agent 具有高度可移植性,可轻松迁移至 Deep Agents CLI、Claude Code 等其他兼容文件约定的框架。

3) 风险与不足(基于原文明确提及)

  • 提示词注入风险:为最小化提示词注入的潜在攻击面,记忆编辑默认采用“人类在环(Human-in-the-loop)”机制,需要人工明确批准才能更新(用户也可选择关闭此限制开启“Yolo 模式”)。
  • 缺乏精简压缩能力:Agent 擅长向文件中添加具体内容,但不擅长精简和泛化知识(例如会穷举具体的推销邮件地址,而不是总结出忽略推销邮件的通用规则)。
  • 缺失情景记忆:目前系统尚未实现“情景记忆”(Agent 过去行为的序列),计划未来通过将历史对话暴露为文件来解决。
  • 机制与层级限制:当前记忆仅在运行时更新,缺乏后台反思与整合进程;同时记忆仅限于 Agent 级别,尚未实现用户级别或组织级别的记忆隔离与共享。

正文

Agent Builder 的一个关键部分是它的记忆系统。在本文中,我们将介绍优先考虑记忆系统的基本原理、构建它的技术细节、构建过程中的经验教训、记忆系统所能实现的功能,并讨论未来的工作。

上个月,我们发布了 LangSmith Agent Builder,这是一种构建 Agent(智能体)的无代码方式。Agent Builder 的一个关键部分是它的记忆系统。在本文中,我们将介绍优先考虑记忆系统的基本原理、构建它的技术细节、构建过程中的经验教训、记忆系统所能实现的功能,并讨论未来的工作。

什么是 LangSmith Agent Builder

LangSmith Agent Builder 是一个无代码的 Agent 构建工具。它构建在 Deep Agents 框架之上。这是一个面向技术背景较弱的平民开发者(citizen developers)的托管式 Web 解决方案。在 LangSmith Agent Builder 中,构建者可以创建一个 Agent 来自动化特定的工作流或日常工作的一部分。例如电子邮件助手、文档助手等。

在早期,我们就有意识地选择将记忆作为平台优先考虑的一部分。这并不是一个显而易见的选择——大多数 AI 产品在最初发布时没有任何形式的记忆,甚至后来添加了记忆,也没有像某些人预期的那样彻底改变产品。我们之所以优先考虑它,是因为我们用户的实际使用模式。

与 ChatGPT、Claude 或 Cursor 不同,LangSmith Agent Builder 不是一个通用型 Agent。相反,它是专门为让构建者针对特定任务定制 Agent 而设计的。在通用型 Agent 中,你执行的各种任务可能完全不相关,因此从一次 Agent 会话中获得的经验可能与下一次毫无关联。而当 LangSmith Agent 执行任务时,它是在一遍又一遍地执行相同的任务。一次会话中的经验教训可以以极高的转化率应用到下一次会话中。事实上,如果没有记忆功能,用户体验将会很糟糕——这意味着你必须在不同的会话中向 Agent 一遍又一遍地重复相同的指令。

在思考记忆对 LangSmith Agent 究竟意味着什么时,我们参考了第三方对记忆的定义。COALA 论文将 Agent 的记忆分为三类:

  • 程序性记忆(Procedural):可应用于工作记忆以决定 Agent 行为的一组规则。
  • 语义记忆(Semantic):关于客观世界的事实。
  • 情景记忆(Episodic):Agent 过去行为的序列。

我们是如何构建记忆系统的

在 Agent Builder 中,我们将记忆表示为一组文件。这是一个刻意的选择,旨在利用模型擅长使用文件系统这一事实。通过这种方式,我们可以轻松地让 Agent 读取和修改其记忆,而无需为其提供专门的工具——我们只需赋予它访问文件系统的权限即可!

在可能的情况下,我们尽量使用行业标准。我们使用 AGENTS.md 来定义 Agent 的核心指令集。我们使用 Agent 技能(skills)为 Agent 提供针对特定任务的专门指令。目前还没有子 Agent(subagent)的标准,但我们使用了类似于 Claude Code 的格式。对于 MCP 访问,我们使用自定义的 tools.json 文件。我们之所以使用自定义的 tools.json 文件而不是标准的 mcp.json,是因为我们希望允许用户仅将 MCP 服务器中的一部分工具提供给 Agent,以避免上下文溢出。

实际上,我们并没有使用真实的文件系统来存储这些文件。相反,我们将它们存储在 Postgres 中,并以文件系统的形式暴露给 Agent。我们这样做是因为 LLM 非常擅长处理文件系统,但从基础设施的角度来看,使用数据库更容易且更高效。这种“虚拟文件系统”得到了 Deep Agents 的原生支持,并且是完全可插拔的,因此你可以引入任何你想要的存储层(如 S3、MySQL 等)。

我们还允许用户(以及 Agent 本身)将其他文件写入 Agent 的记忆文件夹中。这些文件也可以包含任意知识,供 Agent 在运行时参考。Agent 会在工作时“在热路径(hot path)上”编辑这些文件。

之所以能够在没有任何代码或任何领域特定语言(DSL)的情况下构建复杂的 Agent,是因为我们在底层使用了像 Deep Agents 这样的通用 Agent 框架。Deep Agents 抽象掉了许多复杂的上下文工程(如摘要生成、工具调用卸载和规划),让你能够通过相对简单的配置来引导你的 Agent。

这些文件很好地映射到了 COALA 论文中定义的记忆类型。程序性记忆——驱动核心 Agent 指令的内容——是 AGENTS.mdtools.json。语义记忆是 Agent 技能和其他知识文件。唯一缺失的记忆类型是情景记忆,我们认为对于这类 Agent 来说,它不如前两者重要。

文件系统中的 Agent 记忆是什么样的

我们可以看看我们内部一直在使用的一个真实的 Agent——一个基于 LangSmith Agent Builder 构建的 LinkedIn 招聘专员。

  • AGENTS.md:定义核心 Agent 指令。
  • subagents/:仅定义了一个子 Agent linkedin_search_worker:在主 Agent 对搜索进行校准后,它将启动此 Agent 来寻找约 50 名候选人。
  • linkedin_search_worker:在主 Agent 对搜索进行校准后,它将启动此 Agent 来寻找约 50 名候选人。(注:原文重复了此项)
  • tools.json:定义了一个具有 LinkedIn 搜索工具访问权限的 MCP 服务器。
  • 目前记忆中还有 3 个其他文件,代表不同候选人的 JD(职位描述)。随着我们与 Agent 合作进行这些搜索,它已经更新并维护了这些 JD。

记忆编辑是如何工作的:一个具体示例

为了让记忆的工作原理更加具体,我们可以通过一个示例来说明。

初始阶段:

你从一个简单的 AGENTS.md 开始:

总结会议纪要。

第 1 周:

Agent 生成了段落式的总结。你纠正它:“请改用要点列表(bullet points)。” Agent 将 AGENTS.md 编辑为:

# 格式偏好
用户更喜欢用要点列表进行总结,而不是段落。

第 2 周:

你要求 Agent 总结另一个会议。它读取了记忆并自动使用了要点列表。无需提醒。在这次会话中,你要求它:“在末尾单独提取行动项(action items)。” 记忆更新为:

# 格式偏好
用户更喜欢用要点列表进行总结,而不是段落。
在末尾的单独部分提取行动项。

第 4 周:

两种模式都会自动应用。随着新的边缘情况出现,你继续添加改进。

第 3 个月:

Agent 的记忆包括:

  • 不同文档类型的格式偏好
  • 特定领域的术语
  • “行动项”、“决策”和“讨论点”之间的区别
  • 经常参加会议的人员姓名和角色
  • 会议类型处理(工程 vs. 规划 vs. 客户)
  • 通过使用积累的边缘情况纠正

记忆文件可能看起来像这样:

# 会议总结偏好
## 格式
- 使用要点列表,不要使用段落
- 在末尾的单独部分提取行动项
- 决策使用过去时态
- 在顶部包含时间戳
## 会议类型
- 工程会议:突出技术决策和基本原理
- 规划会议:强调优先级和时间表
- 客户会议:隐去敏感信息
- 短会(<10 分钟):只记录关键点
## 人员
- Sarah Chen(工程主管)- 关注技术细节
- Mike Rodriguez(产品经理)- 关注业务影响
...

AGENTS.md 是通过纠正自行构建的,而不是通过前期的文档编写。我们通过迭代得出了一个足够详细的 Agent 规范,而用户从未手动更改过 AGENTS.md

构建此记忆系统的经验教训

在此过程中,我们学到了几个教训。

最困难的部分是提示词工程(Prompting)

构建一个能够记住事物的 Agent,最困难的部分就是编写提示词。在几乎所有 Agent 表现不佳的情况下,解决方案都是改进提示词。通过这种方式解决的问题示例包括:

  • Agent 在该记住的时候没有记住
  • Agent 在不该记住的时候记住了
  • Agent 向 AGENTS.md 写入了太多内容,而不是写入 skills(技能)文件
  • Agent 不知道 skills 文件的正确格式
  • ……还有很多

我们安排了一名员工全职负责记忆的提示词工程(这占了团队很大一部分比例)。

验证文件类型

有几个文件需要遵守特定的 Schema(tools.json 需要有有效的 MCP 服务器,skills 需要有正确的 Frontmatter 等)。我们发现 Agent Builder 有时会忘记这一点,从而生成无效文件。我们添加了一个步骤来显式验证这些自定义格式,如果验证失败,则将所有错误抛回给 LLM,而不是提交该文件。

Agent 擅长向文件中添加内容,但不擅长精简压缩

Agent 在工作时会编辑它们的记忆。它们非常擅长向文件中添加特定的内容。然而,它们不擅长的一点是意识到何时该精简压缩(compact)所学到的知识。例如:我的电子邮件助手一度开始列出所有它应该忽略其推销邮件的具体供应商名单,而不是更新自身规则以忽略所有推销邮件。

作为最终用户,显式提示有时仍然有用

即使 Agent 能够在工作时更新其记忆,但在某些情况下(作为最终用户),我们发现显式提示 Agent 管理其记忆仍然很有用。其中一种情况是在其工作结束时,反思对话并更新其记忆以补充可能遗漏的任何内容。另一种情况是提示它精简压缩其记忆,以解决它只记住了特定案例而没有进行泛化的问题。

人类在环(Human-in-the-loop)

我们将所有对记忆的编辑都设置为“人类在环”——也就是说,在更新之前我们需要明确的人工批准。这在很大程度上是为了最小化提示词注入(prompt injection)的潜在攻击面。对于不太担心这个问题的用户,我们也提供了一种关闭此功能的方法(“Yolo 模式”)。

这实现了什么

除了更好的产品体验之外,以这种方式表示记忆还实现了许多功能。

无代码体验

无代码构建工具的问题之一是,它们要求你学习一种不熟悉的 DSL(领域特定语言),而这种语言在应对复杂性时扩展性很差。通过将 Agent 表示为 Markdown 和 JSON 文件,Agent 现在采用了一种 (a) 大多数技术背景较弱的人都熟悉的格式,(b) 更具可扩展性的格式。

更好的 Agent 构建体验

记忆实际上带来了更好的 Agent 构建体验。Agent 的构建是一个高度迭代的过程——很大程度上是因为在尝试之前,你不知道 Agent 会做什么。记忆使迭代变得更容易,因为你无需每次都手动更新 Agent 配置,只需用自然语言提供反馈,它就会自行更新。

可移植的 Agent

文件是非常易于移植的!这使你可以轻松地将 Agent Builder 中构建的 Agent 移植到其他框架中(只要它们使用相同的文件约定)。出于这个原因,我们尽量使用了尽可能多的标准约定。例如,我们希望让在 Agent Builder 中构建的 Agent 能够轻松地在 Deep Agents CLI 中使用。或者完全在其他 Agent 框架中使用,比如 Claude Code 或 OpenCode。

未来的方向

我们还想进行许多记忆方面的改进,但在发布之前,我们没有时间或足够的信心将它们加入其中。

情景记忆(Episodic memory)

Agent Builder 唯一缺失的 COALA 记忆类型是情景记忆:Agent 过去行为的序列。我们计划通过将以前的对话作为文件系统中的文件暴露出来,让 Agent 可以与之交互来实现这一点。

后台记忆进程

目前,所有的记忆都是“在热路径上”更新的;也就是说,在 Agent 运行时更新。我们希望添加一个在后台运行的进程(可能是一些 Cron 定时任务,每天运行一次左右),以反思所有对话并更新记忆。我们认为这将捕获 Agent 在当下未能识别的项目,并且对于泛化特定的经验教训特别有用。

/remember 命令

我们希望提供一个显式的 /remember 命令,以便你可以提示 Agent 反思对话并更新其记忆。我们发现自己偶尔会这样做,并且收益巨大,因此希望使其变得更容易并鼓励这种做法。

语义搜索

虽然能够使用 globgrep 搜索记忆是一个很好的起点,但在某些情况下,允许 Agent 对其记忆进行语义搜索将带来一些收益。

不同级别的记忆

目前,所有的记忆都是特定于该 Agent 的。我们没有用户级别或组织级别记忆的概念。我们计划通过向 Agent 暴露代表这些类型记忆的特定目录,并提示 Agent 相应地使用和更新这些记忆来实现这一点。

结论

如果构建具有记忆的 Agent 听起来很有趣,请尝试使用 LangSmith Agent Builder。如果你想帮助我们构建这个记忆系统,我们正在招聘。

关联主题