跳到主要内容

文档索引

在以下地址获取完整的文档索引:https://docs.langchain.org.cn/llms.txt

在进一步探索之前,请使用此文件发现所有可用页面。

记忆是一个记录先前交互信息的系统。对于 AI 智能体而言,记忆至关重要,因为它能让它们记住先前的交互,从反馈中学习,并适应用户偏好。随着智能体处理包含大量用户交互的复杂任务,这种能力对于提高效率和用户满意度变得必不可少。 本概念指南涵盖了两类记忆,基于其召回范围:
  • 短期记忆,或称线程(thread)范围记忆,通过在会话中维护消息历史记录来跟踪正在进行的对话。LangGraph 将短期记忆作为智能体状态(state)的一部分进行管理。状态使用检查点(checkpointer)持久化到数据库中,以便线程可以随时恢复。短期记忆在调用图或完成一个步骤时更新,并在每一步开始时读取状态。
  • 长期记忆存储跨会话的用户特定或应用级数据,并对话线程共享。它可以在任何时间任何线程中被召回。记忆的作用域可以是任何自定义命名空间,而不局限于单个线程 ID。LangGraph 提供了存储(stores)参考文档)来让您保存和召回长期记忆。
Short vs long

短期记忆

短期记忆让您的应用能够记住单个线程或对话中的先前交互。一个线程组织会话中的多次交互,类似于电子邮件将消息分组成单个对话。 LangGraph 将短期记忆作为智能体状态的一部分进行管理,通过线程范围的检查点进行持久化。这种状态通常可以包括对话历史以及其他有状态数据,例如上传的文件、检索到的文档或生成的产物。通过将这些存储在图的状态中,机器人可以访问给定对话的完整上下文,同时保持不同线程之间的隔离。

管理短期记忆

对话历史是短期记忆最常见的形式,长对话对当今的 LLM 构成了挑战。完整的历史记录可能无法容纳在 LLM 的上下文窗口内,从而导致不可恢复的错误。即使您的 LLM 支持完整的上下文长度,大多数 LLM 在长上下文下的表现仍然较差。它们会被陈旧或偏离主题的内容“分心”,同时面临响应时间慢和成本高的问题。 聊天模型使用消息接收上下文,包括开发者提供的指令(系统消息)和用户输入(人类消息)。在聊天应用中,消息在人类输入和模型响应之间交替,导致消息列表随时间增长。由于上下文窗口有限且包含大量标记的消息列表成本高昂,许多应用可以从手动删除或遗忘陈旧信息的技术中受益。 过滤器 有关管理消息的常用技术的更多信息,请参阅添加和管理记忆指南。

长期记忆

LangGraph 中的长期记忆允许系统跨不同的对话或会话保留信息。与作用域为线程的短期记忆不同,长期记忆保存在自定义的“命名空间”内。 长期记忆是一个复杂的挑战,没有万能的解决方案。然而,以下问题提供了一个框架来帮助您了解不同的技术:
  • 记忆的类型是什么? 人类使用记忆来记住事实(语义记忆)、经历(情景记忆)和规则(程序记忆)。 人工智能代理可以使用相同的方式使用记忆。 例如,人工智能代理可以使用记忆来记住有关用户的特定事实以完成任务。
  • 您希望何时更新记忆? 记忆可以作为代理应用程序逻辑的一部分进行更新(例如,“在热路径上”)。 在这种情况下,代理通常会在回复用户之前决定记住事实。 或者,记忆可以作为后台任务进行更新(在后台/异步运行并生成记忆的逻辑)。 我们在下文中解释了这些方法之间的权衡。
不同的应用程序需要不同类型的记忆。 虽然类比并不完美,但检查人类记忆类型可能具有启发性。 一些研究(例如,CoALA 论文)甚至已将这些人类记忆类型映射到人工智能代理中使用的类型。
记忆类型存储的内容人类示例代理示例
语义事实我在学校学到的东西关于用户的的事实
情景经历我做过的事情过去的代理操作
程序指令本能或运动技能代理系统提示

语义记忆

语义记忆,在人类和人工智能代理中,都涉及保留特定的事实和概念。 在人类中,它可以包括在学校学到的信息以及对概念及其关系的理解。 对于人工智能代理,语义记忆通常用于通过记住过去交互中的事实或概念来个性化应用程序。
语义记忆不同于“语义搜索”,这是一种使用“含义”(通常是嵌入)查找相似内容的技术。 语义记忆是来自心理学的术语,指的是存储事实和知识,而语义搜索是一种基于含义而不是精确匹配来检索信息的方法。
语义记忆可以以不同的方式管理

