LangChain大模型应用开发指南-大模型Memory不止于对话
  GnFX9Na5BZpa 2023年11月24日 20 0

上节课,我我为您介绍了LangChain中最基本的链式结构,以及基于这个链式结构演化出来的ReAct对话链模型。没有看过的小伙伴可以点击链接查看: AI课程合集

今天我将由简入繁,为大家拆解LangChain内置的多种记忆机制。本教程将详细介绍这些记忆组件的工作原理、特性以及使用方法。

历史对话全带上,记忆居然如此低级

随着大模型的发展,语言模型已经能够进行逻辑自洽的对话。但是与人类智能相比,机器对话仍然存在短板,其中一个重要因素就是“记忆力”的缺失。何为记忆力?简单来说,就是机器需要能够记住之前的上下文和知识,并运用这些“记忆”,使对话更加流畅合理。

ConversationBufferMemory是LangChain中最基础的记忆组件。它的工作原理非常简单:将对话历史缓存到一个队列中,并提供接口获取历史对话。

这种缓存机制实现了最基本的对话“记忆”功能。当用户询问之前提到的问题时,ConversationBufferMemory可以查找相关记忆,从而使机器人的回答更加连贯合理。 组件结构示意图

# 导入所需的库
from langchain.llms import OpenAI
from langchain.chains import ConversationChain
from langchain.chains.conversation.memory import ConversationBufferMemory

# 初始化大语言模型
# 大模型定义
api_key = ""

api_url = ""

modal= "baichuan"

llm = OpenAI(model_name=modal,openai_api_key=api_key,openai_api_base=api_url,temperature=0.0)

# 初始化对话链
conversation = ConversationChain(
    llm=llm,
    memory=ConversationBufferMemory()
)

# 第一天的对话
# 回合1
conversation("我姐姐明天要过生日,我需要一束生日花束。")
print("第一次对话后的记忆:", conversation.memory.buffer)

# 回合2
conversation("她喜欢粉色玫瑰,颜色是粉色的。")
print("第二次对话后的记忆:", conversation.memory.buffer)

# 回合3 (第二天的对话)
conversation("我又来了,还记得我昨天为什么要来买花吗?")
print("/n第三次对话后时提示:/n",conversation.prompt.template)
print("/n第三次对话后的记忆:/n", conversation.memory.buffer)


如以上代码和执行结果所示,ConversationBufferMemory会将所有的对话历史存储在buffer中,开发者可以通过'conversation.memory.buffer',访问最近的对话历史。然后基于这些历史信息进行后续处理,从而实现机器人的“记忆”功能,执行结果截图如下:

这种Remember Everything的全历史记忆策略非常简单直接,但是同时也存在一些问题:

  1. 记忆容量有限,长对话下容易撑爆内存
  2. 对话噪声也全部记住,降低有效信息密度

所以这只是一个低级的记忆实现,我们还需要更智能的记忆机制,为了解决容量有限及,token耗费过高的问题,Langchain提供了时间窗口记忆组件。

容量有限?试试窗口记忆

既然全历史记忆有容量限制问题,那么可以试试只记住部分重要的对话片段。

ConversationBufferWindowMemory实现了基于时间窗口的记忆策略。

它与全历史缓存的差别在于,只维护一个滑动时间窗口,例如最近5轮对话。超出这个窗口的历史对话将被移出缓存。

# 导入所需的库
from langchain.llms import OpenAI
from langchain.chains import ConversationChain
from langchain.chains.conversation.memory import ConversationBufferWindowMemory

# 初始化大语言模型
# 大模型定义
api_key = ""

api_url = ""

modal= "baichuan"

llm = OpenAI(model_name=modal,openai_api_key=api_key,openai_api_base=api_url,temperature=0.0)

# 初始化对话链
conversation = ConversationChain(
    llm=llm,
    memory=ConversationBufferWindowMemory(k=1)
)

# 第一天的对话
# 回合1
result = conversation("我姐姐明天要过生日,我需要一束生日花束。")
print(result)
# 回合2
result = conversation("她喜欢粉色玫瑰,颜色是粉色的。")
print("\n第二次对话后的记忆:\n", conversation.memory.buffer)
print(result)

# 第二天的对话
# 回合3
result = conversation("我又来了,还记得我昨天为什么要来买花吗?")
print(result)

如以上代码所示,buffer中只保留了最近一次对话的记忆。这种窗口机制实现了“遗忘”功能,有效控制了记忆容量,防止内存泄漏,执行结果截图如下:

与此同时,通过窗口大小调整,开发者可以平衡记忆容量和内容质量:

  • 窗口越大,记住的内容越多
  • 窗口越小,记忆更加重点和精炼

但是,本节的截断式遗忘依然是一个相对简单的解决方案。后续我们将探索有选择性地生成“记忆”,这才更符合人类智能的特点。

既要还要,这才算是AI记忆

之前的两种记忆机制要么占用过多容量,要么丢失太多重要信息。我们需要一种折中方案——保留关键信息,移除冗余Noise。

ConversationSummary系列组件通过生成语义摘要的方式实现这一目标。

# 导入所需的库
from langchain.llms import OpenAI
from langchain.chains import ConversationChain
from langchain.chains.conversation.memory import ConversationSummaryMemory

# 初始化大语言模型
# 大模型定义
api_key = ""

api_url = ""

modal= "baichuan"

