- 短期内存,或线程范围的内存,通过在会话中维护消息历史来跟踪正在进行的对话。LangGraph 将短期内存作为智能体状态的一部分进行管理。使用检查点器将状态持久化到数据库,以便可以随时恢复线程。当调用图或完成步骤时,短期内存会更新,并在每个步骤开始时读取状态。
- 长期内存跨会话存储用户特定或应用程序级别的数据,并在对话线程_之间_共享。它可以_在任何时间_和_在任何线程_中被召回。内存的范围是任何自定义命名空间,而不仅仅是在单个线程 ID 内。LangGraph 提供存储(参考文档)让您保存和召回长期内存。
短期内存
短期内存让您的应用程序记住单个线程或对话中的先前交互。线程组织会话中的多个交互,类似于电子邮件在单个对话中对消息进行分组的方式。 LangGraph 将短期内存作为智能体状态的一部分进行管理,通过线程范围的检查点持久化。此状态通常可以包括对话历史以及其他有状态数据,例如上传的文件、检索的文档或生成的工件。通过将这些存储在图的状态中,机器人可以访问给定对话的完整上下文,同时保持不同线程之间的分离。管理短期内存
对话历史是短期内存的最常见形式,长对话对当今的 LLM 构成挑战。完整的历史可能无法适应 LLM 的上下文窗口,导致不可恢复的错误。即使您的 LLM 支持完整的上下文长度,大多数 LLM 在长上下文中仍然表现不佳。它们会被陈旧或离题的内容”分心”,同时遭受更慢的响应时间和更高的成本。 聊天模型使用消息接受上下文,其中包括开发人员提供的指令(系统消息)和用户输入(人类消息)。在聊天应用程序中,消息在人类输入和模型响应之间交替,导致消息列表随时间而增长。由于上下文窗口有限且富含令牌的消息列表可能代价高昂,许多应用程序可以受益于使用技术手动删除或忘记陈旧信息。
有关管理消息的常见技术的更多信息,请参阅添加和管理内存指南。
长期内存
LangGraph 中的长期内存允许系统跨不同对话或会话保留信息。与线程范围的短期内存不同,长期内存保存在自定义”命名空间”中。 长期内存是一个复杂的挑战,没有一刀切的解决方案。但是,以下问题提供了一个框架来帮助您导航不同的技术:- 内存的类型是什么?人类使用内存来记住事实(语义内存)、经历(情节内存)和规则(程序内存)。AI 智能体可以以相同的方式使用内存。例如,AI 智能体可以使用内存记住有关用户的特定事实以完成任务。
- 何时更新内存? 内存可以作为智能体应用程序逻辑的一部分更新(例如,“在热路径上”)。在这种情况下,智能体通常在响应用户之前决定记住事实。或者,内存可以作为后台任务更新(在后台/异步运行并生成内存的逻辑)。我们在下面的部分中解释这些方法之间的权衡。
语义内存
语义内存,无论是在人类还是 AI 智能体中,都涉及特定事实和概念的保留。在人类中,它可以包括在学校学到的信息以及对概念及其关系的理解。对于 AI 智能体,语义内存通常用于通过记住过去交互中的事实或概念来个性化应用程序。语义内存不同于”语义搜索”,后者是一种使用”意义”(通常作为嵌入)查找相似内容的技术。语义内存是心理学中的一个术语,指的是存储事实和知识,而语义搜索是一种基于意义而不是精确匹配来检索信息的方法。
配置文件
内存可以是关于用户、组织或其他实体(包括智能体本身)的范围明确且具体的信息的单个持续更新的”配置文件”。配置文件通常只是一个 JSON 文档,其中包含您选择用于表示域的各种键值对。 在记住配置文件时,您需要确保每次都在更新配置文件。因此,您需要传入先前的配置文件并要求模型生成新配置文件(或一些要应用于旧配置文件的 JSON 补丁)。随着配置文件变大,这可能会变得容易出错,并且可能受益于将配置文件拆分为多个文档或在生成文档时使用严格解码以确保内存模式保持有效。
集合
或者,内存可以是随时间持续更新和扩展的文档集合。每个单独的内存可以更窄地限定范围并且更容易生成,这意味着您不太可能随时间丢失信息。对于 LLM 来说,为新信息生成_新_对象比将新信息与现有配置文件协调更容易。因此,文档集合往往导致下游召回率更高。 但是,这会将一些复杂性转移到内存更新。模型现在必须_删除_或_更新_列表中的现有项目,这可能很棘手。此外,一些模型可能默认过度插入,而其他模型可能默认过度更新。有关管理此问题的一种方法,请参阅 Trustcall 包,并考虑评估(例如,使用 LangSmith 等工具)来帮助您调整行为。 使用文档集合还会将复杂性转移到列表上的内存搜索。Store 目前支持语义搜索和按内容过滤。
最后,使用内存集合可能会使向模型提供全面上下文变得具有挑战性。虽然单个内存可能遵循特定模式,但此结构可能无法捕获内存之间的完整上下文或关系。因此,在使用这些内存生成响应时,模型可能缺少在统一配置文件方法中更容易获得的重要上下文信息。
无论采用何种内存管理方法,核心点是智能体将使用语义内存来基于其响应,这通常会导致更个性化和相关的交互。
情节内存
情节内存,无论是在人类还是 AI 智能体中,都涉及召回过去的事件或行动。CoALA 论文很好地阐述了这一点:事实可以写入语义内存,而经历可以写入情节内存。对于 AI 智能体,情节内存通常用于帮助智能体记住如何完成任务。 在实践中,情节内存通常通过少样本示例提示实现,其中智能体从过去的序列中学习以正确执行任务。有时”展示”比”告诉”更容易,LLM 从示例中学习得很好。少样本学习让您通过使用输入-输出示例更新提示来说明预期行为,从而”编程”您的 LLM。虽然可以使用各种最佳实践来生成少样本示例,但挑战通常在于根据用户输入选择最相关的示例。 请注意,内存存储只是将数据存储为少样本示例的一种方式。如果您想要更多开发人员参与,或将少样本更紧密地与您的评估工具联系起来,您还可以使用 LangSmith 数据集来存储您的数据。然后可以开箱即用地使用动态少样本示例选择器来实现相同的目标。LangSmith 将为您索引数据集,并根据关键字相似性(使用类似 BM25 的算法进行基于关键字的相似性)启用检索与用户输入最相关的少样本示例。 有关 LangSmith 中动态少样本示例选择的示例用法,请参阅此操作视频。另外,请参阅此博客文章,展示少样本提示以提高工具调用性能,以及此博客文章使用少样本示例将 LLM 与人类偏好对齐。程序内存
程序内存,无论是在人类还是 AI 智能体中,都涉及记住用于执行任务的规则。在人类中,程序内存就像是如何执行任务的内化知识,例如通过基本运动技能和平衡骑自行车。另一方面,情节内存涉及召回特定经历,例如第一次在没有训练轮的情况下成功骑自行车或通过风景路线的难忘自行车之旅。对于 AI 智能体,程序内存是模型权重、智能体代码和智能体提示的组合,它们共同决定智能体的功能。 在实践中,智能体修改其模型权重或重写其代码是相当不常见的。但是,智能体修改其自己的提示更为常见。 通过”反思”或元提示来完善智能体指令是一种有效的方法。这涉及使用其当前指令(例如,系统提示)以及最近的对话或明确的用户反馈提示智能体。然后,智能体根据此输入完善其自己的指令。此方法对于指令难以预先指定的任务特别有用,因为它允许智能体从其交互中学习和适应。 例如,我们构建了一个 Tweet 生成器,使用外部反馈和提示重写为 Twitter 生成高质量的论文摘要。在这种情况下,特定的摘要提示很难事先指定,但用户批评生成的 Tweet 并提供有关如何改进摘要过程的反馈相当容易。 下面的伪代码展示了如何使用 LangGraph 内存存储实现此操作,使用存储来保存提示,update_instructions 节点获取当前提示(以及从与用户对话中捕获的反馈,在 state["messages"] 中),更新提示,并将新提示保存回存储。然后,call_model 从存储中获取更新的提示并使用它生成响应。
写入内存
智能体写入内存有两种主要方法:“在热路径中”和”在后台”。
在热路径中
在运行时创建内存既有优势也有挑战。从积极的一面来看,这种方法允许实时更新,使新内存立即可用于后续交互。它还实现了透明度,因为可以在创建和存储内存时通知用户。 但是,此方法也存在挑战。如果智能体需要新工具来决定要提交到内存的内容,它可能会增加复杂性。此外,推理要保存到内存的内容的过程可能会影响智能体延迟。最后,智能体必须在内存创建和其他职责之间进行多任务处理,可能会影响创建的内存的数量和质量。 例如,ChatGPT 使用 save_memories 工具将内存作为内容字符串更新插入,决定是否以及如何在每条用户消息中使用此工具。请参阅我们的 memory-agent 模板作为参考实现。在后台
将内存创建作为单独的后台任务提供了几个优势。它消除了主应用程序中的延迟,将应用程序逻辑与内存管理分开,并允许智能体更专注地完成任务。这种方法还提供了在时间上创建内存的灵活性,以避免冗余工作。 但是,此方法有其自身的挑战。确定内存写入的频率变得至关重要,因为不频繁的更新可能会使其他线程缺少新上下文。决定何时触发内存形成也很重要。常见策略包括在设定的时间段后安排(如果发生新事件则重新安排)、使用 cron 计划或允许用户或应用程序逻辑手动触发。 请参阅我们的 memory-service 模板作为参考实现。内存存储
LangGraph 将长期内存作为 JSON 文档存储在存储中。每个内存都在自定义namespace(类似于文件夹)和不同的 key(如文件名)下组织。命名空间通常包括用户或组织 ID 或其他标签,使组织信息更容易。此结构实现了内存的分层组织。然后通过内容过滤器支持跨命名空间搜索。