档案

记忆可以是一个单一的、持续更新的“个人资料(profile)”,其中包含有关用户、组织或其他实体(包括智能体本身)的范围明确且具体的信息。个人资料通常只是一个 JSON 文档,包含您选择用来代表您领域的各种键值对。 在记住个人资料时,您需要确保每次都在更新该个人资料。因此,您需要传入之前的资料并要求模型生成新的资料(或应用到旧资料的一些 JSON 补丁)。随着个人资料变大,这可能会变得容易出错,拆分为多个文档或在生成文档时使用严格解码可能有助于确保记忆架构(schema)保持有效。 更新个人资料

集合

或者,记忆也可以是随时间不断更新和扩展的文档集合。每个独立的记忆可以有更窄的范围,且更容易生成,这意味着您不太可能随时间丢失信息。对于 LLM 来说,为新信息生成对象比将新信息与现有资料进行协调要容易。因此,文档集合往往会带来更高的下游召回率 然而,这会将一些复杂性转移到记忆更新上。模型现在必须删除更新列表中的现有项目,这可能很棘手。此外,某些模型可能默认过度插入,而另一些可能默认过度更新。有关管理此问题的一种方法,请参阅 Trustcall 包,并考虑使用评估(例如使用 LangSmith 等工具)来帮助您调整行为。 使用文档集合还会将复杂性转移到对列表的记忆搜索上。Store 目前支持语义搜索按内容过滤 最后,使用记忆集合可能难以向模型提供全面的上下文。虽然单个记忆可能遵循特定的架构,但这种结构可能无法捕捉记忆之间的完整上下文或关系。因此,当使用这些记忆生成响应时,模型可能缺乏一些在统一资料方法中更容易获得的重要背景信息。 更新列表 无论采用哪种记忆管理方法,核心点都在于智能体将使用语义记忆来使其响应有所依据,这通常会带来更个性化且相关的交互。

情景记忆

情节记忆,无论是在人类还是 AI 智能体中,都涉及回忆过去的事件或行动。CoALA 论文很好地界定了这一点:事实可以写入语义记忆,而体验可以写入情节记忆。对于 AI 智能体,情节记忆通常用于帮助智能体记住如何完成一项任务。 在实践中,情节记忆通常通过少样本示例提示来实现,智能体从过去的序列中学习以正确执行任务。有时,“演示”比“说教”更容易,LLM 从示例中学习效果很好。少样本学习允许您通过使用输入-输出示例更新提示来“编程”您的 LLM,以说明预期的行为。虽然可以使用各种最佳实践来生成少样本示例,但挑战通常在于根据用户输入选择最相关的示例。 请注意,记忆存储(store)只是将数据存储为少样本示例的一种方式。如果您希望有更多的开发者参与,或者将少样本更紧密地与您的评估体系挂钩,您还可以使用 LangSmith 数据集来存储您的数据,并实现您自己的检索逻辑,以根据用户输入选择最相关的示例。 请参阅这篇展示通过少样本提示提高工具调用性能的博客文章,以及这篇使用少样本示例使 LLM 与人类偏好对齐的博客文章。

程序记忆

程序记忆,在人类和 AI 智能体中,都涉及记住用于执行任务的规则。在人类中,程序记忆就像是关于如何执行任务的内在知识,例如通过基本的运动技能和平衡骑自行车。另一方面,情节记忆涉及回忆具体的体验,例如您第一次在没有辅助轮的情况下成功骑自行车的经历,或者是一次风景优美、令人难忘的骑行。对于 AI 智能体,程序记忆是模型权重、智能体代码和智能体提示词的组合,它们共同决定了智能体的功能。 在实践中,智能体修改其模型权重或重写其代码是相当罕见的。然而,智能体修改自己的提示词更为常见。 改进智能体指令的一种有效方法是通过“反思(Reflection)”或元提示。这包括使用智能体当前的指令(例如系统提示词)连同最近的对话或明确的用户反馈来提示智能体。然后,智能体根据这些输入改进自己的指令。这种方法对于难以预先指定指令的任务特别有用,因为它允许智能体从其交互中学习和适应。 例如,我们构建了一个推文生成器,使用外部反馈和提示词重写来为 Twitter 生成高质量的论文摘要。在这种情况下,特定的摘要提示词很难先验地指定,但用户很容易对生成的推文进行评价,并就如何改进摘要过程提供反馈。 下面的伪代码展示了您如何使用 LangGraph 记忆存储(store)来实现这一点:使用 store 保存提示词,update_instructions 节点获取当前提示词(以及从 state["messages"] 中捕获的与用户对话的反馈),更新提示词,并将新提示词保存回 store。然后,call_model 从 store 获取更新后的提示词并使用它来生成响应。
# Node that *uses* the instructions
def call_model(state: State, store: BaseStore):
    namespace = ("agent_instructions", )
    instructions = store.get(namespace, key="agent_a")[0]
    # Application logic
    prompt = prompt_template.format(instructions=instructions.value["instructions"])
    ...

