> ## Documentation Index
> Fetch the complete documentation index at: https://langchain.idochub.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# 内存概览

<警告>
  **Alpha 版本提示：** 本文档涵盖的是 **v1-alpha** 版本。内容尚不完整，且可能随时更改。

  如需查阅最新稳定版本，请参阅当前的 [LangGraph Python 文档](https://langchain-ai.github.io/langgraph/) 或 [LangGraph JavaScript 文档](https://langchain-ai.github.io/langgraphjs/)。
</警告>

[内存](/oss/python/langgraph/add-memory) 是一种能够记住先前交互信息的系统。对于 AI 代理而言，内存至关重要，因为它使代理能够记住过去的交互、从反馈中学习并适应用户偏好。随着代理处理的任务越来越复杂、涉及的用户交互越来越多，这种能力对提升效率和用户满意度变得不可或缺。

本概念指南根据记忆的召回范围，介绍了两种类型的内存：

* [短期记忆](#短期记忆)，或称[线程](/oss/python/langgraph/persistence#threads)作用域内存，通过在会话中维护消息历史来跟踪正在进行的对话。LangGraph 将短期记忆作为代理[状态](/oss/python/langgraph/graph-api#state)的一部分进行管理。状态通过[检查点](/oss/python/langgraph/persistence#checkpoints)持久化到数据库中，以便随时恢复线程。当图被调用或某一步骤完成时，短期记忆会更新；在每一步开始时，都会读取状态。
* [长期记忆](#长期记忆) 跨会话存储特定于用户或应用级别的数据，并在多个对话线程间共享。它可以“随时”、“在任何线程中”被召回。记忆可以作用于任何自定义命名空间，而不仅限于单个线程 ID。LangGraph 提供了[存储](/oss/python/langgraph/persistence#memory-store)（[参考文档](https://langchain-ai.github.io/langgraph/reference/store/#langgraph.store.base.BaseStore)），以便您保存和召回长期记忆。

[](/oss/images/short-vs-long.png)

## 短期记忆

[短期记忆](/oss/python/langgraph/add-memory#add-short-term-memory) 使您的应用程序能够记住单个[线程](/oss/python/langgraph/persistence#threads)或对话内的先前交互。[线程](/oss/python/langgraph/persistence#threads) 将会话中的多次交互组织在一起，类似于电子邮件将消息分组到单一对话中的方式。

LangGraph 通过线程作用域的检查点将短期记忆作为代理状态的一部分进行管理。该状态通常包括对话历史以及其他有状态数据，例如上传的文件、检索到的文档或生成的产物。通过将这些数据存储在图的状态中，机器人可以访问特定对话的完整上下文，同时保持不同线程之间的隔离。

### 管理短期记忆

对话历史是最常见的短期记忆形式，而长对话对当今的 LLM 构成了挑战。完整的历史可能无法放入 LLM 的上下文窗口，从而导致不可恢复的错误。即使您的 LLM 支持完整的上下文长度，大多数 LLM 在长上下文中表现仍然不佳。它们会被陈旧或离题的内容“分散注意力”，同时响应速度变慢、成本增加。

聊天模型使用消息来接受上下文，消息包括开发者提供的指令（系统消息）和用户输入（人类消息）。在聊天应用中，消息在人类输入和模型响应之间交替出现，从而形成一个随时间增长的消息列表。由于上下文窗口有限，且包含大量令牌的消息列表成本高昂，许多应用可以通过手动移除或遗忘陈旧信息的技术受益。

[](/oss/images/filter.png)

有关管理消息的常用技术的更多信息，请参阅 [添加和管理内存](/oss/python/langgraph/add-memory#manage-short-term-memory) 指南。

## 长期记忆

LangGraph 中的[长期记忆](/oss/python/langgraph/add-memory#add-long-term-memory) 允许系统在不同对话或会话之间保留信息。与**线程作用域**的短期记忆不同，长期记忆保存在自定义的“命名空间”内。

长期记忆是一个复杂挑战，没有一刀切的解决方案。然而，以下问题提供了一个框架，帮助您探索不同的技术：

* [记忆的类型是什么？](#记忆类型) 人类使用记忆来记住事实（[语义记忆](#语义记忆)）、经历（[情景记忆](#情景记忆)）和规则（[程序性记忆](#程序性记忆)）。AI 代理也可以以相同的方式使用记忆。例如，AI 代理可以使用记忆来记住关于用户的特定事实，以完成任务。
* [您希望何时更新记忆？](#写入记忆) 记忆可以在代理的应用逻辑中更新（例如，“在热路径上”）。在这种情况下，代理通常在响应用户之前决定记住事实。或者，记忆可以作为后台任务更新（在后台/异步运行并生成记忆的逻辑）。我们在[下文](#写入记忆)中解释了这些方法之间的权衡。

### 记忆类型

不同的应用程序需要不同类型的记忆。虽然类比并不完美，但考察[人类记忆类型](https://www.psychologytoday.com/us/basics/memory/types-of-memory?ref=blog.langchain.dev) 可以提供深刻的见解。一些研究（例如，[CoALA 论文](https://arxiv.org/pdf/2309.02427)）甚至将这些人类记忆类型映射到 AI 代理使用的记忆类型。

| 记忆类型          | 存储内容 | 人类示例      | 代理示例    |
| ------------- | ---- | --------- | ------- |
| [语义](#语义记忆)   | 事实   | 我在学校学到的东西 | 关于用户的事实 |
| [情景](#情景记忆)   | 经历   | 我做过的事情    | 过去的代理行为 |
| [程序性](#程序性记忆) | 指令   | 本能或运动技能   | 代理系统提示  |

#### 语义记忆

在人类和 AI 代理中，[语义记忆](https://en.wikipedia.org/wiki/Semantic_memory) 涉及特定事实和概念的保留。在人类中，它可以包括在学校学到的信息以及对概念及其关系的理解。对于 AI 代理，语义记忆通常用于通过记住过去交互中的事实或概念来个性化应用程序。

<Note>
  语义记忆不同于“语义搜索”，后者是一种使用“含义”（通常作为嵌入）查找相似内容的技术。语义记忆是心理学中的一个术语，指存储事实和知识，而语义搜索是一种基于含义而非精确匹配检索信息的方法。
</Note>

##### 个人资料

语义记忆可以通过不同方式管理。例如，记忆可以是关于用户、组织或其他实体（包括代理本身）的单一、持续更新的“个人资料”，包含范围明确且具体的信息。个人资料通常只是一个 JSON 文档，其中包含您选择的代表您领域的各种键值对。

在记住个人资料时，您需要确保每次都在**更新**个人资料。因此，您需要传入之前的个人资料，并[要求模型生成新的个人资料](https://github.com/langchain-ai/memory-template)（或应用于旧个人资料的某些 [JSON 补丁](https://github.com/hinthornw/trustcall)）。随着个人资料变大，这可能会变得容易出错，可能需要将个人资料拆分为多个文档，或在生成文档时进行**严格**解码，以确保记忆模式保持有效。

[](/oss/images/update-profile.png)

##### 集合

或者，记忆可以是随着时间推移不断更新和扩展的文档集合。每个单独的记忆可以范围更窄、更易于生成，这意味着您不太可能随着时间推移**丢失**信息。LLM 生成新信息的\_新\_对象比将新信息与现有个人资料协调起来更容易。因此，文档集合往往会导致[下游更高的召回率](https://en.wikipedia.org/wiki/Precision_and_recall)。

然而，这将一些复杂性转移到了记忆更新上。模型现在必须\_删除\_或\_更新\_列表中的现有项目，这可能很棘手。此外，一些模型可能默认过度插入，而其他模型可能默认过度更新。请参阅 [Trustcall](https://github.com/hinthornw/trustcall) 包了解管理此问题的一种方法，并考虑评估（例如，使用 [LangSmith](https://docs.smith.langchain.com/tutorials/Developers/evaluation) 等工具）以帮助您调整行为。

使用文档集合还将复杂性转移到对列表的记忆**搜索**上。`Store` 目前支持[语义搜索](https://langchain-ai.github.io/langgraph/reference/store/#langgraph.store.base.SearchOp.query) 和[按内容过滤](https://langchain-ai.github.io/langgraph/reference/store/#langgraph.store.base.SearchOp.filter)。

最后，使用记忆集合可能难以向模型提供全面的上下文。虽然单个记忆可能遵循特定模式，但这种结构可能无法捕捉记忆之间的完整上下文或关系。因此，在使用这些记忆生成响应时，模型可能缺乏在统一个人资料方法中更容易获得的重要上下文信息。

[](/oss/images/update-list.png)

无论采用何种记忆管理方法，核心要点是代理将使用语义记忆来[为其响应提供依据](/oss/python/langchain/retrieval)，这通常会导致更个性化和相关的交互。

#### 情景记忆

在人类和 AI 代理中，[情景记忆](https://en.wikipedia.org/wiki/Episodic_memory) 涉及回忆过去的事件或行为。[CoALA 论文](https://arxiv.org/pdf/2309.02427) 对此有很好的阐述：事实可以写入语义记忆，而*经历*可以写入情景记忆。对于 AI 代理，情景记忆通常用于帮助代理记住如何完成任务。

在实践中，情景记忆通常通过少样本示例提示实现，代理从过去的序列中学习以正确执行任务。有时“展示”比“讲述”更容易，LLM 从示例中学习效果很好。少样本学习允许您通过在提示中更新输入-输出示例来“[编程](https://x.com/karpathy/status/1627366413840322562)”您的 LLM，以说明预期行为。虽然可以使用各种最佳实践生成少样本示例，但挑战通常在于根据用户输入选择最相关的示例。

请注意，内存[存储](/oss/python/langgraph/persistence#memory-store) 只是将数据存储为少样本示例的一种方式。如果您希望有更多开发者参与，或将少样本示例更紧密地与您的评估框架绑定，您还可以使用 [LangSmith 数据集](/langsmith/index-datasets-for-dynamic-few-shot-example-selection#dynamic-few-shot-example-selection) 来存储您的数据。然后，动态少样本示例选择器可以开箱即用地实现相同的目标。LangSmith 将为您索引数据集，并根据关键词相似性（[使用类似 BM25 的算法](/langsmith/index-datasets-for-dynamic-few-shot-example-selection#dynamic-few-shot-example-selection) 进行基于关键词的相似性）检索与用户输入最相关的少样本示例。

请参阅此[视频](https://www.youtube.com/watch?v=37VaU7e7t5o) 了解 LangSmith 中动态少样本示例选择的示例用法。此外，请参阅此[博客文章](https://blog.langchain.dev/few-shot-prompting-to-improve-tool-calling-performance/) 展示少样本提示以提高工具调用性能，以及此[博客文章](https://blog.langchain.dev/aligning-llm-as-a-judge-with-human-preferences/) 使用少样本示例将 LLM 与人类偏好对齐。

#### 程序性记忆

在人类和 AI 代理中，[程序性记忆](https://en.wikipedia.org/wiki/Procedural_memory) 涉及记住用于执行任务的规则。在人类中，程序性记忆就像内化的知识，例如通过基本运动技能和平衡骑自行车。另一方面，情景记忆涉及回忆特定经历，例如第一次成功在没有辅助轮的情况下骑自行车，或一次穿越风景路线的难忘骑行。对于 AI 代理，程序性记忆是模型权重、代理代码和代理提示的组合，共同决定代理的功能。

在实践中，代理修改其模型权重或重写其代码的情况相当罕见。然而，代理修改其自身提示的情况更为常见。

一种有效的方法是通过“[反思](https://blog.langchain.dev/reflection-agents/)”或元提示来完善代理的指令。这涉及用当前指令（例如，系统提示）以及最近的对话或明确的用户反馈来提示代理。然后，代理根据此输入完善其自身指令。这种方法特别适用于指令难以事先指定的任务，因为它允许代理从其交互中学习和适应。

例如，我们使用外部反馈和提示重写构建了一个[Tweet 生成器](https://www.youtube.com/watch?v=Vn8A3BxfplE)，为 Twitter 生成高质量的论文摘要。在这种情况下，特定的摘要提示很难*a priori*指定，但用户很容易批评生成的 Tweets 并提供有关如何改进摘要过程的反馈。

以下伪代码展示了如何使用 LangGraph 内存[存储](/oss/python/langgraph/persistence#memory-store) 实现这一点，使用存储保存提示，`update_instructions` 节点获取当前提示（以及捕获在 `state["messages"]` 中的与用户的对话反馈），更新提示，并将新提示保存回存储。然后，`call_model` 从存储中获取更新后的提示并使用它生成响应。

```python theme={null}
# 使用指令的节点
def call_model(state: State, store: BaseStore):
    namespace = ("agent_instructions", )
    instructions = store.get(namespace, key="agent_a")[0]
    # 应用逻辑
    prompt = prompt_template.format(instructions=instructions.value["instructions"])
    ...

# 更新指令的节点
def update_instructions(state: State, store: BaseStore):
    namespace = ("instructions",)
    instructions = store.search(namespace)[0]
    # 内存逻辑
    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})
    ...
```

[](/oss/images/update-instructions.png)

### 写入记忆

代理写入记忆有两种主要方法：[“在热路径上”](#在热路径上) 和[“在后台”](#在后台)。

[](/oss/images/hot_path_vs_background.png)

#### 在热路径上

在运行时创建记忆既有优势也有挑战。积极的一面是，这种方法允许实时更新，使新记忆立即可用于后续交互。它还实现了透明度，因为可以通知用户何时创建和存储记忆。

然而，这种方法也带来了挑战。如果代理需要新工具来决定要提交到内存的内容，可能会增加复杂性。此外，推理要保存到内存的内容的过程可能会影响代理延迟。最后，代理必须在记忆创建和其他职责之间进行多任务处理，可能会影响创建记忆的数量和质量。

例如，ChatGPT 使用 [save\_memories](https://openai.com/index/memory-and-new-controls-for-chatgpt/) 工具将记忆作为内容字符串进行插入或更新，并在每个用户消息中决定是否以及如何使用此工具。请参阅我们的 [memory-agent](https://github.com/langchain-ai/memory-agent) 模板作为参考实现。

#### 在后台

将记忆创建作为单独的后台任务具有多个优势。它消除了主应用中的延迟，将应用逻辑与内存管理分离，并允许代理更专注于任务完成。这种方法还提供了在时间上灵活创建记忆以避免冗余工作的可能性。

然而，这种方法也有其自身的挑战。确定写入记忆的频率变得至关重要，因为不频繁的更新可能使其他线程缺乏新上下文。决定何时触发记忆形成也很重要。常见策略包括在设定时间段后调度（如果发生新事件则重新调度）、使用 cron 调度，或允许用户或应用逻辑手动触发。

请参阅我们的 [memory-service](https://github.com/langchain-ai/memory-template) 模板作为参考实现。

### 内存存储

LangGraph 将长期记忆作为 JSON 文档存储在[存储](/oss/python/langgraph/persistence#memory-store)中。每个记忆都组织在自定义 `namespace`（类似于文件夹）和唯一的 `key`（类似于文件名）下。命名空间通常包括用户或组织 ID 或其他标签，以便更轻松地组织信息。这种结构支持记忆的层次化组织。然后通过内容过滤器支持跨命名空间搜索。

```python theme={null}
from langgraph.store.memory import InMemoryStore


def embed(texts: list[str]) -> list[list[float]]:
    # 替换为实际的嵌入函数或 LangChain 嵌入对象
    return [[1.0, 2.0] * len(texts)]


# InMemoryStore 将数据保存到内存字典中。在生产环境中使用基于数据库的存储。
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": [
            "用户喜欢简短、直接的语言",
            "用户只说英语和 python",
        ],
        "my-key": "my-value",
    },
)
# 通过 ID 获取“记忆”
item = store.get(namespace, "a-memory")
# 在此命名空间内搜索“记忆”，按内容等价过滤，按向量相似性排序
items = store.search(
    namespace, filter={"my-key": "my-value"}, query="语言偏好"
)
```

有关内存存储的更多信息，请参阅 [持久化](/oss/python/langgraph/persistence#memory-store) 指南。
