{"cells":[{"cell_type":"markdown","id":"a786c77c","metadata":{"jp-MarkdownHeadingCollapsed":true,"tags":[]},"source":["# 第三章 储存\n","\n"," - [一、设置OpenAI API Key](#一、设置OpenAI-API-Key)\n"," - [二、对话缓存储存 ](#二、对话缓存储存--)\n"," - [2.1 开始对话,第一轮](#2.1-开始对话,第一轮)\n"," - [2.2 第二轮对话](#2.2-第二轮对话)\n"," - [2.3 第三轮对话](#2.3-第三轮对话)\n"," - [2.4 .memory.buffer存储了当前为止所有的对话信息](#2.4-.memory.buffer存储了当前为止所有的对话信息)\n"," - [2.5 也可以通过memory.load_memory_variables({})打印历史消息](#2.5-也可以通过memory.load_memory_variables({})打印历史消息)\n"," - [2.6 添加指定的输入输出内容到记忆缓存区](#2.6-添加指定的输入输出内容到记忆缓存区)\n"," - [三、对话缓存窗口储存](#三、对话缓存窗口储存)\n"," - [3.1 向memory添加两轮对话,并查看记忆变量当前的记录](#3.1-向memory添加两轮对话,并查看记忆变量当前的记录)\n"," - [3.2 在看一个例子,发现和上面的结果一样,只保留了一轮对话记忆](#3.2-在看一个例子,发现和上面的结果一样,只保留了一轮对话记忆)\n"," - [3.3 将对话缓存窗口记忆应用到对话链中](#3.3-将对话缓存窗口记忆应用到对话链中)\n"," - [四、对话token缓存储存](#四、对话token缓存储存)\n"," - [4.1 导入相关包和API密钥](#4.1-导入相关包和API密钥)\n"," - [4.2 限制token数量,进行测试](#4.2-限制token数量,进行测试)\n"," - [4.3 中文例子](#4.3-中文例子)\n"," - [五、对话摘要缓存储存](#五、对话摘要缓存储存)\n"," - [5.1 创建一个长字符串,其中包含某人的日程安排](#5.1-创建一个长字符串,其中包含某人的日程安排)\n"," - [5.2 基于上面的memory,新建一个对话链](#5.2-基于上面的memory,新建一个对话链)\n"," - [5.3 中文例子](#5.3-中文例子)\n"]},{"cell_type":"markdown","id":"7e10db6f","metadata":{},"source":["当你与那些语言模型进行交互的时候,他们不会记得你之前和他进行的交流内容,这在我们构建一些应用程序(如聊天机器人)的时候,是一个很大的问题---显得不够智能!因此,在本节中我们将介绍 LangChain 中的储存模块,即如何将先前的对话嵌入到语言模型中的,使其具有连续对话的能力。\n","\n","当使用 LangChain 中的储存模块时,它可以帮助保存和管理历史聊天消息,以及构建关于特定实体的知识。这些组件可以跨多轮对话存储信息,并允许在对话期间跟踪特定信息和上下文。LangChain 提供了多种储存类型。其中,缓冲区储存允许保留最近的聊天消息,摘要储存则提供了对整个对话的摘要。实体储存则允许在多轮对话中保留有关特定实体的信息。这些记忆组件都是模块化的,可与其他组件组合使用,从而增强机器人的对话管理能力。储存模块可以通过简单的API调用来访问和更新,允许开发人员更轻松地实现对话历史记录的管理和维护。\n","\n","此次课程主要介绍其中四种记忆模块,其他模块可查看文档学习。\n","- 对话缓存记忆 (ConversationBufferMemory)\n","- 对话缓存窗口记忆 (ConversationBufferWindowMemory)\n","- 对话令牌缓存记忆 (ConversationTokenBufferMemory)\n","- 对话摘要缓存记忆 (ConversationSummaryBufferMemory)\n","\n","在LangChain中,Memory指的是大语言模型(LLM)的短期记忆。为什么是短期记忆?那是因为LLM训练好之后(获得了一些长期记忆),它的参数便不会因为用户的输入而发生改变。当用户与训练好的LLM进行对话时,LLM会暂时记住用户的输入和它已经生成的输出,以便预测之后的输出,而模型输出完毕后,它便会“遗忘”之前用户的输入和它的输出。因此,之前的这些信息只能称作为LLM的短期记忆。 \n"," \n","为了延长LLM短期记忆的保留时间,则需要借助一些外部存储方式来进行记忆,以便在用户与LLM对话中,LLM能够尽可能的知道用户与它所进行的历史对话信息。 "]},{"cell_type":"markdown","id":"1ca56e6b-1e07-4405-a1ca-f4237f20fa75","metadata":{"tags":[]},"source":["## 一、设置OpenAI API Key\n","\n","登陆 [OpenAI 账户](https://platform.openai.com/account/api-keys) 获取API Key,然后将其设置为环境变量。\n","\n","- 如果你想要设置为全局环境变量,可以参考[知乎文章](https://zhuanlan.zhihu.com/p/627665725)。\n","- 如果你想要设置为本地/项目环境变量,在本文件目录下创建`.env`文件, 打开文件输入以下内容。\n","\n","
\n"," OPENAI_API_KEY=\"your_api_key\" \n","
\n"," \n"," 替换\"your_api_key\"为你自己的 API Key"]},{"cell_type":"code","execution_count":null,"id":"6932bd47-c6d5-4794-8102-a12b84412a93","metadata":{},"outputs":[],"source":["# 下载需要的包python-dotenv和openai\n","# 如果你需要查看安装过程日志,可删除 -q \n","!pip install -q python-dotenv\n","!pip install -q openai"]},{"cell_type":"code","execution_count":6,"id":"10446712-9fa6-4d71-94ce-2ea4cf197e54","metadata":{},"outputs":[],"source":["import os\n","import openai\n","from dotenv import load_dotenv, find_dotenv\n","\n","# 读取本地/项目的环境变量。\n","\n","# find_dotenv()寻找并定位.env文件的路径\n","# load_dotenv()读取该.env文件,并将其中的环境变量加载到当前的运行环境中 \n","# 如果你设置的是全局的环境变量,这行代码则没有任何作用。\n","_ = load_dotenv(find_dotenv())\n","\n","# 获取环境变量 OPENAI_API_KEY\n","openai.api_key = os.environ['OPENAI_API_KEY'] \n"]},{"cell_type":"markdown","id":"1297dcd5","metadata":{},"source":["## 二、对话缓存储存 \n"," \n","这种记忆允许存储消息,然后从变量中提取消息。"]},{"cell_type":"code","execution_count":2,"id":"20ad6fe2","metadata":{"height":98},"outputs":[],"source":["from langchain.chat_models import ChatOpenAI\n","from langchain.chains import ConversationChain\n","from langchain.memory import ConversationBufferMemory"]},{"cell_type":"code","execution_count":31,"id":"88bdf13d","metadata":{"height":133},"outputs":[],"source":["# 英文链\n","llm = ChatOpenAI(temperature=0.0) #temperature:预测下一个token时,概率越大的值就越平滑(平滑也就是让差异大的值之间的差异变得没那么大),temperature值越小则生成的内容越稳定\n","memory = ConversationBufferMemory()\n","conversation = ConversationChain( #新建一个对话链(关于链后面会提到更多的细节)\n"," llm=llm, \n"," memory = memory,\n"," verbose=True #查看Langchain实际上在做什么,设为FALSE的话只给出回答,看到不到下面绿色的内容\n",")"]},{"cell_type":"code","execution_count":32,"id":"bb4968d9","metadata":{},"outputs":[],"source":["# 中文链\n","llm = ChatOpenAI(temperature=0.0) #temperature:预测下一个token时,概率越大的值就越平滑(平滑也就是让差异大的值之间的差异变得没那么大),temperature值越小则生成的内容越稳定\n","memory_zh = ConversationBufferMemory()\n","conversation_zh = ConversationChain( #新建一个对话链(关于链后面会提到更多的细节)\n"," llm=llm, \n"," memory = memory_zh,\n"," verbose=True #查看Langchain实际上在做什么,设为FALSE的话只给出回答,看到不到下面绿色的内容\n",")"]},{"cell_type":"markdown","id":"dea83837","metadata":{},"source":["### 2.1 开始对话,第一轮"]},{"cell_type":"markdown","id":"1a3b4c42","metadata":{},"source":["当我们运行predict时,生成了一些提示,如下所见,他说“以下是人类和AI之间友好的对话,AI健谈“等等,这实际上是LangChain生成的提示,以使系统进行希望和友好的对话,并且必须保存对话,并提示了当前已完成的模型链。"]},{"cell_type":"code","execution_count":33,"id":"db24677d","metadata":{"height":47},"outputs":[{"name":"stdout","output_type":"stream","text":["\n","\n","\u001b[1m> Entering new chain...\u001b[0m\n","Prompt after formatting:\n","\u001b[32;1m\u001b[1;3mThe 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.\n","\n","Current conversation:\n","\n","Human: Hi, my name is Andrew\n","AI:\u001b[0m\n","\n","\u001b[1m> Finished chain.\u001b[0m\n"]},{"data":{"text/plain":["\"Hello Andrew! It's nice to meet you. How can I assist you today?\""]},"execution_count":33,"metadata":{},"output_type":"execute_result"}],"source":["conversation.predict(input=\"Hi, my name is Andrew\")"]},{"cell_type":"code","execution_count":34,"id":"154561c9","metadata":{},"outputs":[{"name":"stdout","output_type":"stream","text":["\n","\n","\u001b[1m> Entering new chain...\u001b[0m\n","Prompt after formatting:\n","\u001b[32;1m\u001b[1;3mThe 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.\n","\n","Current conversation:\n","\n","Human: 你好, 我叫皮皮鲁\n","AI:\u001b[0m\n","\n","\u001b[1m> Finished chain.\u001b[0m\n"]},{"data":{"text/plain":["'你好,皮皮鲁!很高兴认识你。我是一个AI助手,可以回答你的问题和提供帮助。有什么我可以帮你的吗?'"]},"execution_count":34,"metadata":{},"output_type":"execute_result"}],"source":["#中文\n","conversation_zh.predict(input=\"你好, 我叫皮皮鲁\")"]},{"cell_type":"markdown","id":"e71564ad","metadata":{},"source":["### 2.2 第二轮对话"]},{"cell_type":"markdown","id":"54d006bd","metadata":{},"source":["当我们进行下一轮对话时,他会保留上面的提示"]},{"cell_type":"code","execution_count":35,"id":"cc3ef937","metadata":{"height":31},"outputs":[{"name":"stdout","output_type":"stream","text":["\n","\n","\u001b[1m> Entering new chain...\u001b[0m\n","Prompt after formatting:\n","\u001b[32;1m\u001b[1;3mThe 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.\n","\n","Current conversation:\n","Human: Hi, my name is Andrew\n","AI: Hello Andrew! It's nice to meet you. How can I assist you today?\n","Human: What is 1+1?\n","AI:\u001b[0m\n","\n","\u001b[1m> Finished chain.\u001b[0m\n"]},{"data":{"text/plain":["'1+1 is equal to 2.'"]},"execution_count":35,"metadata":{},"output_type":"execute_result"}],"source":["conversation.predict(input=\"What is 1+1?\")"]},{"cell_type":"code","execution_count":36,"id":"63efc1bb","metadata":{},"outputs":[{"name":"stdout","output_type":"stream","text":["\n","\n","\u001b[1m> Entering new chain...\u001b[0m\n","Prompt after formatting:\n","\u001b[32;1m\u001b[1;3mThe 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.\n","\n","Current conversation:\n","Human: 你好, 我叫皮皮鲁\n","AI: 你好,皮皮鲁!很高兴认识你。我是一个AI助手,可以回答你的问题和提供帮助。有什么我可以帮你的吗?\n","Human: 1+1等于多少?\n","AI:\u001b[0m\n","\n","\u001b[1m> Finished chain.\u001b[0m\n"]},{"data":{"text/plain":["'1+1等于2。'"]},"execution_count":36,"metadata":{},"output_type":"execute_result"}],"source":["#中文\n","conversation_zh.predict(input=\"1+1等于多少?\")"]},{"cell_type":"markdown","id":"33cb734b","metadata":{},"source":["### 2.3 第三轮对话"]},{"cell_type":"markdown","id":"0393df3d","metadata":{},"source":["为了验证他是否记忆了前面的对话内容,我们让他回答前面已经说过的内容(我的名字),可以看到他确实输出了正确的名字,因此这个对话链随着往下进行会越来越长"]},{"cell_type":"code","execution_count":37,"id":"acf3339a","metadata":{"height":31},"outputs":[{"name":"stdout","output_type":"stream","text":["\n","\n","\u001b[1m> Entering new chain...\u001b[0m\n","Prompt after formatting:\n","\u001b[32;1m\u001b[1;3mThe 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.\n","\n","Current conversation:\n","Human: Hi, my name is Andrew\n","AI: Hello Andrew! It's nice to meet you. How can I assist you today?\n","Human: What is 1+1?\n","AI: 1+1 is equal to 2.\n","Human: What is my name?\n","AI:\u001b[0m\n","\n","\u001b[1m> Finished chain.\u001b[0m\n"]},{"data":{"text/plain":["'Your name is Andrew.'"]},"execution_count":37,"metadata":{},"output_type":"execute_result"}],"source":["conversation.predict(input=\"What is my name?\")"]},{"cell_type":"code","execution_count":38,"id":"2206e5b7","metadata":{},"outputs":[{"name":"stdout","output_type":"stream","text":["\n","\n","\u001b[1m> Entering new chain...\u001b[0m\n","Prompt after formatting:\n","\u001b[32;1m\u001b[1;3mThe 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.\n","\n","Current conversation:\n","Human: 你好, 我叫皮皮鲁\n","AI: 你好,皮皮鲁!很高兴认识你。我是一个AI助手,可以回答你的问题和提供帮助。有什么我可以帮你的吗?\n","Human: 1+1等于多少?\n","AI: 1+1等于2。\n","Human: 我叫什么名字?\n","AI:\u001b[0m\n","\n","\u001b[1m> Finished chain.\u001b[0m\n"]},{"data":{"text/plain":["'你叫皮皮鲁。'"]},"execution_count":38,"metadata":{},"output_type":"execute_result"}],"source":["#中文\n","conversation_zh.predict(input=\"我叫什么名字?\")"]},{"cell_type":"markdown","id":"5a96a8d9","metadata":{},"source":["### 2.4 .memory.buffer存储了当前为止所有的对话信息"]},{"cell_type":"code","execution_count":39,"id":"2529400d","metadata":{"height":31},"outputs":[{"name":"stdout","output_type":"stream","text":["Human: Hi, my name is Andrew\n","AI: Hello Andrew! It's nice to meet you. How can I assist you today?\n","Human: What is 1+1?\n","AI: 1+1 is equal to 2.\n","Human: What is my name?\n","AI: Your name is Andrew.\n"]}],"source":["print(memory.buffer) #提取历史消息"]},{"cell_type":"code","execution_count":40,"id":"d948aeb2","metadata":{},"outputs":[{"name":"stdout","output_type":"stream","text":["Human: 你好, 我叫皮皮鲁\n","AI: 你好,皮皮鲁!很高兴认识你。我是一个AI助手,可以回答你的问题和提供帮助。有什么我可以帮你的吗?\n","Human: 1+1等于多少?\n","AI: 1+1等于2。\n","Human: 我叫什么名字?\n","AI: 你叫皮皮鲁。\n"]}],"source":["# 中文\n","print(memory_zh.buffer) #提取历史消息"]},{"cell_type":"markdown","id":"6bd222c3","metadata":{},"source":["### 2.5 也可以通过memory.load_memory_variables({})打印历史消息"]},{"cell_type":"markdown","id":"0b5de846","metadata":{},"source":["这里的花括号实际上是一个空字典,有一些更高级的功能,使用户可以使用更复杂的输入,但我们不会在这个短期课程中讨论它们,所以不要担心为什么这里有一个空的花括号。"]},{"cell_type":"code","execution_count":41,"id":"5018cb0a","metadata":{"height":31},"outputs":[{"data":{"text/plain":["{'history': \"Human: Hi, my name is Andrew\\nAI: Hello Andrew! It's nice to meet you. How can I assist you today?\\nHuman: What is 1+1?\\nAI: 1+1 is equal to 2.\\nHuman: What is my name?\\nAI: Your name is Andrew.\"}"]},"execution_count":41,"metadata":{},"output_type":"execute_result"}],"source":["memory.load_memory_variables({})"]},{"cell_type":"code","execution_count":42,"id":"af4b8b12","metadata":{},"outputs":[{"data":{"text/plain":["{'history': 'Human: 你好, 我叫皮皮鲁\\nAI: 你好,皮皮鲁!很高兴认识你。我是一个AI助手,可以回答你的问题和提供帮助。有什么我可以帮你的吗?\\nHuman: 1+1等于多少?\\nAI: 1+1等于2。\\nHuman: 我叫什么名字?\\nAI: 你叫皮皮鲁。'}"]},"execution_count":42,"metadata":{},"output_type":"execute_result"}],"source":["# 中文\n","memory_zh.load_memory_variables({})"]},{"cell_type":"markdown","id":"07d2e892","metadata":{},"source":["### 2.6 添加指定的输入输出内容到记忆缓存区"]},{"cell_type":"code","execution_count":43,"id":"14219b70","metadata":{"height":31},"outputs":[],"source":["memory = ConversationBufferMemory() #新建一个空的对话缓存记忆"]},{"cell_type":"code","execution_count":45,"id":"a36e9905","metadata":{"height":48},"outputs":[],"source":["memory.save_context({\"input\": \"Hi\"}, #向缓存区添加指定对话的输入输出\n"," {\"output\": \"What's up\"})"]},{"cell_type":"code","execution_count":46,"id":"61631b1f","metadata":{"height":31},"outputs":[{"name":"stdout","output_type":"stream","text":["Human: Hi\n","AI: What's up\n","Human: Hi\n","AI: What's up\n"]}],"source":["print(memory.buffer) #查看缓存区结果"]},{"cell_type":"code","execution_count":47,"id":"a2fdf9ec","metadata":{"height":31},"outputs":[{"data":{"text/plain":["{'history': \"Human: Hi\\nAI: What's up\\nHuman: Hi\\nAI: What's up\"}"]},"execution_count":47,"metadata":{},"output_type":"execute_result"}],"source":["memory.load_memory_variables({}) #再次加载记忆变量"]},{"cell_type":"code","execution_count":48,"id":"27d8dd2f","metadata":{},"outputs":[{"data":{"text/plain":["{'history': 'Human: 你好,我叫皮皮鲁\\nAI: 你好啊,我叫鲁西西'}"]},"execution_count":48,"metadata":{},"output_type":"execute_result"}],"source":["#中文\n","memory = ConversationBufferMemory()\n","memory.save_context({\"input\": \"你好,我叫皮皮鲁\"}, \n"," {\"output\": \"你好啊,我叫鲁西西\"})\n","memory.load_memory_variables({})"]},{"cell_type":"markdown","id":"2ac544f2","metadata":{},"source":["继续添加新的内容,对话历史都保存下来在了!"]},{"cell_type":"code","execution_count":49,"id":"7ca79256","metadata":{"height":64},"outputs":[],"source":["memory.save_context({\"input\": \"Not much, just hanging\"}, \n"," {\"output\": \"Cool\"})"]},{"cell_type":"code","execution_count":50,"id":"890a4497","metadata":{"height":31},"outputs":[{"data":{"text/plain":["{'history': 'Human: 你好,我叫皮皮鲁\\nAI: 你好啊,我叫鲁西西\\nHuman: Not much, just hanging\\nAI: Cool'}"]},"execution_count":50,"metadata":{},"output_type":"execute_result"}],"source":["memory.load_memory_variables({})"]},{"cell_type":"code","execution_count":51,"id":"2b614406","metadata":{},"outputs":[{"data":{"text/plain":["{'history': 'Human: 你好,我叫皮皮鲁\\nAI: 你好啊,我叫鲁西西\\nHuman: Not much, just hanging\\nAI: Cool\\nHuman: 很高兴和你成为朋友!\\nAI: 是的,让我们一起去冒险吧!'}"]},"execution_count":51,"metadata":{},"output_type":"execute_result"}],"source":["#中文\n","memory.save_context({\"input\": \"很高兴和你成为朋友!\"}, \n"," {\"output\": \"是的,让我们一起去冒险吧!\"})\n","memory.load_memory_variables({})"]},{"cell_type":"markdown","id":"8839314a","metadata":{},"source":["当我们在使用大型语言模型进行聊天对话时,**大型语言模型本身实际上是无状态的。语言模型本身并不记得到目前为止的历史对话**。每次调用API结点都是独立的。\n","\n","聊天机器人似乎有记忆,只是因为通常有快速的代码可以向LLM提供迄今为止的完整对话以及上下文。因此,Memory可以明确地存储到目前为止的所有术语或对话。这个Memory存储器被用作输入或附加上下文到LLM中,以便它可以生成一个输出,就好像它只有在进行下一轮对话的时候,才知道之前说过什么。\n"]},{"cell_type":"markdown","id":"cf98e9ff","metadata":{},"source":["## 三、对话缓存窗口储存\n"," \n","随着对话变得越来越长,所需的内存量也变得非常长。将大量的tokens发送到LLM的成本,也会变得更加昂贵,这也就是为什么API的调用费用,通常是基于它需要处理的tokens数量而收费的。\n"," \n","针对以上问题,LangChain也提供了几种方便的memory来保存历史对话。\n","其中,对话缓存窗口记忆只保留一个窗口大小的对话缓存区窗口记忆。它只使用最近的n次交互。这可以用于保持最近交互的滑动窗口,以便缓冲区不会过大"]},{"cell_type":"code","execution_count":52,"id":"66eeccc3","metadata":{"height":47},"outputs":[],"source":["from langchain.memory import ConversationBufferWindowMemory"]},{"cell_type":"markdown","id":"641477a4","metadata":{},"source":["### 3.1 向memory添加两轮对话,并查看记忆变量当前的记录"]},{"cell_type":"code","execution_count":53,"id":"3ea6233e","metadata":{"height":47},"outputs":[],"source":["memory = ConversationBufferWindowMemory(k=1) # k=1表明只保留一个对话记忆 "]},{"cell_type":"code","execution_count":54,"id":"dc4553fb","metadata":{"height":115},"outputs":[],"source":["memory.save_context({\"input\": \"Hi\"},\n"," {\"output\": \"What's up\"})\n","memory.save_context({\"input\": \"Not much, just hanging\"},\n"," {\"output\": \"Cool\"})\n"]},{"cell_type":"code","execution_count":55,"id":"6a788403","metadata":{"height":31},"outputs":[{"data":{"text/plain":["{'history': 'Human: Not much, just hanging\\nAI: Cool'}"]},"execution_count":55,"metadata":{},"output_type":"execute_result"}],"source":["memory.load_memory_variables({})"]},{"cell_type":"markdown","id":"9b401f0b","metadata":{},"source":["### 3.2 在看一个例子,发现和上面的结果一样,只保留了一轮对话记忆"]},{"cell_type":"code","execution_count":56,"id":"68a2907c","metadata":{},"outputs":[{"data":{"text/plain":["{'history': 'Human: 很高兴和你成为朋友!\\nAI: 是的,让我们一起去冒险吧!'}"]},"execution_count":56,"metadata":{},"output_type":"execute_result"}],"source":["#中文\n","memory = ConversationBufferWindowMemory(k=1) # k=1表明只保留一个对话记忆 \n","memory.save_context({\"input\": \"你好,我叫皮皮鲁\"}, \n"," {\"output\": \"你好啊,我叫鲁西西\"})\n","memory.save_context({\"input\": \"很高兴和你成为朋友!\"}, \n"," {\"output\": \"是的,让我们一起去冒险吧!\"})\n","memory.load_memory_variables({})"]},{"cell_type":"markdown","id":"63bda148","metadata":{},"source":["### 3.3 将对话缓存窗口记忆应用到对话链中"]},{"cell_type":"code","execution_count":57,"id":"4087bc87","metadata":{"height":133},"outputs":[],"source":["llm = ChatOpenAI(temperature=0.0)\n","memory = ConversationBufferWindowMemory(k=1)\n","conversation = ConversationChain( \n"," llm=llm, \n"," memory = memory,\n"," verbose=False #这里改为FALSE不显示提示,你可以尝试修改为TRUE后的结果\n",")"]},{"cell_type":"code","execution_count":61,"id":"0c737101","metadata":{},"outputs":[],"source":["# 中文版\n","llm = ChatOpenAI(temperature=0.0)\n","memory_zh = ConversationBufferWindowMemory(k=1)\n","conversation_zh = ConversationChain( \n"," llm=llm, \n"," memory = memory_zh,\n"," verbose=False #这里改为FALSE不显示提示,你可以尝试修改为TRUE后的结果\n",")"]},{"cell_type":"markdown","id":"b6d661e3","metadata":{},"source":["注意此处!由于这里用的是一个窗口的记忆,因此只能保存一轮的历史消息,因此AI并不能知道你第一轮对话中提到的名字,他最多只能记住上一轮(第二轮)的对话信息"]},{"cell_type":"code","execution_count":58,"id":"4faaa952","metadata":{"height":47},"outputs":[{"data":{"text/plain":["\"Hello Andrew! It's nice to meet you. How can I assist you today?\""]},"execution_count":58,"metadata":{},"output_type":"execute_result"}],"source":["conversation.predict(input=\"Hi, my name is Andrew\")"]},{"cell_type":"code","execution_count":63,"id":"b3a941b5","metadata":{},"outputs":[{"data":{"text/plain":["'你好,皮皮鲁!很高兴认识你。我是一个AI助手,可以回答你的问题和提供帮助。有什么我可以帮你的吗?'"]},"execution_count":63,"metadata":{},"output_type":"execute_result"}],"source":["conversation_zh.predict(input=\"你好,我是皮皮鲁\")"]},{"cell_type":"code","execution_count":64,"id":"bb20ddaa","metadata":{"height":31},"outputs":[{"data":{"text/plain":["'1+1 is equal to 2.'"]},"execution_count":64,"metadata":{},"output_type":"execute_result"}],"source":["conversation.predict(input=\"What is 1+1?\")"]},{"cell_type":"code","execution_count":65,"id":"90bf6403","metadata":{},"outputs":[{"data":{"text/plain":["'1+1等于2。'"]},"execution_count":65,"metadata":{},"output_type":"execute_result"}],"source":["conversation_zh.predict(input=\"1+1等于几\")"]},{"cell_type":"code","execution_count":66,"id":"489b2194","metadata":{"height":31},"outputs":[{"data":{"text/plain":["\"I'm sorry, but I don't have access to personal information.\""]},"execution_count":66,"metadata":{},"output_type":"execute_result"}],"source":["conversation.predict(input=\"What is my name?\")"]},{"cell_type":"code","execution_count":67,"id":"932ace4f","metadata":{},"outputs":[{"data":{"text/plain":["'很抱歉,我无法知道您的名字。'"]},"execution_count":67,"metadata":{},"output_type":"execute_result"}],"source":["conversation_zh.predict(input=\"我叫什么名字\")"]},{"cell_type":"markdown","id":"d2931b92","metadata":{},"source":["## 四、对话token缓存储存"]},{"cell_type":"markdown","id":"dff5b4c7","metadata":{},"source":["使用对话token缓存记忆,内存将限制保存的token数量。如果token数量超出指定数目,它会切掉这个对话的早期部分\n","以保留与最近的交流相对应的token数量,但不超过token限制。"]},{"cell_type":"code","execution_count":null,"id":"9f6d063c","metadata":{"height":31},"outputs":[],"source":["#!pip install tiktoken #需要用到tiktoken包,没有的可以先安装一下"]},{"cell_type":"markdown","id":"2187cfe6","metadata":{},"source":["### 4.1 导入相关包和API密钥"]},{"cell_type":"code","execution_count":68,"id":"fb9020ed","metadata":{"height":81},"outputs":[],"source":["from langchain.memory import ConversationTokenBufferMemory\n","from langchain.llms import OpenAI\n","\n","llm = ChatOpenAI(temperature=0.0)"]},{"cell_type":"markdown","id":"f3a84112","metadata":{},"source":["### 4.2 限制token数量,进行测试"]},{"cell_type":"code","execution_count":69,"id":"43582ee6","metadata":{"height":149},"outputs":[],"source":["memory = ConversationTokenBufferMemory(llm=llm, max_token_limit=30)\n","memory.save_context({\"input\": \"AI is what?!\"},\n"," {\"output\": \"Amazing!\"})\n","memory.save_context({\"input\": \"Backpropagation is what?\"},\n"," {\"output\": \"Beautiful!\"})\n","memory.save_context({\"input\": \"Chatbots are what?\"}, \n"," {\"output\": \"Charming!\"})"]},{"cell_type":"markdown","id":"7b62b2e1","metadata":{},"source":["可以看到前面超出的的token已经被舍弃了!!!"]},{"cell_type":"code","execution_count":70,"id":"284288e1","metadata":{"height":31},"outputs":[{"data":{"text/plain":["{'history': 'AI: Beautiful!\\nHuman: Chatbots are what?\\nAI: Charming!'}"]},"execution_count":70,"metadata":{},"output_type":"execute_result"}],"source":["memory.load_memory_variables({})"]},{"cell_type":"markdown","id":"f7f6be43","metadata":{},"source":["### 4.3 中文例子"]},{"cell_type":"code","execution_count":71,"id":"e9191020","metadata":{},"outputs":[{"data":{"text/plain":["{'history': 'AI: 轻舟已过万重山。'}"]},"execution_count":71,"metadata":{},"output_type":"execute_result"}],"source":["memory = ConversationTokenBufferMemory(llm=llm, max_token_limit=30)\n","memory.save_context({\"input\": \"朝辞白帝彩云间,\"}, \n"," {\"output\": \"千里江陵一日还。\"})\n","memory.save_context({\"input\": \"两岸猿声啼不住,\"},\n"," {\"output\": \"轻舟已过万重山。\"})\n","memory.load_memory_variables({})"]},{"cell_type":"markdown","id":"5e4d918b","metadata":{},"source":["补充: \n","\n","ChatGPT使用一种基于字节对编码(Byte Pair Encoding,BPE)的方法来进行tokenization(将输入文本拆分为token)。 \n","BPE是一种常见的tokenization技术,它将输入文本分割成较小的子词单元。 \n","\n","OpenAI在其官方GitHub上公开了一个最新的开源Python库:tiktoken,这个库主要是用来计算tokens数量的。相比较Hugging Face的tokenizer,其速度提升了好几倍