当前位置:   article > 正文

LangChain - Memory_langchain memory

langchain memory


转载改编自:https://python.langchain.com.cn/docs/modules/memory/


一、内存记忆 ( Memory )

默认情况下,链式模型和代理模型都是无状态的,这意味着它们 将每个传入的查询 独立处理(就像底层的 LLMs 和聊天模型本身一样)。
在某些应用程序中,比如聊天机器人,记住先前的交互是至关重要的。
无论是短期还是长期,都要记住先前的交互。
Memory 类正是做到了这一点。

LangChain 提供了两种形式的记忆组件。
首先,LangChain 提供了用于管理和操作以前的聊天消息的辅助工具。
这些工具被设计成模块化的,无论如何使用都很有用。

其次,LangChain 提供了将这些工具轻松整合到链式模型中的方法。


入门

记忆 涉及在用户与语言模型的交互过程中 始终保留状态 的概念。
用户与语言模型的交互被捕获在 ChatMessages 的概念中,因此这归结为在一系列聊天消息中摄取、捕获、转换和提取知识。
有许多不同的方法可以做到这一点,每种方法都作为自己的记忆类型存在。

通常情况下,对于每种类型的记忆,有两种理解和使用记忆的方式。
一种是独立的函数,从一系列消息中提取信息,然后是您可以在链式模型中使用此类型的记忆的方式。
记忆可以返回多个信息片段(例如,最近的 N 条消息和所有先前消息的摘要)。
返回的信息可以是字符串或消息列表。

我们将介绍最简单的存储形式:“缓冲”存储,它只涉及保留所有先前的消息的缓冲区。
我们将展示如何在这里使用模块化实用函数,然后展示它如何在链中使用(返回字符串以及消息列表)。


聊天消息历史 (ChatMessageHistory)

大多数(如果不是全部)内存模块的核心实用类之一是 ChatMessageHistory 类。
这是一个超轻量级的包装器,它公开了方便的方法来保存人类消息、AI 消息,然后获取它们全部。

如果您在链外管理内存,可能需要直接使用此类。

from langchain.memory import ChatMessageHistory

history = ChatMessageHistory()

history.add_user_message("hi!")

history.add_ai_message("whats up?")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

history.messages
  • 1

    [HumanMessage(content='hi!', additional_kwargs={}),
     AIMessage(content='whats up?', additional_kwargs={})]
  • 1
  • 2

ConversationBufferMemory

现在我们展示如何在链中使用这个简单的概念。
我们首先展示 ConversationBufferMemory,它只是 ChatMessageHistory 的一个包装器,可以提取变量中的消息。

我们可以首先将其提取为字符串。

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({})
# -> {'history': 'Human: hi!\nAI: whats up?'}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

我们还可以将历史记录作为消息列表获取

memory = ConversationBufferMemory(return_messages=True)
memory.chat_memory.add_user_message("hi!")
memory.chat_memory.add_ai_message("whats up?")
 
memory.load_memory_variables({})
  • 1
  • 2
  • 3
  • 4
  • 5

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

Using in a chain

最后,我们来看chain中的这个 (设置 verbose=True 这样我们可以看到 prompt ).

from langchain.llms import OpenAI
from langchain.chains import ConversationChain

llm = OpenAI(temperature=0)

conversation = ConversationChain(
    llm=llm,
    verbose=True,
    memory=ConversationBufferMemory()
)

conversation.predict(input="Hi there!")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

    > Entering new ConversationChain chain...
    Prompt after formatting:
    The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.

    Current conversation:

    Human: Hi there!
    AI:

    > Finished chain.

    " Hi there! It's nice to meet you. How can I help you today?"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

conversation.predict(input="I'm doing well! Just having a conversation with an AI.")
  • 1

conversation.predict(input="Tell me about yourself.")
  • 1

保存消息历史

您经常需要保存消息,然后加载它们以便再次使用。
这可以通过先将消息转换为普通的 Python 字典,保存这些字典(如 json 或其他格式),然后加载它们来轻松完成。以下是一个示例。

import json

from langchain.memory import ChatMessageHistory
from langchain.schema import messages_from_dict, messages_to_dict

history = ChatMessageHistory()

history.add_user_message("hi!")

history.add_ai_message("whats up?")

dicts = messages_to_dict(history.messages)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

dicts

    [{'type': 'human', 'data': {'content': 'hi!', 'additional_kwargs': {}}},
     {'type': 'ai', 'data': {'content': 'whats up?', 'additional_kwargs': {}}}]
  • 1
  • 2

new_messages = messages_from_dict(dicts)
  • 1

new_messages

    [HumanMessage(content='hi!', additional_kwargs={}),
     AIMessage(content='whats up?', additional_kwargs={})]
  • 1
  • 2

二、如何为 LLMChain 添加记忆

本笔记本演示了如何在 LLMChain 中使用 Memory 类。在本演示中,我们将添加 ConversationBufferMemory 类,但实际上可以使用任何记忆类。

from langchain.memory import ConversationBufferMemory
from langchain import OpenAI, LLMChain, PromptTemplate
  • 1
  • 2

最重要的一步是正确设置提示。
在下面的提示中,我们有两个输入键:一个用于实际输入,另一个用于来自 Memory 类的输入。
重要的是,确保 PromptTemplate 和 ConversationBufferMemory 中的键匹配(chat_history)。

template = """You are a chatbot having a conversation with a human.

{chat_history}

Human: {human_input}
Chatbot:"""

prompt = PromptTemplate(
    input_variables=["chat_history", "human_input"],
    template=template
)

memory = ConversationBufferMemory(memory_key="chat_history")

llm_chain = LLMChain(
    llm=OpenAI(),
    prompt=prompt,
    verbose=True,
    memory=memory,
)

llm_chain.predict(human_input="Hi there my friend")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

llm_chain.predict(human_input="Not too bad - how are you?")
  • 1

三、对多输入 Chain 添加记忆

大多数记忆对象都适用于单个输入。
在本笔记本中,我们将介绍如何向具有多个输入的链条添加记忆。

作为此类链条的示例,我们将为一个问答链条添加记忆。

该链条的输入包括相关文档和用户提出的问题。

from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.embeddings.cohere import CohereEmbeddings
from langchain.text_splitter import CharacterTextSplitter
from langchain.vectorstores.elastic_vector_search import ElasticVectorSearch
from langchain.vectorstores import Chroma
from langchain.docstore.document import Document

with open("../../state_of_the_union.txt") as f:
    state_of_the_union = f.read()
    
text_splitter = CharacterTextSplitter(chunk_size=1000, 
													chunk_overlap=0)

texts = text_splitter.split_text(state_of_the_union)

embeddings = OpenAIEmbeddings()

