LangChain大型语言模型(LLM)应用开发(二):Conversation Memory

 

LangChain是一个基于大语言模型(如ChatGPT)用于构建端到端语言模型应用的 Python 框架。它提供了一套工具、组件和接口,可简化创建由大型语言模型 (LLM) 和聊天模型提供支持的应用程序的过程。LangChain 可以轻松管理与语言模型的交互,将多个组件链接在一起,以便在不同的应用程序中使用。

今天我们来学习DeepLearning.AI的在线课程:LangChain for LLM Application Development的第二门课:Memory,该门课程主要讲解几种和LLM交互时的内存记忆方法。一般情况下我们通过api的方式来访问openai的语言模型时,LLM是没有记忆能力的,也就是说LLM不能记住之前与用户对话的内容,要解决这个问题,我们必须每次与LLM对话时都必须将之前的所有对话内容全部输入给LLM,但这样也会增加程序的复杂性,同时也会增加经济成本,因为像ChatGPT这样的LLM是根据用户提交的数据内容的token数量来收费的,如果我们每次和LLM交互时提交的内容越多也就意味着token越多,那么就会产生越多的费用。这里Langchain提供了几种内存记忆组件可以帮助我们使用更高效和经济的方法来与LLM交互。

大纲

  • ConversationBufferMemory
  • ConversationBufferWindowMemory
  • ConversationTokenBufferMemory
  • ConversationSummaryMemory

ConversationBufferMemory

ConversationBufferMemory是一种最简单的记忆力组件,它会记住每次与LLM对话内容,并在下一轮对话时将历史对话记录全部传给LLM,这样LLM就会记住之前的对话内容,下面我们看一个例子:

import osfrom dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv()) # read local .env fileimport warnings
warnings.filterwarnings('ignore')
from langchain.chat_models import ChatOpenAI
from langchain.chains import ConversationChain
from langchain.memory import ConversationBufferMemoryllm = ChatOpenAI(temperature=0.0)
#定义ConversationBufferMemory记忆力组件
memory = ConversationBufferMemory()
conversation = ConversationChain(llm=llm, memory = memory,#加入ConversationBufferMemory组件verbose=True
)

这里我们定义了一个llm, 该llm默认使用的是openai的"gpt-3.5-turbo"模型,同时我们温度参数temperature设置为0.0,这个很重要,因为温度参数temperature代表了LLM回答问题时候的随机性,取值范围是0-1之间,如果temperature越大,则LLM回答问题的随机性就会越大,这里我们将temperature设置为0,其目的是让LLM每次只选择概率最高的答案,从而避免产生随机答案。

conversation.predict(input="你好,我的名字叫王老六")

 

 这里我们看到,在与LLM交互时Langchain会产生一个prompt其中除了用户的输入的内容外,还有一段英语的前缀信息,这里前缀信息+用户信息构成了一个完整的prompt,下面我们进行第二轮对话:

conversation.predict(input="1+1等于几?")

 在langchain产生的第二轮对话的prompt中,我们看到除了前缀信息以为,还增加了历史对话记录。下面我们看第三轮对话:

conversation.predict(input="你还记得我叫什么名字吗?")

 

 第三轮对话时用户询问了LLM是否还记得用户的名字,LLM给出了正确的答案,这是因为第三轮对话的prompt中包含了历史所有的对话记录,所以LLM能够记住用户的名字。这里记住所有历史对话记录的能力就是由ConversationBufferMemory组件来实现的。

下面我们可以查看ConversationBufferMemory的一些内置方法:

print(memory.buffer)

memory.load_memory_variables({})

 

memory = ConversationBufferMemory()memory.save_context({"input": "你好"}, {"output": "有啥事吗?"})print(memory.buffer)

 

memory.load_memory_variables({})

memory.save_context({"input": "想找你聊天,可以吗?"}, {"output": "好的,没问题!"})
memory.load_memory_variables({})

 

 ConversationBufferWindowMemory

ConversationBufferWindowMemory组件与ConversationBufferMemory组件功能类似,只是ConversationBufferWindowMemory组件增加了一个窗口参数k, 因为之前的ConversationBufferMemory组件会在prompt中记录历史所有的聊天对话内容,而ConversationBufferWindowMemory组件只会记住最近的k轮对话内容,更早之前的对话讲话被抛弃而不保存在prompt中,下面我们看一个例子:

