Skip to main content

内存 Memory

大多数LLM应用程序都具有对话界面。对话的一个重要组成部分是能够引用先前介绍的信息。至少,对话系统应该能够直接访问一些过去消息的窗口。更复杂的系统需要具有一个不断更新的世界模型,以便能够维护有关实体及其关系的信息。

我们将存储有关过去交互的信息的能力称为"内存Memory"。LangChain为系统添加内存提供了许多实用工具。这些实用工具可以单独使用,也可以无缝地集成到链中。

内存系统需要支持两个基本操作:读取和写入。请记住,每个链都定义了一些核心执行逻辑,这些逻辑期望某些输入。其中一些输入直接来自用户,但其中一些输入可以来自内存。在给定的运行中,链将与其内存系统交互两次。

  1. 在接收到初始用户输入但在执行核心逻辑之前,链将从其内存系统中读取并增强用户输入。
  2. 在执行核心逻辑但在返回答案之前,链将将当前运行的输入和输出写入内存,以便将来可以引用它们。

内存图

将内存集成到系统中

任何内存系统中的两个核心设计决策是:

  • 如何存储状态
  • 如何查询状态

存储:聊天消息列表

任何内存的基础都是所有聊天交互的历史记录。即使这些记录并不直接使用,它们仍然需要以某种形式存储。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("whats up?")

在链中使用内存时,有一些关键概念需要理解。请注意,这里我们介绍的是对大多数类型的内存都有用的一般概念。每个单独的内存类型可能都有自己的参数和概念,需要理解。

内存返回哪些变量

在进入链之前,从内存中读取各种变量。这些变量具有特定的名称,需要与链所期望的变量对齐。您可以通过调用memory.load_memory_variables({})来查看这些变量是什么。请注意,我们传入的空字典只是真实变量的占位符。如果您使用的内存类型依赖于输入变量,您可能需要传入一些变量。

memory.load_memory_variables({})

输出结果如下:

{'history': "Human: hi!\nAI: whats 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("whats up?")

输出结果如下:

{'chat_history': "Human: hi!\nAI: whats up?"}

控制这些键的参数名称可能因内存类型而异,但重要的是要理解:(1)这是可控制的,(2)如何控制。

内存是字符串还是消息列表

以下是对于代码中的英文的翻译,其他内容保留原文。

其中最常见的一种记忆类型是返回一个聊天消息列表。这些消息可以作为一个单独的字符串返回,将它们全部连接在一起(当它们将被传递给LLMs时很有用),或者作为一个ChatMessages列表返回(当它们被传递给ChatModels时很有用)。

默认情况下,它们作为一个单独的字符串返回。为了返回一个消息列表,您可以设置return_messages=True

memory = ConversationBufferMemory(return_messages=True)
memory.chat_memory.add_user_message("hi!")
memory.chat_memory.add_ai_message("whats up?")

输出结果为:

{'history': [HumanMessage(content='hi!', additional_kwargs={}, example=False),
AIMessage(content='whats up?', additional_kwargs={}, example=False)]}

存储在记忆中的键有哪些?

通常情况下,链条会接收或返回多个输入/输出键。在这些情况下,我们如何知道要保存哪些键到聊天消息历史记录中呢?这通常可以通过内存类型的input_keyoutput_key参数来控制。默认情况下,它们都为None - 如果只有一个输入/输出键,则知道只使用该键。然而,如果有多个输入/输出键,则必须指定要使用的键的名称。

端到端示例

最后,让我们来看看如何在链式中使用它。我们将使用LLMChain,并展示如何与LLM和ChatModel一起使用。

使用LLM​

from langchain.llms import OpenAI  
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain
from langchain.memory import ConversationBufferMemory


llm = OpenAI(temperature=0)
# 注意在prompt模板中出现了"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`会由memory填充
conversation({"question": "hi"})

使用ChatModel​

from langchain.chat_models import ChatOpenAI  
from langchain.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`会由memory填充
conversation({"question": "hi"})

下一步​

这就是入门的全部内容!请查看其他部分,了解更高级的主题,如自定义内存、多个内存等。