LangChain入门和应用#3 | 豆包MarsCode AI刷题

146 阅读5分钟

获取相关信息的过程

在上一节中,我们成功的将内部文档都以“文档块嵌入片”的格式被存储在向量数据库里面了,那么当我们向模型提问的时候,模型是如何检索向量数据库并获取相关信息的呢?

获取相关信息的过程

  1. 存储与提取信息

    • 当我们把内部文档存储到一个叫向量数据库的地方后,我们需要根据用户的问题来找到最相关的信息。
    • 这个过程包括把问题转换成一个“向量”,然后去和数据库里的其他向量进行比较,找出最接近的信息。
  2. 比较向量

    • 比较向量的方式有两种常见的方法:欧氏距离余弦相似度
欧氏距离
  • 什么是欧氏距离

    • 想象一下在平面上测量两点之间的直线距离,欧氏距离就是这样一种计算方法。在高维空间中,欧氏距离是通过计算对应维度的差值,平方后求和,再开平方根得到的。
  • 何时使用

    • 当我们关心绝对的数量差异,比如在推荐系统中了解用户的购买量时,欧氏距离会比较合适。
余弦相似度
  • 什么是余弦相似度

    • 这个方法关注的是向量的方向而非大小。它通过测量两个向量之间的角度差来判断它们的相似性,值的范围在-1到1之间,越接近1表示方向越相似。
  • 何时使用

    • 在处理文本或高维稀疏数据时,比如在信息检索和文本分类中,使用余弦相似度更加有效,因为文本的语义往往可以通过方向来反映。
应用实例
  • 在建立问答系统时,我们更关注问题和答案的语义,因此选择使用余弦相似度来找到最匹配的答案。
重要组成部分

在实现问答系统的代码中,主要有两个部分:

  1. 大模型(LLM) :负责回答用户的问题。
  2. 检索器(retriever) :负责从数据库中找出相关文档。它会找到对应的“嵌入片”(即相关信息),并将这些信息传递给大模型。

这样,通过结合这两个部分,我们可以从内部文档中提取并回答用户的问题,而不是仅仅依赖于从互联网获取的信息。

现在我们开始模型准备和构建Retrieval链

代码结构
  1. 导入模块:注释解释了每个导入的用途,帮助理解代码结构。

    import logging  # 导入日志记录模块
    from langchain.chat_models import ChatOpenAI  # 从 langchain 导入 ChatOpenAI 模型
    from langchain.retrievers.multi_query import MultiQueryRetriever  # 导入 MultiQueryRetriever 工具
    from langchain.chains import RetrievalQA  # 导入 RetrievalQA 链
    from langchain.vectorstores import FAISS  # 导入 FAISS 向量存储模块
    
  2. 日志设置:日志记录的配置,如何设置特定模块的日志级别以便于调试。

    # 设置日志记录
    logging.basicConfig()  # 配置日志记录的基本设置
    # 设置特定模块的日志级别为 INFO,方便调试
    logging.getLogger('langchain.retrievers.multi_query').setLevel(logging.INFO)
    
  3. 模型实例化:以ZhipuAI为例,实例化大模型

    # 实例化语言模型
    llm = ChatZhipuAI(model="glm-4", temperature=0.5)
    
  4. 向量存储:如何将文档转化为向量存储。

    # 假设你已经有了一些文档数据,这里用列表表示
    documents = [...]  # 替换为你的实际文档数据# 使用 FAISS 从文档创建向量存储
    vectorstore = FAISS.from_documents(documents)  # 将文档转换为向量存储对象
    
  5. 检索器和链:如何创建检索器和问答链,以及它们之间的关系。

    # 实例化一个 MultiQueryRetriever,结合 LLm 和向量存储
    retriever_from_llm = MultiQueryRetriever.from_llm(
        retriever=vectorstore.as_retriever(),  # 使用 vectorstore 创建检索器
        llm=llm  # 传入之前创建的 LLM
    )
    ​
    # 实例化一个 RetrievalQA 链,结合 LLM 和检索器
    qa_chain = RetrievalQA.from_chain_type(
        llm,  # 传入 LLM
        retriever=retriever_from_llm  # 传入 MultiQueryRetriever
    )
    
  6. 执行查询:说明了如何运行查询并输出结果,确保用户知道如何测试其功能。

    # 执行查询,替换为你的实际查询内容
    query = "你的查询内容"  # 设定查询问题
    result = qa_chain.run(query)  # 运行链以获取结果
    print(result)  # 打印结果
    