llm = OpenAI(model_name=modal,openai_api_key=api_key,openai_api_base=api_url,temperature=0.0)

# 初始化对话链
conversation = ConversationChain(
    llm=llm,
    memory=ConversationSummaryMemory(llm=llm)
)

# 第一天的对话
# 回合1
result = conversation("我姐姐明天要过生日,我需要一束生日花束。")
print(result)
# 回合2
result = conversation("她喜欢粉色玫瑰,颜色是粉色的。")
print("\n第二次对话后的记忆:\n", conversation.memory.buffer)
print(result)

# 第二天的对话
# 回合3
result = conversation("我又来了,还记得我昨天为什么要来买花吗?")
print(result)

如以上代码所示,buffer中只保留的记忆并非历史的对话,而是历史对话的摘要,执行结果截图如下:

其工作流程是:

  1. 对对话历史进行语义分析,提取关键词、实体等语义信息
  2. 基于这些语义信息,生成文本摘要
  3. 将生成的摘要作为记忆,而不是完整的对话历史

这种策略融合了记忆质量和容量的考量。摘要只保留了最核心的语义信息,有效减少了冗余,同时质量也更高。

ConversationSummaryMemory会在每轮对话后更新一次摘要。而ConversationSummaryBufferMemory支持配置生成摘要的间隔,从而进一步降低计算消耗。

这里是推荐文章的第四部分内容:

图谱化,记忆向知识进化

前面讨论的几种记忆机制,都是在自然语言的水平上进行对话历史的记录和重用。那么如果能直接建模并记忆更抽象的知识和关系,将是更高一层的进化。

ConversationKGMemory实现了这一目标。它将对话中的实体和事件抽取出来,构建知识图谱;并在回答问题时,探索知识图谱寻找相关记忆。

from langchain.chains.conversation.memory import ConversationKGMemory
from langchain.llms import OpenAI
from langchain.chains import ConversationChain
from langchain.prompts.prompt import PromptTemplate


api_key = ""

api_url = ""

modal= "baichuan"

llm = OpenAI(model_name=modal,openai_api_key=api_key,openai_api_base=api_url,temperature=0.0)


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)
)

conversation_with_kg("我姐姐明天要过生日,我需要一束生日花束。")

conversation_with_kg("她喜欢粉色玫瑰,颜色是粉色的。")

conversation_with_kg("我又来了,还记得我昨天为什么要来买花吗?")


print(conversation_with_kg.memory.kg)
print(conversation_with_kg.memory.kg.get_triples())

如以上代码所示,多次对话过后,模型进行了多个概念的关系组合,形成了一个小型的知识网络,执行结果截图如下:

这样,机器人的记忆获得了概念化的升华,从低级的语言序列,上升为关系网络的知识模型。这也使得记忆的应用更加灵活:

  • 可以进行知识推理,呈现更多潜在关联
  • 记忆容量也得到扩展

用好实体,AI也会联想

除了建模知识,大模型记忆的另一个途径是通过实体链接链接到大脑知识。ConversationEntityMemory通过实体链接、消歧实现这一目标。

例如用户提到“苹果”,实体识别组件会判断其为“苹果公司”,从Wikipedia等知识源关联大量相关背景常识。这样当用户询问“蒂姆库克”时,机器人也能很自然地联想到他是苹果公司的CEO这一事实。

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


api_key = ""

api_url = ""

modal= "baichuan"

llm = OpenAI(model_name=modal,openai_api_key=api_key,openai_api_base=api_url,temperature=0.0)

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

conversation("我姐姐明天要过生日,我需要一束生日花束。")

conversation("她喜欢粉色玫瑰,颜色是粉色的。")

print("\n记忆1:\n",conversation.memory.entity_cache)

conversation("你能帮我联系花店送货上门吗?")

print("\n记忆2:\n",conversation.memory.entity_cache)

如以上代码所示,在不同的对话阶段,模型提炼出了不同的对话实体,对对话内容的理解也更具关联性,执行结果截图如下:

实体和知识图谱可以有效地帮助机器人记住和扩展背景知识,从而支持更丰富、连贯的对话交互。与此同时,相比纯文本的对话历史,基于结构化知识的记忆也更加可解释。

这就是LangChain平台中几种主要的记忆组件。通过它们,开发者可以打造兼具智能对话和知识应用的AI助手。

总结

LangChain作为业内领先的大模型应用平台,提供了多种实用的记忆组件帮助开发者构建有“记忆力”的AI系统。

这些记忆组件从不同角度试图解决之前机器对话固有的遗忘问题:

  • ConversationBufferMemory:基于对话历史缓存实现简单的全记忆
  • ConversationBufferWindowMemory:利用时间窗口机制控制记忆容量
  • ConversationSummaryMemory:通过提取语义摘要,记住关键信息并舍弃Noise
  • ConversationKGMemory:将对话实体和事件图谱化,实现知识级记忆
  • ConversationEntityMemory:连接外部实体知识,辅助机器人记忆和联想

开发者可以根据实际需求选择使用不同的记忆组件,使AI对话内容更加深层次。此外,这些组件也可以进行定制与扩展,以开发出基于LangChain的创新型应用。

希望本教程能让更多开发者了解并掌握大模型强大的记忆功能,构建出与日俱进、知识渊博的AI助手。

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月24日 0

暂无评论

推荐阅读
GnFX9Na5BZpa