人生和电影不一样,人生要辛苦多了。
01 Memory介绍
Memory(记忆)是LangChain中的一个重要组成部分,它允许模型在处理请求时能够访问历史对话记录或其他相关上下文信息,从而使得对话更加连贯和自然。
LangChain Memory 的作用
- 上下文管理:通过保存历史对话,模型可以基于之前的对话内容来生成更相关的响应。
- 状态跟踪:对于需要持续跟踪用户状态的应用程序来说,Memory 可以帮助维护会话的状态信息。
- 个性化体验:通过记录用户的偏好或历史选择,可以提供更加个性化的用户体验。
02 如何使用
这里以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。这意味着我们Prompt应该需要一个名为 history 的输入占位符。但是,也可以通过内存类上的参数(memory_key)来控制此变量。
memory = ConversationBufferMemory(memory_key="chat_history")
memory.chat_memory.add_user_message("hi!")
memory.chat_memory.add_ai_message("what's up?")
memory.load_memory_variables({})
"""
{'chat_history': "Human: hi!\nAI: what's up?"}
"""
默认情况下,memory 作为单个字符串返回。为了作为消息列表返回,您可以设置 return_messages=True
memory = ConversationBufferMemory(return_messages=True)
memory.chat_memory.add_user_message("hi!")
memory.chat_memory.add_ai_message("what's up?")
memory.load_memory_variables({})
"""
{'history': [HumanMessage(content='hi!'), AIMessage(content="what's up?")]}
"""
完整示例如下:
from langchain_core.prompts import ChatPromptTemplate, SystemMessagePromptTemplate, MessagesPlaceholder, \
HumanMessagePromptTemplate
from langchain_core.prompts import PromptTemplate
from langchain.chains import LLMChain
from langchain.memory import ConversationBufferMemory
# 使用LLM用如下格式的模板
# llm_model = OpenAI(temperature=0)
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)
# 使用ChatModel用如下格式的模板
# chat_model = ChatOpenAI(temperature=0)
"""
prompt = ChatPromptTemplate(
messages=[
SystemMessagePromptTemplate.from_template(
"You are a nice chatbot having a conversation with a human."
),
# The `variable_name` here is what must align with memory
MessagesPlaceholder(variable_name="chat_history"),
HumanMessagePromptTemplate.from_template("{question}")
]
)
"""
# Notice that we need to align the `memory_key`
memory = ConversationBufferMemory(memory_key="chat_history",return_messages=True)
conversation = LLMChain(
llm=llm_model,
prompt=prompt,
verbose=True,
memory=memory
)
# Notice that we just pass in the `question` variables - `chat_history` gets populated by memory
conversation({"question": "hi"})
"""
{'question': 'hi',
'chat_history': [HumanMessage(content='hi'),
AIMessage(content='Hello! How can I help you today?')],
'text': 'Hello! How can I help you today?'}
"""
conversation({"question": "I am learning about LangChain."})
"""
{'question': 'I am learning about LangChain.',
'chat_history': [HumanMessage(content='hi'),
AIMessage(content='Hello! How can I help you today?'),
HumanMessage(content='I am learning about LangChain.'),
AIMessage(content="That's great! LangChain is a powerful tool for building applications that interact with language models in complex ways. What aspects of LangChain are you exploring or do you have any specific questions about it?")],
'text': "That's great! LangChain is a powerful tool for building applications that interact with language models in complex ways. What aspects of LangChain are you exploring or do you have any specific questions about it?"}
"""
03 LangChain中的内置Memory
Conversation Buffer
ConversationBufferMemory。此内存允许存储消息,然后在变量中提取消息。
Conversation Buffer Window
ConversationBufferWindowMemory。ConversationBufferWindowMemory 保留一段时间内对话的交互列表。它只使用最后的 K 个交互。这对于保持最新交互的滑动窗口非常有用,因此缓冲区不会变得太大。
ConversationBufferWindowMemory 是 LangChain 中的一种 Memory 类型,它通过维护一个固定大小的对话历史窗口来管理对话上下文。这种 Memory 类型特别适用于需要控制对话历史长度以避免内存过度消耗的场景。
ConversationBufferWindowMemory 的工作原理
- 对话缓冲区:它会存储最近的对话历史记录。
- 窗口大小:设置一个固定的窗口大小来限制对话历史的长度。
- 循环:随着新对话的进行,最新的对话会被添加到缓冲区,而最早的对话记录会被移除以保持窗口大小不变。
from langchain.chat_models import ChatOpenAI
from langchain.memory import ConversationBufferWindowMemory
from langchain.chains import ConversationChain
# 初始化大模型
llm = ChatOpenAI(temperature=0)
# 创建记忆实例
memory = ConversationBufferWindowMemory(k=5) # 设置窗口大小为 5
# 创建对话链并设置记忆
conversation = ConversationChain(
llm=llm,
memory=memory,
verbose=True
)
# 开始对话
response = conversation.predict(input="你好!")
print(response)
response = conversation.predict(input="你能帮我查一下天气吗?")
print(response)
参数说明
- k: 控制对话历史的窗口大小。一旦达到这个限制,最早的对话记录会被移除,以保证窗口大小不变。
优点
- 节省内存:通过限制对话历史的长度来减少内存占用。
- 保持上下文:即使对话历史中的部分记录被移除,也能确保模型了解最新的对话背景。
注意事项
- 窗口大小的选择:合理设置窗口大小以平衡内存使用和上下文保留的需求。
- 对话连贯性:虽然这种方法可以有效控制内存使用,但在某些情况下可能会影响对话的连贯性,特别是当对话跨越多个会话且涉及多个主题时。
通过使用 ConversationBufferWindowMemory,可以构建出既能处理长时间对话又能有效管理资源的应用程序。这对于需要控制内存使用同时保持对话上下文连贯性的场景非常有用。
Entity
ConversationEntityMemory。实体内存会记住对话中有关特定实体的给定事实。它提取有关实体的信息(使用 LLM),并随着时间的推移积累有关该实体的知识(也使用 LLM)。(会多次调用LLM做结构化提取,这个memory用的很少)
Conversation Knowledge Graph
ConversationKGMemory。顾名思义,这种类型的内存使用知识图谱来重新创建内存。
ConversationKGMemory 在 LangChain 中是一个用于管理对话历史的高级 Memory 类型,它通过构建知识图谱(Knowledge Graph, KG)的方式来维护对话上下文。这种方法不仅能够捕捉对话中的实体和关系,还能够利用这些信息来增强对话模型的理解能力和响应质量。
ConversationKGMemory 的工作原理
- 实体识别:从对话中提取实体(如人名、地点、时间等)。
- 关系抽取:识别实体之间的关系。
- 图谱构建:将实体及其关系构建成知识图谱。
- 查询和推理:利用图谱进行查询和推理,以便更好地理解和响应用户的请求。
from langchain.chat_models import ChatOpenAI
from langchain.memory import ConversationKGMemory
from langchain.chains import ConversationChain
# 初始化大模型
llm = ChatOpenAI(temperature=0)
# 创建记忆实例
memory = ConversationKGMemory(llm=llm)
# 创建对话链并设置记忆
conversation = ConversationChain(
llm=llm,
memory=memory,
verbose=True
)
# 开始对话
response = conversation.predict(input="你好!")
print(response)
response = conversation.predict(input="我想知道明天北京的天气怎么样?")
print(response)
参数说明
- llm: 大模型实例,用于生成摘要或执行其他文本处理任务。
- graph: 可选参数,用于指定知识图谱的实现方式,默认情况下使用默认的知识图谱实现。
优点
- 增强理解能力:通过构建知识图谱,模型能够更好地理解对话中的实体和它们之间的关系。
- 上下文管理:能够有效地管理对话的历史上下文,使对话更加连贯。
- 推理能力:利用知识图谱进行推理,可以提供更准确和相关的响应。
注意事项
- 性能考虑:构建和维护知识图谱可能会消耗较多的计算资源,特别是在处理大规模对话数据时。
- 准确性:实体识别和关系抽取的准确性对最终的对话质量有重要影响。
通过使用 ConversationKGMemory,可以构建出具有更强理解和推理能力的对话系统,尤其是在涉及复杂实体和关系的场景中。这有助于提升对话系统的智能水平和用户体验。
Conversation Summary
ConversationSummaryMemory。这种类型的内存会随着时间的推移创建对话的摘要。这对于随着时间的推移浓缩对话中的信息非常有用。对话摘要内存在对话发生时对其进行摘要,并将当前摘要存储在内存中。然后,可以使用此内存将迄今为止的对话摘要注入到提示/链中。此内存对于较长的对话最有用,因为在较长的对话中,逐字保留过去的消息历史记录会占用太多令牌。
ConversationSummaryMemory 是 LangChain 中的一种 Memory 类型,它通过定期生成对话摘要来管理对话历史。这种 Memory 类型特别适合于需要长期记忆但又希望避免内存消耗过大的场景。
ConversationSummaryMemory 的工作原理
- 对话缓冲区:它会暂时保存最近的对话历史。
- 对话摘要:当对话历史达到一定长度后,会生成一次摘要,并将摘要存储起来。
- 循环:随着新对话的进行,上述过程会不断重复,即新的对话会被添加到缓冲区,当达到阈值时再次生成摘要并清空缓冲区。
from langchain.chat_models import ChatOpenAI
from langchain.memory import ConversationSummaryMemory
from langchain.chains import ConversationChain
# 初始化大模型
llm = ChatOpenAI(temperature=0)
# 创建记忆实例
memory = ConversationSummaryMemory(llm=llm, max_token_limit=200) # 设置最大 token 数限制
# 创建对话链并设置记忆
conversation = ConversationChain(
llm=llm,
memory=memory,
verbose=True
)
# 开始对话
response = conversation.predict(input="你好!")
print(response)
response = conversation.predict(input="你能帮我查一下天气吗?")
print(response)
参数说明
- max_token_limit: 控制对话历史的最大 token 数量。一旦达到这个限制,就会生成一次摘要,并将摘要存入 Memory 中,同时清空当前的对话历史。
优点
- 节省内存:通过定期生成摘要来减少内存占用。
- 保持上下文:即使对话历史被清空,摘要也能确保模型了解之前的对话背景。
注意事项
- 摘要的质量:摘要的质量直接影响后续对话的理解。因此,选择合适的大模型来生成摘要非常重要。
- token 限制:合理设置 max_token_limit 以平衡内存使用和上下文保留的需求。
通过使用 ConversationSummaryMemory,您可以构建出既能处理长时间对话又能有效管理资源的应用程序。
Conversation Summary Buffer
ConversationSummaryBufferMemory。ConversationSummaryBufferMemory 是 LangChain 中的一种 Memory 类型,它结合了对话缓冲区(buffer)和对话摘要(summary)两种机制的优点。这种 Memory 类型特别适合于需要长期记忆但又希望避免内存消耗过大的场景。
ConversationSummaryBufferMemory 的工作原理
- 对话缓冲区:它会暂时保存最近的对话历史。
- 对话摘要:当对话历史达到一定长度后,会生成一次摘要,并将摘要存储起来。之后的对话历史会被清空。
- 循环:随着新对话的进行,上述过程会不断重复,即新的对话会被添加到缓冲区,当达到阈值时再次生成摘要并清空缓冲区。
from langchain.chat_models import ChatOpenAI
from langchain.memory import ConversationSummaryBufferMemory
from langchain.chains import ConversationChain
# 初始化大模型
llm = ChatOpenAI(temperature=0)
# 创建记忆实例
memory = ConversationSummaryBufferMemory(llm=llm, max_token_limit=200) # 设置最大 token 数限制
# 创建对话链并设置记忆
conversation = ConversationChain(
llm=llm,
memory=memory,
verbose=True
)
# 开始对话
response = conversation.predict(input="你好!")
print(response)
response = conversation.predict(input="你能帮我查一下天气吗?")
print(response)
参数说明
- max_token_limit: 控制对话历史的最大 token 数量。一旦达到这个限制,就会生成一次摘要,并将摘要存入 Memory 中,同时清空当前的对话历史。
优点
- 节省内存:通过定期生成摘要来减少内存占用。
- 保持上下文:即使对话历史被清空,摘要也能确保模型了解之前的对话背景。
注意事项
- 摘要的质量:摘要的质量直接影响后续对话的理解。因此,选择合适的大模型来生成摘要非常重要。
- token 限制:合理设置 max_token_limit 以平衡内存使用和上下文保留的需求。
通过使用 ConversationSummaryBufferMemory,您可以构建出既能处理长时间对话又能有效管理资源的应用程序。
Conversation Token Buffer
ConversationTokenBufferMemory。ConversationTokenBufferMemory 在内存中保留最近交互的缓冲区,并使用令牌长度而不是交互数来确定何时刷新交互。
ConversationTokenBufferMemory 是 LangChain 中的一种 Memory 类型,它主要用于管理对话历史记录,特别适用于需要控制对话历史长度和 token 数量的场景。这种 Memory 类型通过限制存储的 token 数量来优化内存使用,同时确保对话上下文的连贯性。
ConversationTokenBufferMemory 的工作原理
- 对话缓冲区:它会存储最近的对话历史记录。
- token 限制:当对话历史中的 token 数量超过设定的上限时,最旧的对话记录会被移除,以保证总 token 数不超过限制。
- 循环:随着新对话的进行,上述过程会不断重复,即新的对话会被添加到缓冲区,当达到 token 限制时,最早的对话记录会被移除。
from langchain.chat_models import ChatOpenAI
from langchain.memory import ConversationTokenBufferMemory
from langchain.chains import ConversationChain
# 初始化大模型
llm = ChatOpenAI(temperature=0)
# 创建记忆实例
memory = ConversationTokenBufferMemory(llm=llm, max_token_limit=200) # 设置最大 token 数限制
# 创建对话链并设置记忆
conversation = ConversationChain(
llm=llm,
memory=memory,
verbose=True
)
# 开始对话
response = conversation.predict(input="你好!")
print(response)
response = conversation.predict(input="你能帮我查一下天气吗?")
print(response)
参数说明
- max_token_limit: 控制对话历史的最大 token 数量。一旦达到这个限制,最旧的对话记录会被移除,以保证总 token 数不超过限制。
优点
- 节省内存:通过限制 token 数量来减少内存占用。
- 保持上下文:即使对话历史中的部分记录被移除,也能确保模型了解最新的对话背景。
注意事项
- token 限制的选择:合理设置 max_token_limit 以平衡内存使用和上下文保留的需求。
- 对话连贯性:虽然这种方法可以有效控制内存使用,但在某些情况下可能会影响对话的连贯性,特别是当对话跨越多个会话且涉及多个主题时。
通过使用 ConversationTokenBufferMemory,可以构建出既能处理长时间对话又能有效管理资源的应用程序。这对于需要控制内存使用同时保持对话上下文连贯性的场景非常有用。
Backed by a Vector Store
VectorStoreRetrieverMemory。VectorStoreRetrieverMemory 将内存存储在向量存储中,并在每次调用时查询前 K 个最“突出”的文档。这与大多数其他 Memory 类的不同之处在于它不显式跟踪交互的顺序。
VectorStoreRetrieverMemory 是 LangChain 中一种高级 Memory 类型,它通过结合向量存储(Vector Store)和检索器(Retriever)来管理对话历史。这种 Memory 类型特别适合于需要处理大量对话历史记录的场景,因为它能够高效地检索相关信息而不会导致内存过度膨胀。
VectorStoreRetrieverMemory 的工作原理
- 向量存储:将对话历史转换为向量,并存储在向量数据库中。
- 检索器:当接收到新的输入时,使用检索器从向量数据库中查找最相关的对话历史记录。
- 上下文提供:将找到的相关对话历史作为上下文提供给模型,以便生成更相关的响应。
from langchain.chat_models import ChatOpenAI
from langchain.vectorstores import Chroma
from langchain.embeddings import OpenAIEmbeddings
from langchain.memory import VectorStoreRetrieverMemory
from langchain.chains import ConversationChain
# 初始化大模型
llm = ChatOpenAI(temperature=0)
# 初始化向量存储
embedding = OpenAIEmbeddings()
vectorstore = Chroma(embedding_function=embedding)
# 创建记忆实例
memory = VectorStoreRetrieverMemory(vectorstore=vectorstore, k=2) # 设置检索 top-k 相关文档
# 创建对话链并设置记忆
conversation = ConversationChain(
llm=llm,
memory=memory,
verbose=True
)
# 开始对话
response = conversation.predict(input="你好!")
print(response)
response = conversation.predict(input="你能帮我查一下天气吗?")
print(response)
参数说明
- vectorstore: 向量数据库实例,用于存储对话历史的向量表示。
- k: 检索时返回的最相关文档数量。
优点
- 高效检索:通过向量检索技术快速找到最相关的对话历史记录。
- 节省内存:向量存储可以有效地管理大量的对话历史,避免内存过度膨胀。
- 上下文管理:能够提供相关的上下文信息,使模型能够生成更准确的响应。
注意事项
- 向量存储的选择:根据具体需求选择合适的向量存储库,例如 Chroma、Pinecone 等。
- 嵌入模型:选择合适的嵌入模型来生成高质量的向量表示,这对于检索结果的质量至关重要。
- 检索参数:合理设置检索参数(如 k 值),以平衡检索速度和相关性。
通过使用 VectorStoreRetrieverMemory,可以构建出既能处理大量对话历史记录又能高效管理资源的应用程序。这对于需要处理大规模数据集的场景非常有用。
04 总结
本质上,Memory 是存储对话历史(至于存在哪里,无所谓),然后对历史消息做处理(至于怎么处理,无所谓),最后将处理后的数据填充到 Prompt 中一起提交给 LLM 做问答。
如果能帮我点个免费的关注,那就是对我个人的最大的肯定。如果觉得写的还行,分享一下也是我生活的小确幸~
以上内容依据官方文档编写,官方地址:https://python.langchain.com/docs/modules/memory
Peace Guys~
- 【LangChain进阶教程】十三、LangChain进阶之Chains
- 【LangChain进阶教程】十二、LangChain进阶之Agents
- 【LangChain进阶教程】十一、LangChain进阶之Tools
- 【LangChain进阶教程】十、LangChain进阶之Retrievers