现在,我们开始着手构建完成实战项目

继续添加设置问答链,完善代码

# 设置日志
logging.basicConfig(level=logging.INFO)
# print("Starting the QA system...")
# 实例化语言模型
llm = ChatZhipuAI(model="glm-4", temperature=0.5)
# 实例化多查询检索器
retriever_from_llm = MultiQueryRetriever.from_llm(retriever=vectorstore.as_retriever(), llm=llm)
# 创建检索问答链
qa_chain = RetrievalQA.from_chain_type(llm, retriever=retriever_from_llm)
print("Ready to answer questions!")
while True:
    question = input("Enter your question: ")
    print(qa_chain({"query": question}))
    if question == 'exit':
        print("Exiting the QA system.")
        break

完整代码

这是使用zhipuAI搭建的智能问答项目,是以上完整的代码,有些许的结构优化

import logging  # 导入日志模块,用于记录信息和错误
import os  # 导入操作系统模块,用于处理文件和环境变量# 导入检索问答链
from langchain.chains import RetrievalQA
# 导入多查询检索器
from langchain.retrievers.multi_query import MultiQueryRetriever
# 导入文本切分器
from langchain.text_splitter import RecursiveCharacterTextSplitter
# 导入聊天模型
from langchain_community.chat_models import ChatZhipuAI
# 导入所需的文档加载器
from langchain_community.document_loaders import PyPDFLoader, Docx2txtLoader, TextLoader
from langchain_community.embeddings import ZhipuAIEmbeddings
# 导入向量存储和嵌入模块
from langchain_community.vectorstores import Qdrant
# 5. Output 问答系统的UI实现
from flask import Flask, request, render_template
​
​
def set_api_key():
    os.environ["ZHIPUAI_API_KEY"] = "Enter your ZhipuAI API key"
    """Set the ZhipuAI API key from environment variable or prompt the user."""
    if not os.getenv("ZHIPUAI_API_KEY"):
        os.environ["ZHIPUAI_API_KEY"] = input("Enter your ZhipuAI API key: ")
​
​
def load_documents(base_dir):
    """Load documents from a specified directory and split them into chunks."""
    loaders = {
        '.pdf': PyPDFLoader,
        '.docx': Docx2txtLoader,
        '.txt': TextLoader
    }
    print("Loading documents...")
    documents = []
    for file in os.listdir(base_dir):
        print("Processing file:" + file)
        file_path = os.path.join(base_dir, file)
        _, ext = os.path.splitext(file)
​
        if ext.lower() in loaders:
            loader = loaders[ext.lower()](file_path)
            documents.extend(loader.load())
        else:
            logging.warning(f"Unsupported file type: {file}")
​
    text_splitter = RecursiveCharacterTextSplitter(chunk_size=200, chunk_overlap=10)
    return text_splitter.split_documents(documents)
​
​
"""Main function to execute the document processing and retrieval."""
set_api_key()  # 设置API密钥
# 指定存放文档的基本目录
base_dir = r'E:\mathProject\LangChainPy\OneFlower'
# 加载和切分文档
chunked_documents = load_documents(base_dir)
​
# 创建一个向量存储
vectorstore = Qdrant.from_documents(
    documents=chunked_documents,
    embedding=ZhipuAIEmbeddings(),
    location=":memory:",
    collection_name="my_documents"
)
# 设置日志
logging.basicConfig(level=logging.INFO)
# print("Starting the QA system...")
# 实例化语言模型
llm = ChatZhipuAI(model="glm-4", temperature=0.5)
# 实例化多查询检索器
retriever_from_llm = MultiQueryRetriever.from_llm(retriever=vectorstore.as_retriever(), llm=llm)
# 创建检索问答链
qa_chain = RetrievalQA.from_chain_type(llm, retriever=retriever_from_llm)
print("Ready to answer questions!")
while True:
    question = input("Enter your question: ")
    result = qa_chain.invoke({"query": question})
    print(result)
    if question == 'exit':
        print("Exiting the QA system.")
        break

成果

image.png 这里我只是简单的打印日志和模型回复信息,你可以进一步优化并处理回复内容。