[Beta] 记忆(Memory)
大多数 LLM 应用都有对话式界面。对话的一个关键组成部分是能够引用对话中较早引入的信息。 至少,对话系统应该能够直接访问过去消息的某个窗口。 更复杂的系统需要一个不断更新的世界模型,使它能够做诸如维护实体及其关系信息之类的事情。
我们把这种存储过去交互信息的能力称为“记忆(memory)”。 LangChain 提供了许多工具来为系统添加记忆。 这些工具既可以单独使用,也可以无缝地集成到链中。
LangChain 中大多数与记忆相关的功能都被标记为 beta。原因有两个:
-
大多数功能(有一些例外,见下文)尚未达到生产就绪。
-
大多数功能(有一些例外,见下文)与旧的链(Legacy chain)配合使用,而不是较新的 LCEL 语法。
主要的例外是 ChatMessageHistory 功能。该功能在很大程度上已达到生产就绪,并且能与 LCEL 集成。
-
LCEL Runnable:关于如何将
ChatMessageHistory与 LCEL runnable 一起使用的概览,请参阅这些文档 -
集成 (opens in a new tab):关于各种
ChatMessageHistory集成的介绍,请参阅这些文档
简介
记忆系统需要支持两个基本操作:读取和写入。 回忆一下,每个链都定义了一些核心执行逻辑,它期望特定的输入。 其中一些输入直接来自用户,但另一些输入可以来自记忆。 在一次运行中,链会与其记忆系统交互两次。
- 在接收到初始用户输入之后、执行核心逻辑之前,链会从其记忆系统读取(READ)并扩充用户输入。
- 在执行核心逻辑之后、返回答案之前,链会把当前运行的输入和输出写入(WRITE)记忆,以便在未来的运行中可以引用它们。

