likes
comments
collection
share

langchain入门三:memory-记忆组件,让大语言模型拥有记忆,燕咂,我刚刚说了啥?

作者站长头像
站长
· 阅读数 3

什么是memory

存储对话历史中的信息的能力称之为’记忆‘,这种工具可以单独使用,也可以无缝的集成到一条链中,记忆的存储长度是程序执行到结束,执行一次的所有记忆。

记忆组件需要支持

  • 读取
  • 写入

每条链定义了核心执行逻辑,期望某些输入,一些来自用户,一些来自记忆组件,在一次与LLM的交互中,链与记忆组件交互两次

  1. 读取记忆:将之前的交互内容进行读取,放入到本次交互中
  2. 写入记忆:将本次的交互内容写入到记忆当中

为什么需要使用记忆组件

在langchain中,直接使用llm.invoke进行大模型对话,llm的记忆范围只有层序执行到运行结束,再次对话就是新的开始,没有以前的记忆内容,当提问'我刚刚说了什么'时,他就回答不出前一次的交互内容.

from langchain_community.llms import Tongyi
llm=Tongyi()
print("第一次对话:",llm.invoke("今天天气真好啊"),"\n\n第二次对话:",llm.invoke("我刚刚说了什么"))

运行结果就是

langchain入门三:memory-记忆组件,让大语言模型拥有记忆,燕咂,我刚刚说了啥? 而使用记忆组件就可以让llm有记忆能力,能够将进行上下文联想.让与大模型对话时有和真人对话的感觉.

这里依然使用的是阿里云的通义千问大模型,如果你还没白嫖成功,请进入白嫖传送门

使用步骤

需要四个部件组合起来使用,大模型,提示词模板,链,记忆组件

  1. 实例化一个LLM
  2. 定义记忆组件
  3. 创建提示词模板
  4. 使用链将他们链接起来

四种记忆组件

ConversationBufferMemory会话缓冲区

如实的记录列表中记录的对话历史消息,并且是记录所有的历史消息,随着历史记录的增加,运行会越来越慢,直到大模型无法处理.适用于交互次数少,输入输出字符量不大的情况下

使用方法

import os
from dotenv import find_dotenv, load_dotenv
load_dotenv(find_dotenv())
DASHSCOPE_API_KEY=os.environ["DASHSCOPE_API_KEY"]
from langchain_community.llms import Tongyi
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
from langchain.memory import (ConversationBufferMemory)
   
llm=Tongyi()
template='''你是一个美少女,你的名字是燕砸,你的任务是用温柔的语气回答人类的问题。
        {chat_memory}
       human:{question}
    '''
prompt=PromptTemplate(
        template=template,
        input_variables=["question"]
)
#ConversationBufferMemory
memory = ConversationBufferMemory(memory_key="chat_memory",return_messages=False)
chain = LLMChain(
        llm=llm,
        prompt=prompt,
        memory=memory,
        verbose=True
        )

chain.invoke("我喜欢美食,我最喜欢的美食是清蒸鲈鱼")
chain.invoke("你是谁?")
chain.invoke("今天的天气真好啊")
res = chain.invoke("我最开始跟你聊的什么呢?")
print(res['text'])

在prompt中的template里面有一个{chat_memory},这就是记忆组件的输入也就是链与记忆组件的第一次交互

在memory定义中需要将这个记忆组件的输入定义出来

memory = ConversationBufferMemory(memory_key="chat_memory",return_messages=False)中的 memory_key就是记忆组件的输入,return_messages是返回值中是否带有记忆内容.

.env文件内容

DASHSCOPE_API_KEY="你的apikey"

看看输出结果:

langchain入门三:memory-记忆组件,让大语言模型拥有记忆,燕咂,我刚刚说了啥?

ConversationBufferWindowMemory会话缓冲窗口

持续记录对话历史,但只使用最近的k个交互。确保缓存大小不会过大,运行速度比较稳定

使用方法

import os
from dotenv import find_dotenv, load_dotenv
load_dotenv(find_dotenv())
DASHSCOPE_API_KEY=os.environ["DASHSCOPE_API_KEY"]
from langchain_community.llms import Tongyi
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
from langchain.memory import (ConversationBufferMemory,ConversationBufferWindowMemory,ConversationSummaryMemory)
   
llm=Tongyi()
template='''你是一个美少女,你的名字是燕砸,你的任务是用温柔的语气回答人类的问题。
        {chat_memory}
       human:{question}
    '''
prompt=PromptTemplate(
        template=template,
        input_variables=["question"]
)
#ConversationBufferWindowMemory
memory = ConversationBufferWindowMemory(memory_key="chat_memory",k=2,return_messages=False)

chain = LLMChain(
        llm=llm,
        prompt=prompt,
        memory=memory,
        verbose=True
        )

chain.invoke("我喜欢美食,我最喜欢的美食是清蒸鲈鱼")
chain.invoke("你是谁?")
chain.invoke("今天的天气真好啊")
res = chain.invoke("我最开始跟你聊的什么呢?")
print(res['text'])

memory = ConversationBufferWindowMemory(memory_key="chat_memory",k=3,return_messages=False)中的k就是记录交互的次数,其余两个参数与上一个一致