from langchain.memory import ConversationBufferWindowMemoryllm = ChatOpenAI(temperature=0.0)#定义内存组件
memory = ConversationBufferWindowMemory(k=1)#k=1,意味着只能记住最后1轮对话内容
conversation = ConversationChain(llm=llm, memory = memory, #添加记忆力组件verbose=True #展示中间结果
)

 这里我们将ConversationBufferWindowMemory对象的参数K设置为1,这意味着在prompt中只保留最近一轮的历史对话记录。

conversation.predict(input="你好,我是王老六。")

 

conversation.predict(input="1+1等于几?")

conversation.predict(input="你还记得我叫什么名字吗?")

 

 由于在第三轮对话的时候,prompt中只保留了上一轮对话的历史记录,且没有包含首轮对话记录,因此LLM并不记得在首轮对话时用户告诉LLM关于用户名字的信息,因此此时LLM无法给出用户的名字。下面我们再做一些简单的测试:

memory = ConversationBufferWindowMemory(k=1)memory.save_context({"input": "Hi"},{"output": "What's up"})
memory.save_context({"input": "Not much, just hanging"},{"output": "Cool"})memory.load_memory_variables({})

 这里我们也可以看到,虽然我们给memory组件手动增加了两组对话记录,但是最终它只保存了一组对话记录,这是因为我们定义memory时设置了窗口参数k=1所导致的。

ConversationTokenBufferMemory

ConversationTokenBufferMemory组件的功能也是限制prompt中存储对话记录的数量,与ConversationBufferWindowMemory不同的是ConversationBufferWindowMemory组件是根据窗口参数K来限制对话条数,而ConversationTokenBufferMemory组件是根据token数量来限制prompt中的对话条数:

#!pip install tiktokenfrom langchain.memory import ConversationTokenBufferMemory
from langchain.llms import OpenAI
llm = ChatOpenAI(temperature=0.0)memory = ConversationTokenBufferMemory(llm=llm, max_token_limit=30)
memory.save_context({"input": "AI is what?!"},{"output": "Amazing!"})
memory.save_context({"input": "Backpropagation is what?"},{"output": "Beautiful!"})
memory.save_context({"input": "Chatbots are what?"}, {"output": "Charming!"})

上面我们在定义ConversationTokenBufferMemory时设置了参数max_token_limit的值为30,这意味着prompt中的历史对话数据的token数量不能超过30个token,接着我们给ConversationTokenBufferMemory组件手动增加了3轮对话记录,下面我们看看ConversationTokenBufferMemory组件最终能保存多少轮对话记录:

memory.load_memory_variables({})

 这里我们看到ConversationTokenBufferMemory组件保存了最近的30个token左右的对话记录,更早之前的对话记录已被丢弃,关于计算token的方法,不在本篇博客中说明。用户可以执行查阅相关资料。

ConversationSummaryMemory

ConversationSummaryMemory顾名思义会在prompt中保存历史对话记录的摘要,而不是完整的对话记录,下面我们来看一个例子:

from langchain.memory import ConversationSummaryBufferMemory
from langchain.chat_models import ChatOpenAI#定义llm
llm = ChatOpenAI(temperature=0.0)schedule ="""
上午 8 点与您的产品团队召开会议。\
您需要准备好幻灯片演示文稿。\
上午 9 点到中午 12 点有时间处理你的 LangChain 项目,\
这会进展得很快,因为 Langchain 是一个非常强大的工具。\
中午,在意大利餐厅与开车的顾客共进午餐\
距您一个多小时的路程,与您见面,了解人工智能的最新动态。\
请务必携带您的笔记本电脑来展示最新的LLM演示。\
"""
#定义ConversationSummaryBufferMemory组件
memory = ConversationSummaryBufferMemory(llm=llm, max_token_limit=100)
memory.save_context({"input": "你好"}, {"output": "什么事?"})
memory.save_context({"input": "没啥事情, 有个小问题请教"},{"output": "好的,请说"})
memory.save_context({"input": "今天的日程安排是什么?"}, {"output": f"{schedule}"})memory.load_memory_variables({})

在上面我们定义了一个ConversationSummaryMemory组件,并且设置了max_token_limit为100,这意味着prompt中的历史对话摘要的长度不能超过100个token。然后我们模拟了一组聊天记录,从返回的结果上看,memory组件中保存的不再是完整的对话记录,而是一段原始对话的摘要。保留摘要的好处是即保存了原始对话的内容的主要含义,又节省了token。

