摘要

1) 一句话总结

LangSmith Agent Builder 构建了一个基于虚拟文件系统(如 Markdown 和 JSON)的记忆系统,使无代码 Agent 能够通过与用户的持续交互自动迭代指令,从而优化特定任务的工作流。

2) 核心要点

  • 定位与场景:作为面向平民开发者的无代码工具,其 Agent 专为重复性特定任务设计,因此跨会话的“记忆”转化率极高,是优先开发的核心功能。
  • 虚拟文件系统架构:记忆以文件形式暴露给大模型(利用 LLM 擅长处理文件系统的特性),但底层实际存储在 Postgres 数据库中,支持热插拔。
  • 采用标准文件约定:使用 AGENTS.md 定义核心指令,使用类似 Claude Code 的格式定义子 Agent,并使用自定义的 tools.json 控制 MCP(模型上下文协议)工具访问以防止上下文溢出。
  • 契合 COALA 记忆模型:目前已实现程序性记忆(AGENTS.mdtools.json)和语义记忆(技能与知识文件)。
  • 热路径自动迭代:Agent 在运行的“热路径”中直接编辑记忆文件,用户只需提供自然语言反馈,Agent 即可自动完善其规范,实现真正的无代码体验。
  • 提示词工程投入:提示词是构建该系统最困难的部分,团队为此安排了一名全职员工专门负责记忆系统的提示词优化。
  • 显式格式验证:系统内置了文件格式验证步骤,若 Agent 生成了无效的 JSON 或 frontmatter,会将错误直接抛回给 LLM 重新处理。
  • 高可移植性:将 Agent 状态表示为标准 Markdown 和 JSON 文件,使其能够轻松移植到其他支持相同约定的 Agent 框架中。
  • 未来规划:计划开发后台定时记忆处理(用于反思和泛化)、/remember 显式命令、语义搜索以及用户/组织级别的多层级记忆。

3) 风险与不足

  • 精简与压缩能力差:Agent 擅长向记忆中添加具体案例和内容,但不擅长精简、压缩和泛化经验(例如倾向于列举所有具体对象,而不是总结出通用规则)。
  • 提示词注入风险:记忆系统存在提示词注入的潜在攻击面,因此系统默认强制采用“人在回路”(人工批准)模式来拦截恶意更新。
  • 情景记忆缺失:目前系统尚未实现情景记忆(即 Agent 过去行为的序列),计划未来通过将历史对话作为文件暴露来弥补此空白。
  • 格式遗忘问题:LLM 在生成记忆文件时,有时会忘记遵循特定的 Schema(模式),导致生成无效文件。

正文

上个月,我们推出了 LangSmith Agent Builder,这是一种无代码构建 Agent(智能体)的方式。Agent Builder 的一个核心部分是它的记忆系统。在本文中,我们将探讨优先开发记忆系统的原因、构建该系统的技术细节、开发过程中的经验教训、记忆系统所带来的功能,并讨论未来的工作方向。

什么是 LangSmith Agent Builder

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

在早期,我们就有意识地将“记忆”作为平台的优先事项。这并不是一个显而易见的选择——大多数 AI 产品在最初发布时没有任何形式的记忆,即使后来加入,也没有像预期那样彻底改变产品。我们优先考虑它的原因在于用户的使用模式

与 ChatGPT、Claude 或 Cursor 不同,LangSmith Agent Builder 不是通用型 Agent,而是专门为特定任务定制的。通用型 Agent 处理的任务多种多样且互不相关,因此一次会话中的经验可能对下一次毫无用处。但 LangSmith Agent 会反复执行相同的任务,一次会话中的经验可以高度转化为下一次的参考。如果不具备记忆功能,用户体验将会很差,因为这意味着你必须在不同的会话中向 Agent 反复强调相同的内容。

在思考 LangSmith Agent 的记忆究竟意味着什么时,我们参考了 COALA 论文中对 Agent 记忆的三种分类:

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

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

