大家好,我是Jake,负责LangChain Labs团队。今天是会议的最后一天,很高兴见到各位。我想和大家探讨一个核心话题:如何赋予AI代理记忆能力,使其能从交互中持续学习并自我修正。无论是构建编码、客服还是研究代理,大多数团队都有一个共同的直觉:系统应当越用越好。以我们近期合作的一家金融服务公司为例,他们正在开发一个涉及支出、预算及非投资建议的代理。由于金融行业的强监管性质,该代理必须严格遵守语气指南。其用词、互动方式以及是否遵循正确的语调,是判断合规性的关键指标。
代理的语气应从上下文、记忆及规则政策中推导得出。然而,在实际运行中,我们观察到代理的语气偶尔会失控,从建议性转变为指令性。例如,它可能会说“你这种情况通常应该设置上限”,或者更直接地指出“你需要取消订阅并将资金转入储蓄”。客户认为这违反了语气规范,且类似情况多次发生。为了纠正这一问题,目前依赖人工审查:理解追踪记录、定位语气偏差、修改上下文引用规则并防止回归。这种流程显然难以扩展,因此系统需要成为一个能让代理从交互和经验中持续学习和受益的平台。
我们正处于一个观察力极强的时代,但将观察转化为未来运行的经验是另一回事。代理产生的信号和追踪记录前所未有地多,每一次运行都会生成存储在某处的日志。这非常有用,因为它让我们能了解调用的工具、推理过程及参考的工件。可观测性是构建可靠代理的基础,但挑战在于:如果未来引用的上下文从未改变,那么今天的错误将在未来的运行中持续存在。我们的目标是让追踪记录不再仅仅是日志,而是成为信号的来源——即记忆。可观测的代理告诉你发生了什么,而自适应的代理能够改变并指导未来的运行。
为了明确概念,我将“记忆”定义为代理引用的、能引导未来运行的持久上下文。追踪记录或日志只是发生了什么的证据,只有当这些经验被转化为代理在未来可引用的持久上下文时,才成为记忆。我将其分为三类认知科学范畴:语义记忆(代理知道什么),包括事实和偏好;情景记忆(代理经历过什么),包括学到的模式、过往互动和示例;以及程序性记忆(代理应如何行为),包括指令和技能。在之前的金融助手案例中,语气调整是通过更新程序性记忆实现的——我们不是添加新事实或禁用坏词,而是赋予其新的行为规则。
此外,区分工作记忆(短期)和长期记忆至关重要。工作记忆是代理在当前运行中引用的内容,如中间草稿、工具结果或检索到的文件。长期记忆则存储在外部数据结构中,包含代理在后续交互和持续行为中需要引用的指令和技能。不同的代理框架会以不同方式利用和注入长期记忆,但其核心在于建立一种机制,使代理能够从历史数据中提取教训并内化为持久的行为准则。
构建自己的SDK或使用各种流行的代理框架。有些将记忆直接注入提示词中,有些使用工具来检索它,有些直接从存储库中提取,有些通过文件加载,还有些改变运行时状态。实现方式可能各不相同,但区分记忆的类型至关重要。代理引用的部分上下文是临时的,而另一部分是持久且长期的,有助于指导未来的运行。我认为在思考代理运行时产生的痕迹、日志和产物时,这种区分非常重要。
痕迹不仅仅是最终输出的记录,它记录了工作记忆在整个运行过程中的变化方式。其中一部分应作为历史保留下来,另一部分则转化为持久的长期上下文。这为我们提供了一个非常有用的思维模型,我和LangChain团队以及我认为都是一种很有益的方式,用来思考短期工作记忆与长期记忆之间的关系。这是一个读写循环的过程。
你可能听说过“睡眠时计算”或“做梦”。这些术语指的是在代理执行工作时,甚至在某些情况下当代理不工作时,记忆如何从长期更新或提取到短期记忆中。这就是睡眠时计算的概念。如果我们从一个代理运行的开始阶段出发,它需要从长期记忆中将相关的上下文读取到短期记忆中。
这包括对当前运行重要的任何内容,比如技能或指令。你可能见过代理通过渐进式披露的方式拉取特定任务的相关技能。这就是从长期记忆向短期记忆转移的阶段。随后,当代理运行时,它会生成一条痕迹记录。这些证据对我们来说非常重要,因为它让我们能够理解它是如何检索上下文、调用了哪些工具或子代理,以及做出了什么决策。
正如我提到的,大多数证据应保留为历史记录。如果我们将所有生成的证据都转化为长期记忆,代理在处理未来任务时将会面临困难,推理能力也会下降。因此我认为非常有趣的一步是过滤环节:如何决定哪些是有用的信号并转化为持久的长期上下文,而哪些应保留为历史。
一旦做出这一决定(我们稍后会深入探讨),这些信号就会被写回持久的长期上下文中。以我们的财务助手为例,它被设定为“测量”而非“指令性”,以便更新其未来交互中的行为方式。这就是核心循环:记忆指导运行,运行产生证据,我们过滤证据以提取信号,然后利用该信号更新记忆。
如果你能构建起这个飞轮效应,我们就能朝着一个方向发展:构建非常可靠且强大的代理,它们似乎随着时间推移而变得更强。因此,一旦我们建立了这种思维模型,我认为可以用更简单的方式来拆解或抽象这一过程:捕捉发生的情况、分析可能重要的内容,然后更新上下文供未来运行使用。
不同框架构建者的实现方式仍在演变中,但这种三部分的结构是一个非常有用的起点。其中一种实现方式是LangSmith的做法(我并非声称这是唯一的方案,只是为了给讨论内容赋予具体的结构)。捕捉步骤旨在理解并查看代理的行为轨迹和所做事项,这正是我们可观测性发挥作用的地方。
你可以看到工具调用、代理如何调用子代理以及检索到的上下文。中间的“分析”步骤是我非常兴奋的新事物,它本质上是一个智能过程,进行后台分析和处理工作。基于对你而言重要的事项,从痕迹中提取信号,并将这些信号提升(Promote)到记忆库或代理未来运行所引用的参考点。
这就是LangSmith的上下文中心(Context Hub)。你可以想象,如果有一个地方可以进行这种分析、提取信号并作为远程存储库供你更新内容,以便代理下次运行时拉取这些数据。这就是LangChain的上下文中心所做的功能。这个抽象循环映射得非常清晰:你捕捉经验,提取信号,然后存储持久的上下文供未来使用。
为了更详细地说明这一点,并结合我在开头提到的财务助手案例来构建这个循环:当代理运行时,其上下文从上下文中心加载。这可能包括指令、技能、相关的Markdown文件以及政策和规则。当代理被调用或执行时,会生成一条痕迹记录,显示失败的 tool calls、用户纠正措施,或者在本例中,标记出语调偏离了我们期望的范围。
引擎随后会寻找模式并识别基于上下文中心的静态技能Markdown文件需要更改或提升的位置,并进行相应的修改。下一次代理运行时,我们实际上可以观察并理解其语调在与用户互动时发生了变化。
我和团队在设计记忆机制时,始终考虑的一些原则也应理想地指导你设计和构建这些抽象概念:代理会产生大量我们称之为“废气”或反馈的数据。并非所有内容都应成为记忆更新的一部分。正如我们在整个演示过程中所讨论的,关键在于细节:理解并决定什么对你来说是重要的。
大多数痕迹数据实际上应保留为可参考的历史记录,有些可能成为用于离线评估的数据集。只有其中一小部分子集最终应转化为改变代理记忆的内容。
其次,这是一个我常遇到的陷阱。我认为需要在构建优化系统与提供热路径中的记忆存储或更新之间,小心翼翼地走钢丝。我发现对于长期运行的后台代理尤其如此:想象一个长时间运行的代理,如果在操作初期进行了记忆更新,但由于运行时状态或访问方式的问题,后续运行中并未实际获取这些更新。因此,理解哪些内容可以缓存、哪些不能,以确保记忆更新在后续运行中可用,这一点非常重要。
我曾为此苦恼不已,试图理解为何行为没有变化,直到我意识到需要重视这一原则以及第三条。我认为开源核心模式、Hermes代理以及其他流行的具备自我更新功能的代理,都涉及这一过程。这里有一个重要的考量:什么应该允许自我更新,而什么需要人工审核?
如果讨论的是包含指令、政策和语气指南的程序性记忆,它们可能决定了代理约75%的行为。因此,这部分内容应保留一定程度的“人在回路”和人工交互机制。在将更改提交并放入代理的热重载路径之前,应有一个界面来展示对程序性记忆部分的修改。保护重要行为是另一个重要的层级。
回到最初的基调:下一次运行应该更好。记忆使我们能够做到这一点。“记忆”一词常被提及,但在构建代理、使用框架或提供上下文的语境中,记忆确实赋予了我们要的能力。它不仅仅是存储信息的场所,更是经验转化为上下文的方式。正是这种上下文让下一次运行有机会得到改进。
如果你对实际实现感兴趣,那个二维码里有我演示如何构建一个完全符合上述描述的代理的视频。希望这能使其具象化,并连接起这些概念点。如果你想就此进行进一步讨论,请来Eugene 19展位找我,我很乐意交流。