获取相关信息的过程
在上一节中,我们成功的将内部文档都以“文档块嵌入片”的格式被存储在向量数据库里面了,那么当我们向模型提问的时候,模型是如何检索向量数据库并获取相关信息的呢?
获取相关信息的过程
-
存储与提取信息:
- 当我们把内部文档存储到一个叫向量数据库的地方后,我们需要根据用户的问题来找到最相关的信息。
- 这个过程包括把问题转换成一个“向量”,然后去和数据库里的其他向量进行比较,找出最接近的信息。
-
比较向量:
- 比较向量的方式有两种常见的方法:欧氏距离和余弦相似度。
欧氏距离
-
什么是欧氏距离
:
- 想象一下在平面上测量两点之间的直线距离,欧氏距离就是这样一种计算方法。在高维空间中,欧氏距离是通过计算对应维度的差值,平方后求和,再开平方根得到的。
-
何时使用
:
- 当我们关心绝对的数量差异,比如在推荐系统中了解用户的购买量时,欧氏距离会比较合适。
余弦相似度
-
什么是余弦相似度
:
- 这个方法关注的是向量的方向而非大小。它通过测量两个向量之间的角度差来判断它们的相似性,值的范围在-1到1之间,越接近1表示方向越相似。
-
何时使用
:
- 在处理文本或高维稀疏数据时,比如在信息检索和文本分类中,使用余弦相似度更加有效,因为文本的语义往往可以通过方向来反映。
应用实例
- 在建立问答系统时,我们更关注问题和答案的语义,因此选择使用余弦相似度来找到最匹配的答案。
重要组成部分
在实现问答系统的代码中,主要有两个部分:
- 大模型(LLM) :负责回答用户的问题。
- 检索器(retriever) :负责从数据库中找出相关文档。它会找到对应的“嵌入片”(即相关信息),并将这些信息传递给大模型。
这样,通过结合这两个部分,我们可以从内部文档中提取并回答用户的问题,而不是仅仅依赖于从互联网获取的信息。
现在我们开始模型准备和构建Retrieval链
代码结构
-
导入模块:注释解释了每个导入的用途,帮助理解代码结构。
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 向量存储模块 -
日志设置:日志记录的配置,如何设置特定模块的日志级别以便于调试。
# 设置日志记录 logging.basicConfig() # 配置日志记录的基本设置 # 设置特定模块的日志级别为 INFO,方便调试 logging.getLogger('langchain.retrievers.multi_query').setLevel(logging.INFO) -
模型实例化:以ZhipuAI为例,实例化大模型
# 实例化语言模型 llm = ChatZhipuAI(model="glm-4", temperature=0.5) -
向量存储:如何将文档转化为向量存储。
# 假设你已经有了一些文档数据,这里用列表表示 documents = [...] # 替换为你的实际文档数据 # 使用 FAISS 从文档创建向量存储 vectorstore = FAISS.from_documents(documents) # 将文档转换为向量存储对象 -
检索器和链:如何创建检索器和问答链,以及它们之间的关系。
# 实例化一个 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 ) -
执行查询:说明了如何运行查询并输出结果,确保用户知道如何测试其功能。
# 执行查询,替换为你的实际查询内容 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
成果
这里我只是简单的打印日志和模型回复信息,你可以进一步优化并处理回复内容。