将记忆构建到系统中
任何记忆系统的两个核心设计决策是:
- 状态如何存储
- 状态如何查询
存储:聊天消息列表
任何记忆的底层都是所有聊天交互的历史。 即使这些历史并非全部被直接使用,它们也需要以某种形式被存储。 LangChain 记忆模块的关键部分之一就是一系列用于存储这些聊天消息的集成, 从内存列表到持久化数据库。
- 聊天消息存储:如何使用聊天消息,以及所提供的各种集成。
查询:在聊天消息之上的数据结构和算法
维护一个聊天消息列表相当直接。 不那么直接的是建立在聊天消息之上的数据结构和算法,它们提供那些消息中最有用的视图。
一个非常简单的记忆系统可能每次运行只返回最近的消息。稍微复杂一点的记忆系统可能返回过去 K 条消息的简洁摘要。 更复杂的系统可能从存储的消息中提取实体,只返回当前运行中引用到的实体信息。
每个应用对记忆如何查询可能有不同的需求。记忆模块应该让上手简单记忆系统变得容易,并在需要时让你能编写自己的自定义系统。
- 记忆类型:构成 LangChain 所支持记忆类型的各种数据结构和算法
入门
让我们来看看 LangChain 中记忆实际上是什么样的。 这里我们将介绍与任意记忆类交互的基础知识。
让我们看看如何在链中使用 ConversationBufferMemory。
ConversationBufferMemory 是一种极其简单的记忆形式,它只是把聊天消息保存在一个缓冲区中,
并将它们传入提示模板。
from langchain.memory import ConversationBufferMemory
memory = ConversationBufferMemory()
memory.chat_memory.add_user_message("hi!")
memory.chat_memory.add_ai_message("what's up?")在链中使用记忆时,有几个关键概念需要理解。 请注意,这里我们介绍的是对大多数记忆类型都适用的通用概念。 每种单独的记忆类型很可能都有自己的参数和需要理解的概念。
从记忆返回哪些变量
进入链之前,会从记忆读取各种变量。
这些变量有特定的名称,需要与链期望的变量对齐。
你可以通过调用 memory.load_memory_variables({}) 查看这些变量是什么。
注意,我们传入的空字典只是真实变量的占位符。
如果你使用的记忆类型依赖于输入变量,你可能需要传入一些变量。
memory.load_memory_variables({}) {'history': "Human: hi!\nAI: what's up?"}在本例中,你可以看到 load_memory_variables 返回单个键 history。
这意味着你的链(很可能还有你的提示)应该期望一个名为 history 的输入。
你通常可以通过记忆类上的参数来控制这个变量。
例如,如果你希望记忆变量以 chat_history 为键返回,可以这样做:
memory = ConversationBufferMemory(memory_key="chat_history")
memory.chat_memory.add_user_message("hi!")
memory.chat_memory.add_ai_message("what's up?") {'chat_history': "Human: hi!\nAI: what's up?"}控制这些键的参数名可能因记忆类型而异,但重要的是要理解:(1) 这是可控的,(2) 如何控制它。
记忆是字符串还是消息列表
最常见的记忆类型之一涉及返回一个聊天消息列表。 它们既可以作为单个字符串全部拼接在一起返回(当它们将被传入 LLM 时有用), 也可以作为 ChatMessage 列表返回(当传入 ChatModel 时有用)。
默认情况下,它们作为单个字符串返回。
要以消息列表返回,你可以设置 return_messages=True
memory = ConversationBufferMemory(return_messages=True)
memory.chat_memory.add_user_message("hi!")
memory.chat_memory.add_ai_message("what's up?") {'history': [HumanMessage(content='hi!', additional_kwargs={}, example=False),
AIMessage(content='what's up?', additional_kwargs={}, example=False)]}哪些键被保存到记忆
链经常会接收或返回多个输入/输出键。
在这些情况下,我们怎么知道要把哪些键保存到聊天消息历史中?
这通常可以通过记忆类型上的 input_key 和 output_key 参数控制。
它们默认为 None——如果只有一个输入/输出键,就会自动使用它。
然而,如果有多个输入/输出键,那么你 必须 指定使用哪一个的名称。
端到端示例
最后,让我们看看在链中使用它。
我们将使用 LLMChain,并演示如何同时配合 LLM 和 ChatModel 使用。
使用 LLM
from langchain_openai import OpenAI
from langchain_core.prompts import PromptTemplate
from langchain.chains import LLMChain
from langchain.memory import ConversationBufferMemory
llm = OpenAI(temperature=0)
# 注意 "chat_history" 出现在提示模板中
template = """You are a nice chatbot having a conversation with a human.
Previous conversation:
{chat_history}
New human question: {question}
Response:"""
prompt = PromptTemplate.from_template(template)
# 注意我们需要对齐 `memory_key`
memory = ConversationBufferMemory(memory_key="chat_history")
conversation = LLMChain(
llm=llm,
prompt=prompt,
verbose=True,
memory=memory
)# 注意我们只传入 `question` 变量——`chat_history` 由记忆填充
conversation({"question": "hi"})使用 ChatModel
from langchain_openai import ChatOpenAI
from langchain_core.prompts import (
ChatPromptTemplate,
MessagesPlaceholder,
SystemMessagePromptTemplate,
HumanMessagePromptTemplate,
)
from langchain.chains import LLMChain
from langchain.memory import ConversationBufferMemory
llm = ChatOpenAI()
prompt = ChatPromptTemplate(
messages=[
SystemMessagePromptTemplate.from_template(
"You are a nice chatbot having a conversation with a human."
),
# 这里的 `variable_name` 必须与 memory 对齐
MessagesPlaceholder(variable_name="chat_history"),
HumanMessagePromptTemplate.from_template("{question}")
]
)
# 注意我们设置 `return_messages=True` 以适配 MessagesPlaceholder
# 注意 `"chat_history"` 与 MessagesPlaceholder 的名称对齐。
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
conversation = LLMChain(
llm=llm,
prompt=prompt,
verbose=True,
memory=memory
)# 注意我们只传入 `question` 变量——`chat_history` 由记忆填充
conversation({"question": "hi"})后续步骤
以上就是入门介绍! 关于更高级主题的演练,请参阅其他章节, 例如自定义记忆、多个记忆等。