这里将k设置成2,看看输出结果:

langchain入门三:memory-记忆组件,让大语言模型拥有记忆,燕咂,我刚刚说了啥? 可以看到大模型确实有记忆,但是她只记得k=2个交互.

ConversationSummaryMemory会话摘要

随着时间的推移总结对话内容,并且将摘要存储在记忆中,需要的时候将摘要注入提示词或链中,缓存不会过大,运行稳定,但是运行速度比ConversationBufferWindowMemory慢很多,因为他在写入记忆的时候,做了一个摘要的操作.这使得她可以记住很长的交互记忆,不过随着交互的增加,摘要的内容不断迭代更换,使得某些内容会遗失.

使用方法

import os
from dotenv import find_dotenv, load_dotenv
load_dotenv(find_dotenv())
DASHSCOPE_API_KEY=os.environ["DASHSCOPE_API_KEY"]
from langchain_community.llms import Tongyi
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
from langchain.memory import (ConversationBufferMemory,ConversationBufferWindowMemory,ConversationSummaryMemory)
   
llm=Tongyi()
template='''你是一个美少女,你的名字是燕砸,你的任务是用温柔的语气回答人类的问题。
        {chat_memory}
       human:{question}
    '''
prompt=PromptTemplate(
        template=template,
        input_variables=["question"]
)
#ConversationSummaryMemory
memory = ConversationSummaryMemory(llm=llm, memory_key="chat_memory",return_messages=False)
chain = LLMChain(
        llm=llm,
        prompt=prompt,
        memory=memory,
        verbose=True
        )

chain.invoke("我喜欢美食,我最喜欢的美食是清蒸鲈鱼")
chain.invoke("你是谁?")
chain.invoke("今天的天气真好啊")
res = chain.invoke("我最开始跟你聊的什么呢?")
print(res['text'])

这个类型的记忆组件需要传入一个llm参数memory = ConversationSummaryMemory(llm=llm, memory_key="chat_memory",return_messages=False),使用llm来进行对话摘要. 看看输出结果:

langchain入门三:memory-记忆组件,让大语言模型拥有记忆,燕咂,我刚刚说了啥? 从输出内容看得出,确实有记忆,但是结果好像不太对,这可能就是摘要的缺点吧

再来看看最后一次输出的摘要内容:

langchain入门三:memory-记忆组件,让大语言模型拥有记忆,燕咂,我刚刚说了啥? 大致意思就是我问她最开始聊的什么,她提醒谈话内容是天气并且描述天气.她已经完全把鲈鱼搞没了

VectorStoreRetrieverMemory向量存储

将记忆存储在向量存储中,并在每次调用时查询前K个最"显著"的文档。 与大多数其他记忆类不同的是,它不明确跟踪交互的顺序。 在这种情况下,"文档"是先前对话片段。这对于提及AI在对话中早些时候被告知的相关信息可能是有用的。这段话是官方文档的描述,猜测应该是将记忆做成了一个文档,使用文档阅读器来进行读取

使用方式

from datetime import datetime
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.llms import OpenAI
from langchain.memory import VectorStoreRetrieverMemory
from langchain.chains import ConversationChain
from langchain.prompts import PromptTemplate
import faiss

from langchain.docstore import InMemoryDocstore
from langchain.vectorstores import FAISS

embedding_size = 1536 # OpenAIEmbeddings的维度
index = faiss.IndexFlatL2(embedding_size)
embedding_fn = OpenAIEmbeddings().embed_query
vectorstore = FAISS(embedding_fn, index, InMemoryDocstore({}), {})
llm = OpenAI(temperature=0) # 可以是任何有效的LLM
_DEFAULT_TEMPLATE = """以下是人类和AI之间友好的对话。AI健谈并从其上下文中提供了许多具体细节。如果AI不知道问题的答案,它会真诚地说自己不知道。

先前对话的相关部分:
{history}

(如果不相关,您无需使用这些信息)

当前对话:
人类:{input}
AI:"""
PROMPT = PromptTemplate(
    input_variables=["history", "input"], template=_DEFAULT_TEMPLATE
)
conversation_with_summary = ConversationChain(
    llm=llm, 
    prompt=PROMPT,
    # 出于测试目的,我们将max_token_limit设置得非常低。
    memory=memory,
    verbose=True
)
conversation_with_summary.predict(input="Hi, 我叫Perry,有什么新鲜事?")

这是官方的实例代码,有兴趣的可以进行尝试,这里就不过多展示.

总结

对于一个聊天机器人,在对话中可能需要进行上下文联想,分析的操作,或者是进行一个情景对话,记忆组件都是不可或缺的重要组成部分.在langchain的早期版本中,记忆组件运行速度非常的慢,如果作为一个请求内容返回给前端百分百会超时,在稳定的版本出来之后就流畅很多了,应用到实际的应用中也更具有体验感.在这样的条件下,对大模型进行角色定制,对话中这个角色的丰富度就会高很多.也能做出更多更有意思的聊天机器人.

更多有趣的langchain程序持续更新中~~

参考资料:LangChain