docsearch = Chroma.from_texts(
    texts, embeddings, 
    metadatas=[{"source": i} for i in range(len(texts))]
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

Running Chroma using direct local API.
Using DuckDB in-memory for database. Data will be transient.
  • 1
  • 2

query = "What did the president say about Justice Breyer"
docs = docsearch.similarity_search(query)
  • 1
  • 2

from langchain.chains.question_answering import load_qa_chain
from langchain.llms import OpenAI
from langchain.prompts import PromptTemplate
from langchain.memory import ConversationBufferMemory

template = """You are a chatbot having a conversation with a human.

Given the following extracted parts of a long document and a question, create a final answer.

{context}

{chat_history}
Human: {human_input}
Chatbot:"""

prompt = PromptTemplate(
    input_variables=["chat_history", "human_input", "context"], template=template
)
memory = ConversationBufferMemory(memory_key="chat_history", input_key="human_input")
chain = load_qa_chain(
    OpenAI(temperature=0), chain_type="stuff", memory=memory, prompt=prompt
)

query = "What did the president say about Justice Breyer"
chain({"input_documents": docs, "human_input": query}, return_only_outputs=True)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

{'output_text': ' Tonight, I’d like to honor someone who has dedicated his life to serve this country: Justice Stephen Breyer—an Army veteran, Constitutional scholar, and retiring Justice of the United States Supreme Court. Justice Breyer, thank you for your service.'}
  • 1

print(chain.memory.buffer)
  • 1

Human: What did the president say about Justice Breyer
AI:  Tonight, I’d like to honor someone who has dedicated his life to serve this country: Justice Stephen Breyer—an Army veteran, Constitutional scholar, and retiring Justice of the United States Supreme Court. Justice Breyer, thank you for your service.
  • 1
  • 2

四、向代理添加记忆

本笔记本将介绍如何 向代理添加记忆。
在阅读本笔记本之前,请先阅读以下笔记本,因为本笔记本将基于它们进行构建:


为了将记忆添加到代理中,我们将执行以下步骤:

  1. 我们将创建一个带有记忆的LLMChain。
  2. 我们将使用该LLMChain来创建一个自定义代理。

为了完成这个练习,我们将创建一个简单的自定义代理,该代理可以访问搜索工具并使用ConversationBufferMemory类。

from langchain.agents import ZeroShotAgent, Tool, AgentExecutor
from langchain.memory import ConversationBufferMemory
from langchain import OpenAI, LLMChain
from langchain.utilities import GoogleSearchAPIWrapper

search = GoogleSearchAPIWrapper()

tools = [
    Tool(
        name="Search",
        func=search.run,
        description="useful for when you need to answer questions about current events",
    )
]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

请注意PromptTemplate中 chat_history 变量的用法,该变量与ConversationBufferMemory 中的动态密钥名称相匹配。

prefix = """Have a conversation with a human, answering the following questions as best you can. You have access to the following tools:"""

suffix = """Begin!"

{chat_history}
Question: {input}
{agent_scratchpad}"""

prompt = ZeroShotAgent.create_prompt(
    tools,
    prefix=prefix,
    suffix=suffix,
    input_variables=["input", "chat_history", "agent_scratchpad"],
)

memory = ConversationBufferMemory(memory_key="chat_history")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

我们现在可以使用 Memory对象 构建 LLMChain,然后创建代理。

llm_chain = LLMChain(llm=OpenAI(temperature=0), prompt=prompt)

agent = ZeroShotAgent(llm_chain=llm_chain, tools=tools, verbose=True)

agent_chain = AgentExecutor.from_agent_and_tools(
    agent=agent, 
    tools=tools, 
    verbose=True, 
    memory=memory
)

agent_chain.run(input="How many people live in canada?")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

为了测试该代理的内存,我们可以提出一个后续问题,该问题依赖于前一次交换中的信息才能得到正确回答。

agent_chain.run(input="what is their national anthem called?")
  • 1

我们可以看到,代理人记得之前的问题是关于加拿大的,并恰当地询问了谷歌搜索加拿大国歌的名字。

为了好玩,让我们将其与一个没有内存的代理进行比较。

prefix = """Have a conversation with a human, answering the following questions as best you can. You have access to the following tools:"""
suffix = """Begin!"

Question: {input}
{agent_scratchpad}"""

prompt = ZeroShotAgent.create_prompt(
    tools, prefix=prefix, suffix=suffix, input_variables=["input", "agent_scratchpad"]
)
llm_chain = LLMChain(llm=OpenAI(temperature=0), prompt=prompt)
agent = ZeroShotAgent(llm_chain=llm_chain, tools=tools, verbose=True)
agent_without_memory = AgentExecutor.from_agent_and_tools(
    agent=agent, tools=tools, verbose=True
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

agent_without_memory.run("How many people live in canada?")
  • 1

agent_without_memory.run("what is their national anthem called?")
  • 1

五、向代理添加由数据库支持的消息记忆

本笔记本将介绍如何为使用外部消息存储的代理添加记忆。在阅读本笔记本之前,请先阅读以下笔记本,因为本笔记本将基于它们进行构建:


为了将带有外部消息存储的记忆添加到代理中,我们将执行以下步骤:

  1. 我们将创建一个RedisChatMessageHistory,用于连接到外部数据库以存储消息。
  2. 我们将使用该聊天记录作为记忆创建一个LLMChain
  3. 我们将使用该LLMChain来创建一个自定义代理。

针对本练习,我们将创建一个简单的自定义代理,该代理可以访问搜索工具并使用ConversationBufferMemory类。

from langchain.agents import ZeroShotAgent, Tool, AgentExecutor
from langchain.memory import ConversationBufferMemory
from langchain.memory.chat_memory import ChatMessageHistory
from langchain.memory.chat_message_histories import RedisChatMessageHistory
from langchain import OpenAI, LLMChain
from langchain.utilities import GoogleSearchAPIWrapper

search = GoogleSearchAPIWrapper()

tools = [
    Tool(
        name="Search",
        func=search.run,
        description="useful for when you need to answer questions about current events",
    )
]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

请注意PromptTemplate中 chat_history 变量的用法,该变量与ConversationBufferMemory 中的动态密钥名称相匹配。

prefix = """Have a conversation with a human, answering the following questions as best you can. You have access to the following tools:"""

suffix = """Begin!"

{chat_history}
Question: {input}
{agent_scratchpad}"""

prompt = ZeroShotAgent.create_prompt(
    tools,
    prefix=prefix,
    suffix=suffix,
    input_variables=["input", "chat_history", "agent_scratchpad"],
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

现在我们可以创建 由数据库支持的 ChatMessageHistory。

message_history = RedisChatMessageHistory(
    url="redis://localhost:6379/0", ttl=600, session_id="my-session"
)

memory = ConversationBufferMemory(
    memory_key="chat_history", chat_memory=message_history
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

我们现在可以 使用Memory对象 构建LLMChain,然后创建代理。

llm_chain = LLMChain(llm=OpenAI(temperature=0), prompt=prompt)

agent = ZeroShotAgent(
                llm_chain=llm_chain, 
                tools=tools, 
                verbose=True
            )

agent_chain = AgentExecutor.from_agent_and_tools(
    agent=agent, tools=tools, verbose=True, memory=memory
)

agent_chain.run(input="How many people live in canada?")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

为了测试该代理的内存,我们可以提出一个后续问题,该问题依赖于前一次交换中的信息才能得到正确回答。

agent_chain.run(input="what is their national anthem called?")
  • 1

我们可以看到,代理人记得之前的问题是关于加拿大的,并恰当地询问了谷歌搜索加拿大国歌的名字。

为了好玩,让我们将其与一个没有内存的代理进行比较。

prefix = """Have a conversation with a human, answering the following questions as best you can. You have access to the following tools:"""
suffix = """Begin!"

Question: {input}
{agent_scratchpad}"""

prompt = ZeroShotAgent.create_prompt(
    tools, prefix=prefix, suffix=suffix, 
    input_variables=["input", "agent_scratchpad"]
)

llm_chain = LLMChain(llm=OpenAI(temperature=0), prompt=prompt)

agent = ZeroShotAgent(llm_chain=llm_chain, tools=tools, verbose=True)

agent_without_memory = AgentExecutor.from_agent_and_tools(
    agent=agent, tools=tools, verbose=True
)

agent_without_memory.run("How many people live in canada?")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

agent_without_memory.run("what is their national anthem called?")
  • 1

六、会话缓存内存 ConversationBufferMemory

本文档演示了如何使用 ConversationBufferMemory
该内存允许存储消息,并将消息提取到一个变量中。

我们可以首先将其提取为字符串。

from langchain.memory import ConversationBufferMemory

memory = ConversationBufferMemory()
memory.save_context({"input": "hi"}, {"output": "whats up"})

memory.load_memory_variables({})
# -> {'history': 'Human: hi\nAI: whats up'}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

我们还可以将历史记录作为消息列表获取(如果您正在将其与聊天模型一起使用,则这非常有用)。

memory = ConversationBufferMemory(return_messages=True)
memory.save_context({"input": "hi"}, {"output": "whats up"})

memory.load_memory_variables({})
  • 1
  • 2
  • 3
  • 4

    {'history': [HumanMessage(content='hi', additional_kwargs={}),
      AIMessage(content='whats up', additional_kwargs={})]}
  • 1
  • 2

在链中使用

最后,让我们来看看如何在链中使用它(设置 verbose=True,以便我们可以看到提示)。

from langchain.llms import OpenAI
from langchain.chains import ConversationChain

llm = OpenAI(temperature=0)

conversation = ConversationChain(
    llm=llm, 
    verbose=True, 
    memory=ConversationBufferMemory()
)

conversation.predict(input="Hi there!")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

conversation.predict(input="I'm doing well! Just having a conversation with an AI.")
  • 1

conversation.predict(input="Tell me about yourself.")
  • 1

这就是入门的全部内容!有很多不同类型的记忆,请查看我们的示例以查看所有类型。


七、会话缓冲窗口记忆 ( Conversation buffer window memory )

ConversationBufferWindowMemory 会随着时间记录会话的交互列表。它只使用最后 K 个交互。
这对于保持最近交互的滑动窗口很有用,以避免缓冲区过大。

让我们首先探索这种类型记忆的基本功能。

from langchain.memory import ConversationBufferWindowMemory

memory = ConversationBufferWindowMemory( k=1)
memory.save_context({"input": "hi"}, {"output": "whats up"})
memory.save_context({"input": "not much you"}, {"output": "not much"})

memory.load_memory_variables({})
# -> {'history': 'Human: not much you\nAI: not much'}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

我们还可以将历史记录作为消息列表获取(如果您正在将其与聊天模型一起使用,则这将非常有用)。

memory = ConversationBufferWindowMemory( k=1, return_messages=True)
memory.save_context({"input": "hi"}, {"output": "whats up"})
memory.save_context({"input": "not much you"}, {"output": "not much"})

memory.load_memory_variables({})
  • 1
  • 2
  • 3
  • 4
  • 5

    {'history': [HumanMessage(content='not much you', additional_kwargs={}),
      AIMessage(content='not much', additional_kwargs={})]}
  • 1
  • 2

在链中使用

让我们通过一个示例来演示,再次设置 verbose=True,以便我们可以看到提示。

from langchain.llms import OpenAI
from langchain.chains import ConversationChain

conversation_with_summary = ConversationChain(
    llm=OpenAI(temperature=0), 
    # We set a low k = 2, to only keep the last 2 interactions in memory
    memory=ConversationBufferWindowMemory(k=2), 
    verbose=True
)

conversation_with_summary.predict(input="Hi, what's up?")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

    > Entering new ConversationChain chain...
    Prompt after formatting:
    The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.
    
    Current conversation:
    
    Human: Hi, what's up?
    AI:
    
    > Finished chain.

    " Hi there! I'm doing great. I'm currently helping a customer with a technical issue. How about you?"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

conversation_with_summary.predict(input="What's their issues?")
  • 1

    > Entering new ConversationChain chain...
    Prompt after formatting:
    The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.
    
    Current conversation:
    Human: Hi, what's up?
    AI:  Hi there! I'm doing great. I'm currently helping a customer with a technical issue. How about you?
    Human: What's their issues?
    AI:
    
    > Finished chain.

    " The customer is having trouble connecting to their Wi-Fi network. I'm helping them troubleshoot the issue and get them connected."
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

conversation_with_summary.predict(input="Is it going well?")
  • 1

    > Entering new ConversationChain chain...
    Prompt after formatting:
    The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.
    
    Current conversation:
    Human: Hi, what's up?
    AI:  Hi there! I'm doing great. I'm currently helping a customer with a technical issue. How about you?
    Human: What's their issues?
    AI:  The customer is having trouble connecting to their Wi-Fi network. I'm helping them troubleshoot the issue and get them connected.
    Human: Is it going well?
    AI:
    
    > Finished chain.

    " Yes, it's going well so far. We've already identified the problem and are now working on a solution."
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

Notice here that the first interaction does not appear.
conversation_with_summary.predict(input="What's the solution?")
  • 1
  • 2

    > Entering new ConversationChain chain...
    Prompt after formatting:
    The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.
    
    Current conversation:
    Human: What's their issues?
    AI:  The customer is having trouble connecting to their Wi-Fi network. I'm helping them troubleshoot the issue and get them connected.
    Human: Is it going well?
    AI:  Yes, it's going well so far. We've already identified the problem and are now working on a solution.
    Human: What's the solution?
    AI:
    
    > Finished chain.

    " The solution is to reset the router and reconfigure the settings. We're currently in the process of doing that."
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

八、如何自定义对话记忆

本笔记本将介绍几种自定义对话记忆的方法。

from langchain.llms import OpenAI
from langchain.chains import ConversationChain
from langchain.memory import ConversationBufferMemory

llm = OpenAI(temperature=0)
  • 1
  • 2
  • 3
  • 4
  • 5

AI 前缀

第一种方法是通过更改对话摘要中的 AI 前缀来实现。
默认情况下,这个前缀被设置为 “AI”,但您可以将其设置为任何您想要的值。
请注意,如果您更改了这个前缀,您应该同时更改链条中使用的提示,以反映这个命名的变化。
让我们通过下面的示例来演示。

# Here it is by default set to "AI"
conversation = ConversationChain(
    llm=llm, verbose=True, memory=ConversationBufferMemory()
)

conversation.predict(input="Hi there!")
# -> " Hi there! It's nice to meet you. How can I help you today?"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

conversation.predict(input="What's the weather?")
  • 1

# Now we can override it and set it to "AI Assistant"
from langchain.prompts.prompt import PromptTemplate

template = """The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.

Current conversation:
{history}
Human: {input}
AI Assistant:"""
PROMPT = PromptTemplate(input_variables=["history", "input"], template=template)
conversation = ConversationChain(
    prompt=PROMPT,
    llm=llm,
    verbose=True,
    memory=ConversationBufferMemory(ai_prefix="AI Assistant"),
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

conversation.predict(input="Hi there!")
  • 1

conversation.predict(input="What's the weather?")
  • 1

Human Prefix

下一种方法是更改会话摘要中的 Human 前缀。
默认情况下,这被设置为Human,但您可以将其设置为任何您想要的内容。
请注意,如果对此进行更改,还应更改链中使用的提示,以反映此命名更改。
让我们在下面的例子中走过一个这样的例子。

# Now we can override it and set it to "Friend"
from langchain.prompts.prompt import PromptTemplate

template = """The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.

Current conversation:
{history}
Friend: {input}
AI:"""

PROMPT = PromptTemplate(input_variables=["history", "input"], template=template)

conversation = ConversationChain(
    prompt=PROMPT,
    llm=llm,
    verbose=True,
    memory=ConversationBufferMemory(human_prefix="Friend"),
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

conversation.predict(input="Hi there!")
  • 1

conversation.predict(input="What's the weather?")
  • 1

九、如何创建自定义的记忆类

尽管在LangChain中提供了几种预定义的记忆类型,但您很可能会想要添加自己的记忆类型,以便优化您的应用。本笔记本将介绍如何实现这一点。

对于本笔记本,我们将向ConversationChain添加一个自定义的记忆类型。为了添加一个自定义的记忆类,我们需要导入基本的记忆类并派生它。

from langchain import OpenAI, ConversationChain
from langchain.schema import BaseMemory
from pydantic import BaseModel
from typing import List, Dict, Any
  • 1
  • 2
  • 3
  • 4

在这个例子中,我们将编写一个自定义的记忆类,它使用spacy来提取实体,并将有关这些实体的信息保存在一个简单的哈希表中。
然后,在对话期间,我们将查看输入文本,提取任何实体,并将实体的任何信息放入上下文中。

  • 请注意,这个实现非常简单且容易出错,可能在实际生产场景中没有用处。它的目的是展示您可以添加自定义的记忆实现。

为此,我们需要安装spacy。

# !pip install spacy
# !python -m spacy download en_core_web_lg
  • 1
  • 2

import spacy

nlp = spacy.load("en_core_web_lg")
  • 1
  • 2
  • 3

class SpacyEntityMemory(BaseMemory, BaseModel):
    """Memory class for storing information about entities."""

    # Define dictionary to store information about entities.
    entities: dict = {}
    # Define key to pass information about entities into prompt.
    memory_key: str = "entities"

    def clear(self):
        self.entities = {}

    @property
    def memory_variables(self) -> List[str]:
        """Define the variables we are providing to the prompt."""
        return [self.memory_key]

    def load_memory_variables(self, inputs: Dict[str, Any]) -> Dict[str, str]:
        """Load the memory variables, in this case the entity key."""
        # Get the input text and run through spacy
        doc = nlp(inputs[list(inputs.keys())[0]])
        # Extract known information about entities, if they exist.
        entities = [
            self.entities[str(ent)] for ent in doc.ents if str(ent) in self.entities
        ]
        # Return combined information about entities to put into context.
        return {self.memory_key: "\n".join(entities)}

    def save_context(self, inputs: Dict[str, Any], outputs: Dict[str, str]) -> None:
        """Save context from this conversation to buffer."""
        # Get the input text and run through spacy
        text = inputs[list(inputs.keys())[0]]
        doc = nlp(text)
        # For each entity that was mentioned, save this information to the dictionary.
        for ent in doc.ents:
            ent_str = str(ent)
            if ent_str in self.entities:
                self.entities[ent_str] += f"\n{text}"
            else:
                self.entities[ent_str] = text
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

我们现在定义了一个提示,该提示接收有关实体的信息以及用户输入

from langchain.prompts.prompt import PromptTemplate

template = """The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know. You are provided with information about entities the Human mentions, if relevant.

Relevant entity information:
{entities}

Conversation:
Human: {input}
AI:"""

prompt = PromptTemplate(input_variables=["entities", "input"], template=template)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

组合在一起

llm = OpenAI(temperature=0)
conversation = ConversationChain(
    llm=llm, prompt=prompt, verbose=True, memory=SpacyEntityMemory()
)
  • 1
  • 2
  • 3
  • 4

In the first example, with no prior knowledge about Harrison, the “Relevant entity information” section is empty.

conversation.predict(input="Harrison likes machine learning")
  • 1

Now in the second example, we can see that it pulls in information about Harrison.

conversation.predict(
    input="What do you think Harrison's favorite subject in college was?"
)
  • 1
  • 2
  • 3

Again, please note that this implementation is pretty simple and brittle and probably not useful in a production setting. Its purpose is to showcase that you can add custom memory implementations.


  • 1

十、实体记忆 entity_summary_memory

实体记忆会记住对话中特定实体的给定事实。它使用 LLM 从实体中提取信息,并随时间建立对该实体的知识(也使用 LLM)。

让我们首先通过使用这个功能来演示一下。

from langchain.llms import OpenAI
from langchain.memory import ConversationEntityMemory
llm = OpenAI(temperature=0)

memory = ConversationEntityMemory(llm=llm)

_input = {"input": "Deven & Sam are working on a hackathon project"}

memory.load_memory_variables(_input)

memory.save_context(
    _input,
    {"output": " That sounds like a great project! What kind of project are they working on?"}
)

memory.load_memory_variables({"input": 'who is Sam'})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

    {'history': 'Human: Deven & Sam are working on a hackathon project\nAI:  That sounds like a great project! What kind of project are they working on?',
     'entities': {'Sam': 'Sam is working on a hackathon project with Deven.'}}
  • 1
  • 2

memory = ConversationEntityMemory(llm=llm, return_messages=True)

_input = {"input": "Deven & Sam are working on a hackathon project"}

memory.load_memory_variables(_input)

memory.save_context(
    _input,
    {"output": " That sounds like a great project! What kind of project are they working on?"}
)

memory.load_memory_variables({"input": 'who is Sam'})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

    {'history': [HumanMessage(content='Deven & Sam are working on a hackathon project', additional_kwargs={}),
      AIMessage(content=' That sounds like a great project! What kind of project are they working on?', additional_kwargs={})],
     'entities': {'Sam': 'Sam is working on a hackathon project with Deven.'}}
  • 1
  • 2
  • 3

在链中使用

现在让我们在链中使用它!

from langchain.chains import ConversationChain
from langchain.memory import ConversationEntityMemory
from langchain.memory.prompt import ENTITY_MEMORY_CONVERSATION_TEMPLATE
from pydantic import BaseModel
from typing import List, Dict, Any

conversation = ConversationChain(
    llm=llm, 
    verbose=True,
    prompt=ENTITY_MEMORY_CONVERSATION_TEMPLATE,
    memory=ConversationEntityMemory(llm=llm)
)

conversation.predict(input="Deven & Sam are working on a hackathon project")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

    > Entering new ConversationChain chain...
    Prompt after formatting:
    You are an ... assist.
    
    Context:
    {'Deven': 'Deven is working on a hackathon project with Sam.', 'Sam': 'Sam is working on a hackathon project with Deven.'}
    
    Current conversation:
    
    Last line:
    Human: Deven & Sam are working on a hackathon project
    You:
    
    > Finished chain.

    ' That sounds like a great project! What kind of project are they working on?'
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

conversation.memory.entity_store.store
  • 1

    {'Deven': 'Deven is working on a hackathon project with Sam, which they are entering into a hackathon.',
     'Sam': 'Sam is working on a hackathon project with Deven.'}
  • 1
  • 2

conversation.predict(input="They are trying to add more complex memory structures to Langchain")
  • 1

    > Entering new ConversationChain chain...
    Prompt after formatting:
    You are ... here to assist.
    
    Context:
    {'Deven': 'Deven is working on a hackathon project with Sam, which they are entering into a hackathon.', 'Sam': 'Sam is working on a hackathon project with Deven.', 'Langchain': ''}
    
    Current conversation:
    Human: Deven & Sam are working on a hackathon project
    AI:  That sounds like a great project! What kind of project are they working on?
    Last line:
    Human: They are trying to add more complex memory structures to Langchain
    You:
    
    > Finished chain.

    ' That sounds like an interesting project! What kind of memory structures are they trying to add?'
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

conversation.predict(input="They are adding in a key-value store for entities mentioned so far in the conversation.")
  • 1

conversation.predict(input="What do you know about Deven & Sam?")
  • 1

检查内存存储 (Inspecting the memory store)

我们也可以直接检查内存存储。在下面的例子中,我们直接查看它,然后通过一些添加信息的示例来观察它的变化。

from pprint import pprint
pprint(conversation.memory.entity_store.store)
  • 1
  • 2

    {'Daimon': 'Daimon is a company founded by Sam, a successful entrepreneur.',
     'Deven': 'Deven is working on a hackathon project with Sam, which they are '
              'entering into a hackathon. They are trying to add more complex '
              'memory structures to Langchain, including a key-value store for '
              'entities mentioned so far in the conversation, and seem to be '
              'working hard on this project with a great idea for how the '
              'key-value store can help.',
     'Key-Value Store': 'A key-value store is being added to the project to store '
                        'entities mentioned in the conversation.',
     'Langchain': 'Langchain is a project that is trying to add more complex '
                  'memory structures, including a key-value store for entities '
                  'mentioned so far in the conversation.',
     'Sam': 'Sam is working on a hackathon project with Deven, trying to add more '
            'complex memory structures to Langchain, including a key-value store '
            'for entities mentioned so far in the conversation. They seem to have '
            'a great idea for how the key-value store can help, and Sam is also '
            'the founder of a company called Daimon.'}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

conversation.predict(input="Sam is the founder of a company called Daimon.")
  • 1

from pprint import pprint
pprint(conversation.memory.entity_store.store)
  • 1
  • 2

    {'Daimon': 'Daimon is a company founded by Sam, a successful entrepreneur, who '
               'is working on a hackathon project with Deven to add more complex '
               'memory structures to Langchain.',
     'Deven': 'Deven is working on a hackathon project with Sam, which they are '
              'entering into a hackathon. They are trying to add more complex '
              'memory structures to Langchain, including a key-value store for '
              'entities mentioned so far in the conversation, and seem to be '
              'working hard on this project with a great idea for how the '
              'key-value store can help.',
     'Key-Value Store': 'A key-value store is being added to the project to store '
                        'entities mentioned in the conversation.',
     'Langchain': 'Langchain is a project that is trying to add more complex '
                  'memory structures, including a key-value store for entities '
                  'mentioned so far in the conversation.',
     'Sam': 'Sam is working on a hackathon project with Deven, trying to add more '
            'complex memory structures to Langchain, including a key-value store '
            'for entities mentioned so far in the conversation. They seem to have '
            'a great idea for how the key-value store can help, and Sam is also '
            'the founder of a successful company called Daimon.'}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

conversation.predict(input="What do you know about Sam?")
  • 1

十一、Conversation Knowledge Graph Memory

这种类型的记忆使用知识图谱来重建记忆。

首先,让我们详细介绍如何使用这些工具。

from langchain.memory import ConversationKGMemory
from langchain.llms import OpenAI
  • 1
  • 2

llm = OpenAI(temperature=0)

memory = ConversationKGMemory(llm=llm)

memory.save_context({"input": "say hi to sam"}, {"output": "who is sam"})

memory.save_context({"input": "sam is a friend"}, {"output": "okay"})

memory.load_memory_variables({"input": "who is sam"})
# -> {'history': 'On Sam: Sam is friend.'}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

We can also get the history as a list of messages (this is useful if you are using this with a chat model).

memory = ConversationKGMemory(llm=llm, return_messages=True)
memory.save_context({"input": "say hi to sam"}, {"output": "who is sam"})
memory.save_context({"input": "sam is a friend"}, {"output": "okay"})

memory.load_memory_variables({"input": "who is sam"})
# -> {'history': [SystemMessage(content='On Sam: Sam is friend.', additional_kwargs={})]}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

We can also more modularly get current entities from a new message (will use previous messages as context.)

memory.get_current_entities("what's Sams favorite color?")
# -> ['Sam']
  • 1
  • 2

We can also more modularly get knowledge triplets from a new message (will use previous messages as context.)

memory.get_knowledge_triplets("her favorite color is red")
  • 1

[KnowledgeTriple(subject='Sam', predicate='favorite color', object_='red')]
  • 1

Using in a chain

Let’s now use this in a chain!

llm = OpenAI(temperature=0)
from langchain.prompts.prompt import PromptTemplate
from langchain.chains import ConversationChain

template = """The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. 
If the AI does not know the answer to a question, it truthfully says it does not know. The AI ONLY uses information contained in the "Relevant Information" section and does not hallucinate.

Relevant Information:

{history}

Conversation:
Human: {input}
AI:"""
prompt = PromptTemplate(input_variables=["history", "input"], template=template)
conversation_with_kg = ConversationChain(
    llm=llm, verbose=True, prompt=prompt, memory=ConversationKGMemory(llm=llm)
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

conversation_with_kg.predict(input="Hi, what's up?")
  • 1

conversation_with_kg.predict(
    input="My name is James and I'm helping Will. He's an engineer."
)
  • 1
  • 2
  • 3

conversation_with_kg.predict(input="What do you know about Will?")
  • 1

十二、如何在同一链条中使用多个记忆类

可以在同一链条中使用多个记忆类。为了组合多个记忆类,我们可以初始化CombinedMemory类,然后使用它。

from langchain.llms import OpenAI
from langchain.prompts import PromptTemplate
from langchain.chains import ConversationChain

from langchain.memory import (
    ConversationBufferMemory,
    CombinedMemory,
    ConversationSummaryMemory,
)

conv_memory = ConversationBufferMemory(
    memory_key="chat_history_lines", input_key="input"
)

summary_memory = ConversationSummaryMemory(llm=OpenAI(), input_key="input")

# Combined
memory = CombinedMemory(memories=[conv_memory, summary_memory])

_DEFAULT_TEMPLATE = """The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.

Summary of conversation:
{history}
Current conversation:
{chat_history_lines}
Human: {input}
AI:"""

PROMPT = PromptTemplate(
    input_variables=["history", "input", "chat_history_lines"],
    template=_DEFAULT_TEMPLATE,
)

llm = OpenAI(temperature=0)

conversation = ConversationChain(llm=llm, verbose=True, memory=memory, prompt=PROMPT)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

conversation.run("Hi!")
  • 1

conversation.run("Can you tell me a joke?")
  • 1

十三、会话摘要记忆 summary

现在让我们来看一下使用稍微复杂的记忆类型 - ConversationSummaryMemory
这种记忆类型会随着时间的推移对对话进行摘要。
这对于从对话中压缩信息非常有用。

会话摘要记忆会即时总结对话并将当前摘要存储在记忆中。
然后可以将这个记忆用于将到目前为止的对话摘要注入到提示/链中。
这种记忆对于较长的对话非常有用,因为将过去的消息历史原样保留在提示中会占用太多的标记。

让我们首先探索这种记忆类型的基本功能。

from langchain.memory import ConversationSummaryMemory, ChatMessageHistory
from langchain.llms import OpenAI

memory = ConversationSummaryMemory(llm=OpenAI(temperature=0))
memory.save_context({"input": "hi"}, {"output": "whats up"})

memory.load_memory_variables({})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

    {'history': '\nThe human greets the AI, to which the AI responds.'}
  • 1

我们还可以将历史记录作为消息列表获取(如果您将其与聊天模型一起使用,这将非常有用)。

memory = ConversationSummaryMemory(llm=OpenAI(temperature=0), return_messages=True)
memory.save_context({"input": "hi"}, {"output": "whats up"})

memory.load_memory_variables({})
  • 1
  • 2
  • 3
  • 4

    {'history': [SystemMessage(content='\nThe human greets the AI, to which the AI responds.', additional_kwargs={})]}
  • 1

我们还可以直接利用 predict_new_summary 方法。

messages = memory.chat_memory.messages
previous_summary = ""
memory.predict_new_summary(messages, previous_summary)
# -> '\nThe human greets the AI, to which the AI responds.'
  • 1
  • 2
  • 3
  • 4

使用消息初始化

如果您有类外的消息,可以轻松使用 ChatMessageHistory 初始化类。在加载过程中,将计算摘要。

history = ChatMessageHistory()
history.add_user_message("hi")
history.add_ai_message("hi there!")

memory = ConversationSummaryMemory.from_messages(llm=OpenAI(temperature=0), chat_memory=history, return_messages=True)
  • 1
  • 2
  • 3
  • 4
  • 5

memory.buffer
  • 1

    '\nThe human greets the AI, to which the AI responds with a friendly greeting.'
  • 1

在链中使用

让我们通过一个在链中使用的示例来演示,再次设置 verbose=True 以便我们可以看到提示信息。

from langchain.llms import OpenAI
from langchain.chains import ConversationChain
llm = OpenAI(temperature=0)
conversation_with_summary = ConversationChain(
    llm=llm, 
    memory=ConversationSummaryMemory(llm=OpenAI()),
    verbose=True
)
conversation_with_summary.predict(input="Hi, what's up?")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

conversation_with_summary.predict(input="Tell me more about it!")
  • 1

conversation_with_summary.predict(input="Very cool -- what is the scope of the project?")
  • 1

十四、ConversationSummaryBufferMemory

ConversationSummaryBufferMemory结合了前两个想法。
它在内存中保留了最近的交互缓冲区,但不仅仅是完全清除旧的交互,而是将它们编译为摘要并同时使用它们。
然而,与以前的实现不同,它使用token长度而不是交互次数来确定何时清除交互。

让我们首先了解如何使用这些工具。

from langchain.memory import ConversationSummaryBufferMemory
from langchain.llms import OpenAI

llm = OpenAI()

memory = ConversationSummaryBufferMemory(llm=llm, max_token_limit=10)
memory.save_context({"input": "hi"}, {"output": "whats up"})
memory.save_context({"input": "not much you"}, {"output": "not much"})

memory.load_memory_variables({})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

{'history': 'System: \nThe human says "hi", and the AI responds with "whats up".\nHuman: not much you\nAI: not much'}
  • 1

We can also get the history as a list of messages (this is useful if you are using this with a chat model).

memory = ConversationSummaryBufferMemory(
    llm=llm, max_token_limit=10, return_messages=True
)

memory.save_context({"input": "hi"}, {"output": "whats up"})
memory.save_context({"input": "not much you"}, {"output": "not much"})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

We can also utilize the predict_new_summary method directly.

messages = memory.chat_memory.messages
previous_summary = ""
memory.predict_new_summary(messages, previous_summary)
# ->  '\nThe human and AI state that they are not doing much.'
  • 1
  • 2
  • 3
  • 4

Using in a chain

Let’s walk through an example, again setting verbose=True so we can see the prompt.

from langchain.chains import ConversationChain

conversation_with_summary = ConversationChain(
    llm=llm,
    # We set a very low max_token_limit for the purposes of testing.
    memory=ConversationSummaryBufferMemory(llm=OpenAI(), max_token_limit=40),
    verbose=True,
)
conversation_with_summary.predict(input="Hi, what's up?")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

conversation_with_summary.predict(input="Just working on writing some documentation!")
  • 1

# We can see here that there is a summary of the conversation and then some previous interactions
conversation_with_summary.predict(input="For LangChain! Have you heard of it?")
  • 1
  • 2

# We can see here that the summary and the buffer are updated
conversation_with_summary.predict(
    input="Haha nope, although a lot of people confuse it for that"
)
  • 1
  • 2
  • 3
  • 4

十五、ConversationTokenBufferMemory

ConversationTokenBufferMemory在内存中保留了最近的交互缓冲区,使用的是token长度而不是交互次数来确定何时清除交互。

让我们首先了解如何使用这些工具。

from langchain.memory import ConversationTokenBufferMemory
from langchain.llms import OpenAI

llm = OpenAI()

memory = ConversationTokenBufferMemory(llm=llm, max_token_limit=10)
memory.save_context({"input": "hi"}, {"output": "whats up"})
memory.save_context({"input": "not much you"}, {"output": "not much"})

memory.load_memory_variables({})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

{'history': 'Human: not much you\nAI: not much'}
  • 1

We can also get the history as a list of messages (this is useful if you are using this with a chat model).

memory = ConversationTokenBufferMemory(
    llm=llm, max_token_limit=10, return_messages=True
)
memory.save_context({"input": "hi"}, {"output": "whats up"})
memory.save_context({"input": "not much you"}, {"output": "not much"})
  • 1
  • 2
  • 3
  • 4
  • 5

Using in a chain

Let’s walk through an example, again setting verbose=True so we can see the prompt.

from langchain.chains import ConversationChain

conversation_with_summary = ConversationChain(
    llm=llm,
    # We set a very low max_token_limit for the purposes of testing.
    memory=ConversationTokenBufferMemory(llm=OpenAI(), max_token_limit=60),
    verbose=True,
)
conversation_with_summary.predict(input="Hi, what's up?")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

conversation_with_summary.predict(input="Just working on writing some documentation!")
  • 1

conversation_with_summary.predict(input="For LangChain! Have you heard of it?")
  • 1

# We can see here that the buffer is updated
conversation_with_summary.predict(
    input="Haha nope, although a lot of people confuse it for that"
)
  • 1
  • 2
  • 3
  • 4

十六、基于向量存储的记忆 vectorstore_retriever_memory

VectorStoreRetrieverMemory 在 VectorDB 中存储记忆,并在每次调用时查询最具 “显著性” 的前 K 个文档。

与大多数其他 Memory 类不同的是,它不明确跟踪交互的顺序。

在这种情况下,“docs” 是先前对话片段。这对于引用对话中 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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

初始化你的 VectorStore

根据你选择的存储方式,这一步可能会有所不同。请参考相关的 VectorStore 文档以获取更多详细信息。

import faiss

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

embedding_size = 1536 # Dimensions of the OpenAIEmbeddings
index = faiss.IndexFlatL2(embedding_size)
embedding_fn = OpenAIEmbeddings().embed_query
vectorstore = FAISS(embedding_fn, index, InMemoryDocstore({}), {})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

创建你的 VectorStoreRetrieverMemory

内存对象是从任意 VectorStoreRetriever 实例化的。

In actual usage, you would set `k` to be a higher value, but we use k = 1 to show that
# the vector lookup still returns the semantically relevant information
retriever = vectorstore.as_retriever(search_kwargs=dict(k=1))
memory = VectorStoreRetrieverMemory(retriever=retriever)

# When added to an agent, the memory object can save pertinent information from conversations or used tools
memory.save_context({"input": "My favorite food is pizza"}, {"output": "thats good to know"})
memory.save_context({"input": "My favorite sport is soccer"}, {"output": "..."})
memory.save_context({"input": "I don't the Celtics"}, {"output": "ok"}) #
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

Notice the first result returned is the memory pertaining to tax help, which the language model deems more semantically relevant
# to a 1099 than the other documents, despite them both containing numbers.
print(memory.load_memory_variables({"prompt": "what sport should i watch?"})["history"])
  • 1
  • 2
  • 3

    input: My favorite sport is soccer
    output: ...
  • 1
  • 2

在链中使用

让我们通过一个例子来演示,再次设置 verbose=True 以便我们可以看到提示信息。

llm = OpenAI(temperature=0) # Can be any valid LLM
_DEFAULT_TEMPLATE = """The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.

Relevant pieces of previous conversation:
{history}

(You do not need to use these pieces of information if not relevant)

Current conversation:
Human: {input}
AI:"""
PROMPT = PromptTemplate(
    input_variables=["history", "input"], template=_DEFAULT_TEMPLATE
)
conversation_with_summary = ConversationChain(
    llm=llm, 
    prompt=PROMPT,
    # We set a very low max_token_limit for the purposes of testing.
    memory=memory,
    verbose=True
)
conversation_with_summary.predict(input="Hi, my name is Perry, what's up?")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

Here, the basketball related content is surfaced
conversation_with_summary.predict(input="what's my favorite sport?")
  • 1
  • 2

Even though the language model is stateless, since relavent memory is fetched, it can "reason" about the time.
# Timestamping memories and data is useful in general to let the agent determine temporal relevance
conversation_with_summary.predict(input="Whats my favorite food")
  • 1
  • 2
  • 3

The memories from the conversation are automatically stored,
# since this query best matches the introduction chat above,
# the agent is able to 'remember' the user's name.
conversation_with_summary.predict(input="What's my name?")
  • 1
  • 2
  • 3
  • 4

十七、聊天消息

信息请参阅Integrations,了解内置内存集成与第三方数据库和工具的文档。

一个支持大多数(如果不是全部)内存模块的核心实用类是ChatMessageHistory类。 这是一个超轻量级的包装器,提供了保存HumanMessages、AIMessages以及获取它们的便捷方法。

如果您在链外管理内存,您可能希望直接使用这个类。

from langchain.memory import ChatMessageHistory

history = ChatMessageHistory()

history.add_user_message("hi!")

history.add_ai_message("whats up?")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

history.messages
  • 1

    [HumanMessage(content='hi!', additional_kwargs={}),
     AIMessage(content='whats up?', additional_kwargs={})]
  • 1
  • 2

内存类型

有许多不同类型的内存。 每种类型都有自己的参数、返回类型,在不同的场景中有不同的用途。 请参阅它们各自的页面以获取更多详细信息。

十八、会话缓冲区

这个笔记本展示了如何使用ConversationBufferMemory。这个内存可以用来存储消息,并从中提取消息。

我们可以首先将其提取为字符串。

from langchain.memory import ConversationBufferMemory
 
memory = ConversationBufferMemory()
memory.save_context({"input": "hi"}, {"output": "whats up"})
 
memory.load_memory_variables({})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

    {'history': 'Human: hi\nAI: whats up'}
  • 1

我们还可以将历史记录作为消息列表获取(当与聊天模型一起使用时,这会很有用)。

memory = ConversationBufferMemory(return_messages=True)
memory.save_context({"input": "hi"}, {"output": "whats up"})
 
memory.load_memory_variables({})
  • 1
  • 2
  • 3
  • 4

    {'history': [HumanMessage(content='hi', additional_kwargs={}),
      AIMessage(content='whats up', additional_kwargs={})]}
  • 1
  • 2

在链式结构中使用

最后,让我们看一下在链式结构中如何使用(设置verbose=True以便查看提示信息)。

from langchain.llms import OpenAI
from langchain.chains import ConversationChain

llm = OpenAI(temperature=0)
conversation = ConversationChain(
    llm=llm, 
    verbose=True, 
    memory=ConversationBufferMemory()
)
 
conversation.predict(input="Hi there!")
 
conversation.predict(input="I'm doing well! Just having a conversation with an AI.")
 
conversation.predict(input="Tell me about yourself.")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

十九、会话缓冲窗口

ConversationBufferWindowMemory 会在一段时间内保持对话的交互列表。它只使用最后的 K 个交互。这对于保持最近交互的滑动窗口很有用,这样缓冲区就不会变得太大。

让我们首先探索这种类型的内存的基本功能。

from langchain.memory import ConversationBufferWindowMemory
 
memory = ConversationBufferWindowMemory(k=1)
memory.save_context({"input": "hi"}, {"output": "whats up"})
memory.save_context({"input": "not much you"}, {"output": "not much"})
 
memory.load_memory_variables({})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

    {'history': 'Human: not much you\nAI: not much'}
  • 1

我们还可以将历史记录以消息列表的形式获取(如果您将其与聊天模型一起使用,这会很有用)。

memory = ConversationBufferWindowMemory(k=1, return_messages=True)
memory.save_context({"input": "hi"}, {"output": "whats up"})
memory.save_context({"input": "not much you"}, {"output": "not much"})
  • 1
  • 2
  • 3

memory.load_memory_variables({})
  • 1

    {'history': [HumanMessage(content='not much you', additional_kwargs={}),
      AIMessage(content='not much', additional_kwargs={})]}
  • 1
  • 2

在链式结构中使用

让我们通过一个示例进行演示,再次设置verbose=True以便查看提示信息。

from langchain.llms import OpenAI
from langchain.chains import ConversationChain
conversation_with_summary = ConversationChain(
    llm=OpenAI(temperature=0), 
    # We set a low k=2, to only keep the last 2 interactions in memory
    memory=ConversationBufferWindowMemory(k=2), 
    verbose=True
)
conversation_with_summary.predict(input="Hi, what's up?")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

conversation_with_summary.predict(input="What's their issues?")
  • 1

conversation_with_summary.predict(input="Is it going well?")
  • 1

# Notice here that the first interaction does not appear.
conversation_with_summary.predict(input="What's the solution?")
  • 1
  • 2

二十、会话摘要

现在让我们来看一下使用稍微复杂的记忆类型 - ConversationSummaryMemory。这种类型的记忆会随着时间的推移创建一份对话摘要。这对于从对话中压缩信息非常有用。 会话摘要记忆将对话进行摘要并将当前摘要存储在记忆中。然后可以将此记忆用于将迄今为止的对话摘要注入到提示/链中。此记忆对于较长的对话非常有用,如果在提示中完全保留过去的消息历史将占用太多的标记。

让我们首先探索一下这种类型记忆的基本功能。

from langchain.memory import ConversationSummaryMemory, ChatMessageHistory
from langchain.llms import OpenAI
 
memory = ConversationSummaryMemory(llm=OpenAI(temperature=0))
memory.save_context({"input": "hi"}, {"output": "whats up"})
 
memory.load_memory_variables({})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

    {'history': '\nThe human greets the AI, to which the AI responds.'}
  • 1

We can also get the history as a list of messages (this is useful if you are using this with a chat model).

memory = ConversationSummaryMemory(llm=OpenAI(temperature=0), return_messages=True)
memory.save_context({"input": "hi"}, {"output": "whats up"})
 
memory.load_memory_variables({})
  • 1
  • 2
  • 3
  • 4

    {'history': [SystemMessage(content='\nThe human greets the AI, to which the AI responds.', additional_kwargs={})]}
  • 1

We can also utilize the predict_new_summary method directly.

messages = memory.chat_memory.messages
previous_summary = ""
memory.predict_new_summary(messages, previous_summary)
  • 1
  • 2
  • 3

    '\nThe human greets the AI, to which the AI responds.'
  • 1

Initializing with messages/existing summary

If you have messages outside this class, you can easily initialize the class with ChatMessageHistory. During loading, a summary will be calculated.

history = ChatMessageHistory()
history.add_user_message("hi")
history.add_ai_message("hi there!")
 
memory = ConversationSummaryMemory.from_messages(
    llm=OpenAI(temperature=0),
    chat_memory=history,
    return_messages=True
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

memory.buffer
  • 1

    '\nThe human greets the AI, to which the AI responds with a friendly greeting.'
  • 1

Optionally you can speed up initialization using a previously generated summary, and avoid regenerating the summary by just initializing directly.

memory = ConversationSummaryMemory(
    llm=OpenAI(temperature=0),
    buffer="The human asks what the AI thinks of artificial intelligence. The AI thinks artificial intelligence is a force for good because it will help humans reach their full potential.",
    chat_memory=history,
    return_messages=True
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Using in a chain

Let’s walk through an example of using this in a chain, again setting verbose=True so we can see the prompt.

from langchain.llms import OpenAI
from langchain.chains import ConversationChain
llm = OpenAI(temperature=0)
conversation_with_summary = ConversationChain(
    llm=llm, 
    memory=ConversationSummaryMemory(llm=OpenAI()),
    verbose=True
)
conversation_with_summary.predict(input="Hi, what's up?")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

conversation_with_summary.predict(input="Tell me more about it!")
  • 1

conversation_with_summary.predict(input="Very cool -- what is the scope of the project?")
  • 1

二十一、支持向量存储

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

初始化向量存储

根据您选择的存储方式,这一步可能会有所不同。请参考相关的向量存储文档以获取更多详细信息。

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({}), {})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

创建VectorStoreRetrieverMemory

从任何向量存储检索器实例化内存对象。

# 在实际使用中,您可以将`k`设置得更高,但我们使用k=1来展示向量查找仍然返回语义相关的信息
retriever = vectorstore.as_retriever(search_kwargs=dict(k=1))
memory = VectorStoreRetrieverMemory(retriever=retriever)

# 当添加到代理程序时,内存对象可以保存来自对话或使用工具的相关信息
memory.save_context({"input": "我最喜欢的食物是比萨饼"}, {"output": "知道了"})
memory.save_context({"input": "我最喜欢的运动是足球"}, {"output": "..."})
memory.save_context({"input": "我不喜欢凯尔特人队"}, {"output": "好的"})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

# 注意,返回的第一个结果是与1099相关的记忆,与其他文件相比,语言模型认为它与1099更有语义相关性,尽管它们都包含数字。
print(memory.load_memory_variables({"prompt": "我应该看什么运动?"})["history"])
  • 1
  • 2

    input: 我最喜欢的运动是足球
    output: ...
  • 1
  • 2

在链式结构中使用

让我们通过一个示例进行演示,再次设置verbose=True以便查看提示信息。

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,有什么新鲜事?")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

# Here, the basketball related content is surfaced
conversation_with_summary.predict(input="what's my favorite sport?")
  • 1
  • 2

# Even though the language model is stateless, since relevant memory is fetched, it can "reason" about the time.
# Timestamping memories and data is useful in general to let the agent determine temporal relevance
conversation_with_summary.predict(input="Whats my favorite food")
  • 1
  • 2
  • 3

# The memories from the conversation are automatically stored,
# since this query best matches the introduction chat above,
# the agent is able to 'remember' the user's name.
conversation_with_summary.predict(input="What's my name?")
  • 1
  • 2
  • 3
  • 4

二十二、集成


2024-04-09(二)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/680682
推荐阅读
相关标签
  

闽ICP备14008679号