conversation = ConversationChain(llm=llm, memory = memory,verbose=True
)conversation.predict(input="一个好的演示应该展示什么?")

 参考资料

https://learn.deeplearning.ai/langchain/lesson/3/memory

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/7743.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Unity协程

unity提供了一种类似“多段代码并行执行”的功能,即协程。 我们在定义一个协程的时候,需要遵循类似这样的语法 IEnumerator(枚举器接口) namespace System.Collections {public interface IEnumerator{object Current { get; }/…

MySql基础知识及数据查询

目录 第一章 数据库概述 1.为什么要学习数据库? 2.数据库的相关概念 3.ORM(Object Relational Mapping)思想 4.表与表的记录之间存在哪些关联关系 第二章 基本的SELECT语句 1.SQL的分类 2. SQL基本规则 3.导入现有的数据表、表的数据 4.最基本的…

位图的详解

目录 位图 位图的概念 位图的实现 位图常见三道面试题 1.给定100亿个整数,设计算法找到只出现一次的整数? 2. 给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集? 3. 位图应用变形…

使用随便测测平台-做接口测试

目录 接口数据的来源 导出har文件 导入har文件 转化为用例 提取数据进行替换 如何选择哪些数据需要替换呢? Url Params、Data ​编辑进行替换操作 断言-冒烟 断言-详细 测试报告 结束 接口数据的来源 1、可通过charles工具,录制好接口操作&…

IDEA恢复误删除的文件

idea将删除的文件放在idea文件缓存中,文件的更改等信息都放在这个缓存中,所以短时间内删除的文件可以尝试恢复。

蚂蚁金服面试题解析:为什么String是HashMap中的绝佳Key类型?

大家好,我是小米,在今天的文章中,我将与大家一起探讨在使用HashMap时,选择使用String作为Key所带来的诸多好处。作为一位热爱技术的小伙伴,相信你一定对HashMap这个数据结构有所了解,那么,我们就…

【flutter滑动拼图验证码】

Java后台使用aj_captcha插件,提供/captcha/get(获取captcha底图和拼块图片)、/captcha/check(验证拼图偏移量)这两个接口。并且这个插件在GitHub上有源码。 1.先准备好aj_captcha的工具类: import dart:co…

【Spring/Java项目】如何利用swagger-parser解析yaml中的api请求类型、注释、接口名等等(含示例代码)

手打不易,如果转摘,请注明出处! 注明原文:https://zhangxiaofan.blog.csdn.net/article/details/129167371 目录 前言 官方文档 项目配置 示例代码 测试文件 解析代码 运行结果 前言 用到这个工具是因为项目需要&#xff0…

Java9集合类新增功能

前言 Java8及Java9在集合Collection类中新增了一些很好用的新方法&#xff0c;能方便程序员更便捷的处理集合数据&#xff0c;本文对其中的一些方法进行总结 一. List 1.创建 // 传统方法List<String> list1 new ArrayList<>();list1.add("item1");li…

FPGA实验一:层次法设计组合电路(加法器)

目录 一、实验目的 二、实验要求 三、实验代码 四、实验结果及分析 1、引脚锁定 2、仿真波形及分析 3、下载测试结果及分析 五、实验心得 一、实验目的 &#xff08;1&#xff09;掌握基本组合逻辑电路的 FPGA实现&#xff1b; &#xff08;2&#xff09;学习 Verilo…

烂sql导致clickhouse集群memory_tracking直线飙升触发熔断

版 本 v e r s i o n 1 9 . 1 7 . 4 . 1 1 c l i c k h o u s e 集 群 &#xff0c; 主 要 存 日 志 数 据 与 监 控 数 据 。 架 构 为 4 台 主 机 1 2 个 实 例 数 &#xff0c; 数 据 为 单 副 本 。 近 日 &#xff0c; 该 c l i c k h o u s e 集 群 有 一 台 物…

Leetcode 数据库刷题记录

https://leetcode-cn.com/problemset/database/ 题目都是leetcode 上的可以点击题目会有相应的链接 每道题后面都应相应的难度等级&#xff0c;如果没时间做的话 可以在leetcode 按出题频率刷题&#xff0c;答案仅供参考 175. 组合两个表 难度简单 SQL架构 表1: Person ---…