# Node that updates instructions
def update_instructions(state: State, store: BaseStore):
    namespace = ("instructions",)
    instructions = store.search(namespace)[0]
    # Memory logic
    prompt = prompt_template.format(instructions=instructions.value["instructions"], conversation=state["messages"])
    output = llm.invoke(prompt)
    new_instructions = output['new_instructions']
    store.put(("agent_instructions",), "agent_a", {"instructions": new_instructions})
    ...
Update instructions

写入记忆

智能体写入记忆主要有两种方法:“在热路径中”“在后台” 热路径 vs 后台

在热路径中

在运行时创建记忆既有优点也有挑战。从积极的一面来看,这种方法允许实时更新,使新记忆在后续交互中立即生效。它还实现了透明度,因为在创建和存储记忆时可以通知用户。 然而,这种方法也带来了挑战。如果智能体需要一个新工具来决定将什么存入记忆,这可能会增加复杂性。此外,推理要保存什么记忆的过程可能会影响智能体的延迟。最后,智能体必须在记忆创建和其他职责之间进行多任务处理,这可能会影响创建记忆的数量和质量。 例如,ChatGPT 使用 save_memories 工具将记忆作为内容字符串插入/更新,并在每次处理用户消息时决定是否以及如何使用此工具。请参阅我们的 memory-agent 模板作为参考实现。

在后台

作为单独的后台任务创建记忆提供了几个优点。它消除了主应用程序中的延迟,将应用程序逻辑与记忆管理分离,并允许代理更专注于完成任务。这种方法还提供了在时间上创建记忆的灵活性,以避免重复工作。 然而,这种方法也有其自身的挑战。确定记忆写入的频率变得至关重要,因为不频繁的更新可能会导致其他线程没有新的上下文。确定何时触发记忆形成也很重要。常见的策略包括在设置的时间段后安排(如果发生新的事件则重新安排),使用 cron 计划,或允许用户或应用程序逻辑手动触发。 请参阅我们的 memory-service 模板作为参考实现。

记忆存储

LangGraph 将长期记忆作为 JSON 文档存储在存储(store)中。每个记忆都组织在自定义的 namespace(类似于文件夹)和唯一的 key(类似于文件名)下。命名空间通常包括用户或组织 ID 或其他标签,以便更轻松地组织信息。这种结构实现了记忆的分层组织。然后通过内容过滤器支持跨命名空间搜索。
from langgraph.store.memory import InMemoryStore


def embed(texts: list[str]) -> list[list[float]]:
    # Replace with an actual embedding function or LangChain embeddings object
    return [[1.0, 2.0] * len(texts)]


# InMemoryStore saves data to an in-memory dictionary. Use a DB-backed store in production use.
store = InMemoryStore(index={"embed": embed, "dims": 2})
user_id = "my-user"
application_context = "chitchat"
namespace = (user_id, application_context)
store.put(
    namespace,
    "a-memory",
    {
        "rules": [
            "User likes short, direct language",
            "User only speaks English & python",
        ],
        "my-key": "my-value",
    },
)
# get the "memory" by ID
item = store.get(namespace, "a-memory")
# search for "memories" within this namespace, filtering on content equivalence, sorted by vector similarity
items = store.search(
    namespace, filter={"my-key": "my-value"}, query="language preferences"
)
有关内存存储的更多信息,请参阅 持久化 指南。

了解更多


© . This site is unofficial and not affiliated with LangChain, Inc.