在 Agent Builder 中,我们将记忆表示为一组文件。这是一个有意为之的选择,目的是利用大模型擅长使用文件系统的特点。这样一来,我们无需提供专门的工具,只需赋予 Agent 访问文件系统的权限,它就能轻松读取和修改自己的记忆。

在可能的情况下,我们尽量使用行业标准:

  • 使用 AGENTS.md 定义 Agent 的核心指令集。
  • 使用 Agent 技能(skills)为特定任务提供专门的指令。
  • 虽然目前没有子 Agent(subagent)标准,但我们使用了类似于 Claude Code 的格式。
  • 对于 MCP(模型上下文协议)访问,我们使用自定义的 tools.json 文件,而不是标准的 mcp.json,因为我们希望允许用户仅向 Agent 提供 MCP 服务器中的部分工具,以避免上下文溢出。

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

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

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

这些文件与 COALA 论文中定义的记忆类型完美契合:程序性记忆(驱动核心指令)对应 AGENTS.mdtools.json;语义记忆对应 Agent 技能和其他知识文件。唯一缺失的是情景记忆,但我们认为对于这类 Agent 来说,它不如前两者重要。

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

我们可以看看内部正在使用的一个真实 Agent——基于 LangSmith Agent Builder 构建的 LinkedIn 招聘助手:

  • AGENTS.md:定义核心指令。
  • subagents/:仅定义了一个子 Agent 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. 客户)
  • 通过使用积累的边缘情况纠正

此时的记忆文件可能包含非常详细的会议总结偏好(如工程会议强调技术决策、客户会议脱敏、特定人员的关注点等)。

AGENTS.md 是通过不断的纠正自行构建的,而不是通过前期的预先编写。我们在用户从未手动修改过 AGENTS.md 的情况下,通过迭代得到了一个细节恰到好处的 Agent 规范。

构建记忆系统的经验教训

在开发过程中,我们学到了几个重要的教训:

最困难的部分是提示词工程(Prompting) 构建具备记忆能力的 Agent,最难的一环就是提示词。在几乎所有 Agent 表现不佳的情况下,解决方案都是改进提示词。通过这种方式解决的问题包括:

  • Agent 没有在应该记住的时候记住信息
  • Agent 在不该记住的时候记住了信息
  • Agent 向 AGENTS.md 写入了过多内容,而不是写入技能文件
  • Agent 不知道技能文件的正确格式
  • ……以及更多问题

我们甚至安排了一名员工全职负责记忆系统的提示词工作(这占了团队很大一部分精力)。

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

Agent 擅长添加内容,但不擅长精简压缩 Agent 会在工作时编辑记忆,它们非常擅长向文件中添加具体内容。然而,它们不擅长意识到何时应该精简和压缩经验。例如:我的电子邮件助手一度开始列出所有它应该忽略的特定推销供应商,而不是自我更新为“忽略所有冷启动推销邮件”。

作为最终用户,显式提示仍然很有用 即使 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 移植到其他框架中(只要它们使用相同的文件约定)。正是出于这个原因,我们尽量使用了标准的约定。我们希望让用户能够轻松地在 Deep Agents CLI,甚至 Claude Code 或 OpenCode 等完全不同的 Agent 框架中使用这些 Agent。

未来的发展方向

在发布之前,由于时间或信心不足,我们还有许多想要实现的记忆系统改进尚未完成:

情景记忆(Episodic memory) Agent Builder 目前唯一缺失的 COALA 记忆类型是情景记忆:Agent 过去行为的序列。我们计划通过将以前的对话作为文件系统中的文件暴露给 Agent 来实现这一点。

后台记忆处理 目前,所有的记忆更新都是“在热路径中”进行的,即在 Agent 运行时更新。我们希望添加一个在后台运行的进程(可能是一个每天运行一次的定时任务),以反思所有对话并更新记忆。我们认为这将捕获 Agent 在当下未能识别的信息,并且对泛化特定经验特别有用。

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

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

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

结语

如果你对构建具备记忆能力的 Agent 感兴趣,欢迎尝试 LangSmith Agent Builder。如果你想帮助我们构建这个记忆系统,我们正在招聘。

相关文档

关联主题