LangChain 核心模块学习:Memory

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

我们将存储过去交互信息的能力称为“记忆(Memory)”。

LangChain提供了许多用于向应用/系统中添加 Memory 的实用工具。这些工具可以单独使用,也可以无缝地集成到链中。

一个记忆系统(Memory System)需要支持两个基本操作:读取(READ)和写入(WRITE)

每个链都定义了一些核心执行逻辑,并期望某些输入。其中一些输入直接来自用户,但有些输入可能来自 Memory。

在一个典型 Chain 的单次运行中,将与其 Memory System 进行至少两次交互:

1. 在接收到初始用户输入之后,在执行核心逻辑之前,链将从其 Memory 中读取并扩充用户输入。

2. 在执行核心逻辑之后但在返回答案之前,一个链条将把当前运行的输入和输出写入Memory ,以便在未来的运行中可以引用它们。

BaseMemory Class 基类

类继承关系:

## 适用于简单的语言模型
BaseMemory --> BaseChatMemory --> <name>Memory # Examples: ZepMemory, MotorheadMemory
# 定义一个名为BaseMemory的基础类
class BaseMemory(Serializable, ABC):
"""用于Chains中的内存的抽象基类。 这里的内存指的是Chains中的状态。内存可以用来存储关于Chain的过去执行的信息,
并将该信息注入到Chain的未来执行的输入中。例如,对于会话型Chains,内存可以用来
存储会话,并自动将它们添加到未来的模型提示中,以便模型具有必要的上下文来连贯地
响应最新的输入。""" # 定义一个名为Config的子类
class Config:
"""为此pydantic对象配置。 Pydantic是一个Python库,用于数据验证和设置管理,主要基于Python类型提示。
""" # 允许在pydantic模型中使用任意类型。这通常用于允许复杂的数据类型。
arbitrary_types_allowed = True # 下面是一些必须由子类实现的方法: # 定义一个属性,它是一个抽象方法。任何从BaseMemory派生的子类都需要实现此方法。
# 此方法应返回该内存类将添加到链输入的字符串键。
@property
@abstractmethod
def memory_variables(self) -> List[str]:
"""获取此内存类将添加到链输入的字符串键。""" # 定义一个抽象方法。任何从BaseMemory派生的子类都需要实现此方法。
# 此方法基于给定的链输入返回键值对。
@abstractmethod
def load_memory_variables(self, inputs: Dict[str, Any]) -> Dict[str, Any]:
"""根据给链的文本输入返回键值对。""" # 定义一个抽象方法。任何从BaseMemory派生的子类都需要实现此方法。
# 此方法将此链运行的上下文保存到内存。
@abstractmethod
def save_context(self, inputs: Dict[str, Any], outputs: Dict[str, str]) -> None:
"""保存此链运行的上下文到内存。""" # 定义一个抽象方法。任何从BaseMemory派生的子类都需要实现此方法。
# 此方法清除内存内容。
@abstractmethod
def clear(self) -> None:
"""清除内存内容。"""

BaseChatMessageHistory Class 基类

类继承关系:

## 适用于聊天模型

BaseChatMessageHistory --> <name>ChatMessageHistory  # Example: ZepChatMessageHistory
# 定义一个名为BaseChatMessageHistory的基础类
class BaseChatMessageHistory(ABC):
"""聊天消息历史记录的抽象基类。""" # 在内存中存储的消息列表
messages: List[BaseMessage] # 定义一个add_user_message方法,它是一个方便的方法,用于将人类消息字符串添加到存储区。
def add_user_message(self, message: str) -> None:
"""为存储添加一个人类消息字符串的便捷方法。 参数:
message: 人类消息的字符串内容。
"""
self.add_message(HumanMessage(content=message)) # 定义一个add_ai_message方法,它是一个方便的方法,用于将AI消息字符串添加到存储区。
def add_ai_message(self, message: str) -> None:
"""为存储添加一个AI消息字符串的便捷方法。 参数:
message: AI消息的字符串内容。
"""
self.add_message(AIMessage(content=message)) # 抽象方法,需要由继承此基类的子类来实现。
@abstractmethod
def add_message(self, message: BaseMessage) -> None:
"""将Message对象添加到存储区。 参数:
message: 要存储的BaseMessage对象。
"""
raise NotImplementedError() # 抽象方法,需要由继承此基类的子类来实现。
@abstractmethod
def clear(self) -> None:
"""从存储中删除所有消息"""

