学习总结

3 阅读4分钟

学习总结

通过这段代码,我实现了一个结合文档检索与聊天对话的智能聊天机器人(Chatbot)。其主要特点是能够根据用户输入的问题,从事先加载的文档中检索相关信息,并且能够与用户进行多轮对话。我的目标是通过引入大模型(LLM)和文档检索技术,提升聊天机器人的回答准确性和互动能力。

在构建过程中,我利用了langchain框架的多个功能模块:

  1. 文档加载与处理:通过PyPDFLoaderDocx2txtLoaderTextLoader等模块加载文档(如PDF、DOCX、TXT等),并进行文本分割处理,确保大文档可以被有效检索。
  2. 向量化与检索:通过OpenAIEmbeddings生成文档的向量表示,并使用Qdrant创建向量数据库,实现基于文本内容的检索功能。
  3. 对话记忆:引入ConversationSummaryMemory,使得聊天机器人能够记住上下文信息,处理多轮对话中的历史记录,从而使得回答更具连贯性和智能化。
  4. LLM与Retrieval链:结合ChatOpenAI作为语言模型,配合文档检索,利用ConversationalRetrievalChain实现对用户输入问题的响应。

通过这些技术的结合,我的目标是创建一个智能的、能够从特定文档库中获取信息并与用户自然对话的系统,进而为用户提供有用的知识和答案。

思考题:简化代码中的Memory部分

在代码中,我使用了ConversationSummaryMemory来进行多轮对话的记忆。根据第11讲的内容,ConversationChain实际上是对MemoryLLMChain的封装,它能简化记忆管理和模型交互的步骤。为了简化代码,可以考虑使用ConversationChain来替代ConversationSummaryMemoryLLMChain的组合。

ConversationChain可以自动管理记忆,且它已经集成了LLMMemory,因此不需要手动创建ConversationSummaryMemory对象。简化后的代码可以如下:

from langchain.chains import ConversationChain

class ChatbotWithRetrieval:

    def __init__(self, dir):
        base_dir = dir
        documents = []
        for file in os.listdir(base_dir):
            file_path = os.path.join(base_dir, file)
            if file.endswith('.pdf'):
                loader = PyPDFLoader(file_path)
                documents.extend(loader.load())
            elif file.endswith('.docx') or file.endswith('.doc'):
                loader = Docx2txtLoader(file_path)
                documents.extend(loader.load())
            elif file.endswith('.txt'):
                loader = TextLoader(file_path)
                documents.extend(loader.load())
        
        # 文本分割
        text_splitter = RecursiveCharacterTextSplitter(chunk_size=200, chunk_overlap=0)
        all_splits = text_splitter.split_documents(documents)
        
        # 向量数据库
        self.vectorstore = Qdrant.from_documents(
            documents=all_splits,
            embedding=OpenAIEmbeddings(),
            location=":memory:",
            collection_name="my_documents",
        )
        
        # 初始化LLM
        self.llm = ChatOpenAI()
        
        # 使用ConversationChain直接结合Memory和LLM
        retriever = self.vectorstore.as_retriever()
        self.qa = ConversationChain.from_llm(
            self.llm,
            memory_key="chat_history", 
            retriever=retriever
        )

    def chat_loop(self):
        print("Chatbot 已启动! 输入'exit'来退出程序。")
        while True:
            user_input = input("你: ")
            if user_input.lower() == 'exit':
                print("再见!")
                break
            # 调用ConversationChain进行对话
            response = self.qa({"input": user_input})
            print(f"Chatbot: {response['output']}")

在这个简化版的实现中,ConversationChain.from_llm自动创建了一个包含Memory的链,并处理了对话历史和检索功能的集成。这样一来,代码更加简洁,避免了手动管理Memory对象的步骤。

思考题:增加数据库查询能力

在现有的聊天机器人中增加数据库查询能力,可以参考第17讲的内容。通过调用数据库查询功能,我们可以让聊天机器人根据用户提供的查询条件,查询鲜花的库存或销售情况。假设我们有一个数据库,包含关于鲜花的表(如:flowers_inventory),并且我们能够通过SQL查询来获取相关信息。

为了将这一功能集成到聊天机器人中,我们可以在现有的ChatbotWithRetrieval类中增加一个查询数据库的功能模块。以下是一个简单的集成方法:

  1. 数据库连接:通过Python的数据库库(如sqlite3pymysql等)连接到数据库。
  2. 查询功能:添加一个查询功能,查询特定鲜花的库存、销售等信息。
  3. 与聊天机器人结合:在用户的输入中,判断是否涉及数据库查询任务,如果是,则调用数据库查询模块。

示例代码如下:

import sqlite3

class ChatbotWithRetrieval:

    def __init__(self, dir):
        # 省略其他初始化代码
        
        # 数据库连接
        self.db_conn = sqlite3.connect('flower_inventory.db')  # 连接数据库
        self.db_cursor = self.db_conn.cursor()
        
        # 初始化LLM和Retrieval Chain等

    def query_inventory(self, flower_name):
        # 查询数据库,返回鲜花库存情况
        query = "SELECT stock, sales FROM flowers_inventory WHERE name = ?"
        self.db_cursor.execute(query, (flower_name,))
        result = self.db_cursor.fetchone()
        if result:
            return f"{flower_name}的库存为{result[0]},销售情况为{result[1]}。"
        else:
            return f"未找到{flower_name}的信息。"
    
    def chat_loop(self):
        print("Chatbot 已启动! 输入'exit'来退出程序。")
        while True:
            user_input = input("你: ")
            if user_input.lower() == 'exit':
                print("再见!")
                break
            
            # 判断是否涉及查询数据库
            if '库存' in user_input or '销售' in user_input:
                flower_name = user_input.split(' ')[0]  # 假设用户输入的是类似 "玫瑰 库存情况" 的格式
                response = self.query_inventory(flower_name)
            else:
                # 调用Retrieval Chain进行文档检索
                response = self.qa({"input": user_input})["output"]
            
            print(f"Chatbot: {response}")

在这个扩展中,我增加了一个query_inventory方法来查询数据库,返回鲜花的库存和销售情况。然后,在chat_loop中,判断用户输入是否包含“库存”或“销售”,如果是,就调用query_inventory方法进行查询。如果不是查询请求,则继续执行文档检索功能。

通过这种方式,聊天机器人不仅能够处理文档中的信息,还能结合数据库的查询,增强了其业务能力。