ConversationChain and ConversationBufferMemory

ConversationBufferMemory 可以用来存储消息,并将消息提取到一个变量中。

from langchain_openai import OpenAI
from langchain.chains import ConversationChain
from langchain.memory import ConversationBufferMemory llm = OpenAI(temperature=0)
conversation = ConversationChain(
llm=llm,
verbose=True,
memory=ConversationBufferMemory()
)
> 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: 你好呀!
AI:
[INFO][2025-02-06 16:35:18.141] oauth.py:304 [t:3812]: successfully refresh token
> Finished chain.

ConversationBufferWindowMemory

ConversationBufferWindowMemory 会在时间轴上保留对话的交互列表。它只使用最后 K 次交互。这对于保持最近交互的滑动窗口非常有用,以避免缓冲区过大。

from langchain.memory import ConversationBufferWindowMemory

conversation_with_summary = ConversationChain(
llm=OpenAI(temperature=0, max_tokens=1000),
# 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="嗨,你最近过得怎么样?")

ConversationSummaryBufferMemory

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

from langchain.memory import ConversationSummaryBufferMemory

memory = ConversationSummaryBufferMemory(llm=llm, max_token_limit=10)
memory.save_context({"input": "嗨,你最近过得怎么样?"}, {"output": " 嗨!我最近过得很好,谢谢你问。我最近一直在学习新的知识,并且正在尝试改进自己的性能。我也在尝试更多的交流,以便更好地了解人类的思维方式。"})
memory.save_context({"input": "你最近学到什么新知识了?"}, {"output": " 最近我学习了有关自然语言处理的知识,以及如何更好地理解人类的语言。我还学习了有关机器学习的知识,以及如何使用它来改善自己的性能。"})
memory.load_memory_variables({})

LangChain基础篇 (03)的更多相关文章

  1. iOS系列 基础篇 03 探究应用生命周期

    iOS系列 基础篇 03 探究应用生命周期 目录: 1. 非运行状态 - 应用启动场景 2. 点击Home键 - 应用退出场景 3. 挂起重新运行场景 4. 内存清除 - 应用终止场景 5. 结尾 本 ...

  2. Java多线程系列--“基础篇”03之 Thread中start()和run()的区别

    概要 Thread类包含start()和run()方法,它们的区别是什么?本章将对此作出解答.本章内容包括:start() 和 run()的区别说明start() 和 run()的区别示例start( ...

  3. WebBug靶场基础篇 — 03

    基础篇 6 - 16 关... 在记录之前,先说一件事 = =! 小学生真多 = =!好心提供一个靶场,玩玩就算了,他挂黑页 ?现在好了,以后这个靶场不对外啊!你高兴了?爽了吧? 都是新手过来的,好心 ...

  4. Java基础篇(03):流程控制语句,和算法应用

    本文源码:GitHub·点这里 || GitEE·点这里 一.分支语句 流程控制语句对任何一门编程语言都是非常重要的,Java中基于流程控制程序执行的不同步骤和代码块. 1.IF条件 IF条件语句会根 ...

  5. MySQL基础篇(03):系统和自定义函数总结,触发器使用详解

    本文源码:GitHub·点这里 || GitEE·点这里 一.系统封装函数 MySQL 有很多内置的函数,可以快速解决开发中的一些业务需求,大概包括流程控制函数,数值型函数.字符串型函数.日期时间函数 ...

  6. C#基础篇03

    1:不管是实参还是形参,都在内存中开辟空间. 2:写一个方法,它的功能一定要单一,方法中最忌讳的就是出现提示用户输入的字眼. 3:out参数 如果在一个方法中,返回多个类型相同的值时,可以考虑返回一个 ...

  7. python 基础篇03

    本节主要内容:1. python基本数据类型回顾2. int----数字类型3. bool---布尔类型4. str--- 字符串类型一.python基本数据类型1. int ==> 整数. 主 ...

  8. Java多线程系列 基础篇03 线程的优先级和守护线程

    1. 线程优先级 现代操作系统中基本上使用时间分片的方式调度线程,通过设置线程优先级,使优先级高的线程获得时间片的次数多于优先级低的线程. 在java 线程中,通过一个整形变量prority来控制优先 ...

  9. scala基础篇-03 if与for

    一.Scala中的if是表达式** 1.定义方式 2.例子 二.for 的用法 1.定义方式: for{ x <- xs y=x+ ) }yield y 2.例子:

  10. mysql学习之基础篇03

    我们今天来进行建表的基本操作: 首先要建表就要了解列类型,因为建表就是声明列的过程,列声明完成了,表也就建好了. mysql中列分为三大类: 一.数值型 数值型又分为整型和浮点型两种. 先来看整型: ...

随机推荐

  1. 大语言模型中的MoE

    1.概述 MoE代表"混合专家模型"(Mixture of Experts),这是一种架构设计,通过将不同的子模型(即专家)结合起来进行任务处理.与传统的模型相比,MoE结构能够动 ...

  2. 一个关于CountDownLatch的并发需求

    需求 A,B,C可并发运行,全部成功才算成功,一个失败全员回滚. 思考 使用CountDownLatch,可以保证三个线程结束后,才进行提交成功状态.但是怎么才能判断某个任务失败了呢? 捕获子线程异常 ...

  3. Visual Studio 2017 rc 资源文件 预处理 宏 无效

    在属性c++下的预处理宏不会影响rc资源文件的,需要对rc资源文件单独设置. 右键rc资源文件,点击属性,在预处理器定义添加需要的宏

  4. 【异或运算】codeforces 1153 B. Dima and a Bad XOR

    前言 异或运算:是一种在二进制数系统中使用的逻辑运算.它的基本规则是对两个二进制位进行比较,如果这两个位不同,则结果为 \(1\):如果相同,则结果为 \(0\). 异或运算的规则 \(0\) XOR ...

  5. 云数据备份 | CDN 日志备份最佳实践

    前言 ​ 内容分发网络(Content Delivery Network,CDN),是在现有 Internet 中增加的一层新的网络架构,可以有效降低用户访问延迟,提升可用性. CDN 按照小时粒度对 ...

  6. Flutter WebView报错ERR_NAME_NOT_RESOLVED

    WebView报错ERR_NAME_NOT_RESOLVED 用的webview_flutter插件,开始都用的好好的,后面突然报错ERR_NAME_NOT_RESOLVED,上网逛了一圈说如果要用h ...

  7. simpleui

    目录 一.simpleui 1.1 使用步骤 1.2 功能介绍 1.3 展示大屏 一.simpleui 之前公司里,做项目前后端结合,要使用权限,要快速搭建后台管理,使用djagno的admin直接搭 ...

  8. Qt开发经验小技巧241-245

    QString类是我个人认为Qt所有类中的精华,封装的无可挑剔.内置了各种进制数据的转换,比如将数据转成10进制.16进制显示,或者将10进制.16进制数据转成字符串显示.这里很容易忽略的一点就是,很 ...

  9. Python设计模式(第2版)中文的pdf电子书

    Python设计模式(第2版)中文的pdf电子书下载地址:百度云盘,提取码:dmem

  10. U盘或光盘启动的Win7-8-10的PE系统制作步骤

    U盘或光盘启动的Win7-8-10的PE系统制作步骤 1.打开http://www.ushendu.com/下载PE制作工具 2.下载完成后安装到我的电脑, 把准备好的U盘插到电脑上,打开U深度PE制 ...