13.3.5 实现Web端的问答系统
文件app.py实现了一个基于 LangChain 和大语言模型ChatGLM的自动问答系统,支持用户上传文本文件作为知识库,并根据用户的问题在知识库中检索答案。用户可以选择不同的语言模型和嵌入模型,调整参数以获取更准确的答案。应用还提供了通过 Web 搜索获取答案的选项,并允许用户清除历史对话记录。文件app.py的具体实现流程如下所示。
(1)下面这段代码导入了一系列Python模块,包括系统模块、Gradio、NLTK(自然语言处理工具包)、Sentence Transformers、DuckDuckGo搜索引擎的API、LangChain库中的一些模块(用于处理文档和嵌入)、Hugging Face的模型嵌入库、LangChain中的提示模板等。同时,还导入了自定义的一些配置以及PyTorch库。
import os
from typing import List
import gradio as gr
import nltk
import sentence_transformers
from duckduckgo_search import ddg
from duckduckgo_search.utils import SESSION
from langchain.chains import RetrievalQA
from langchain_community.document_loaders import UnstructuredFileLoader
from langchain.embeddings.huggingface import HuggingFaceEmbeddings
from langchain.prompts import PromptTemplate
from langchain.prompts.prompt import PromptTemplate
from langchain_community.vectorstores import FAISS
from chatllm import ChatLLM
from chinese_text_splitter import ChineseTextSplitter
from config import *
import torch
(2)下面这段代码的主要功能如下:
- 将NLTK数据路径设置为当前文件所在目录下的"nltk_data"文件夹,这样可以确保NLTK在运行时能够找到所需的数据文件。
- 将一些配置变量赋值给对应的变量名,例如嵌入模型字典、LLM模型字典、嵌入设备、LLM设备、GPU数量等。
- 遍历LLM模型字典,并将所有模型名称存储在一个列表中,这样可以在后续的代码中使用这个列表来选择特定的LLM模型。
nltk.data.path = [os.path.join(os.path.dirname(__file__), "nltk_data")
] + nltk.data.path
embedding_model_dict = embedding_model_dict
llm_model_dict = llm_model_dict
EMBEDDING_DEVICE = EMBEDDING_DEVICE
LLM_DEVICE = LLM_DEVICE
num_gpus = num_gpus
init_llm = init_llm
init_embedding_model = init_embedding_model
llm_model_list = []
llm_model_dict = llm_model_dict
for i in llm_model_dict:
for j in llm_model_dict[i]:
llm_model_list.append(j)
(3)函数search_web(query)实现了一个简单的网络搜索功能,利用 DuckDuckGo 搜索引擎进行查询,然后将相关搜索结果的网页内容提取并返回。
def search_web(query):
SESSION.proxies = {
"http": f"socks5h://localhost:7890",
"https": f"socks5h://localhost:7890"
}
results = ddg(query)
web_content = ''
if results:
for result in results:
web_content += result['body']
return web_content
(4)类KnowledgeBasedChatLLM是一个知识驱动的聊天语言模型(Knowledge-Based ChatLLM)的实现。其中包含了初始化模型配置的方法init_model_config,用于加载和配置语言模型以及词嵌入模型。在init_model_config方法中:
- 首先,通过指定的词嵌入模型名称初始化了HuggingFaceEmbeddings对象,并使用SentenceTransformer加载词嵌入模型。
- 然后,初始化了一个ChatLLM对象作为语言模型,并根据传入的large_language_model参数配置了相应的模型类型和路径。
- 最后,调用了语言模型的load_llm方法加载模型到指定的设备上。
类KnowledgeBasedChatLLM的实例可以用于基于知识的对话系统,结合了预训练的语言模型和词嵌入模型,可以根据输入的问题和已有的知识进行回答。
class KnowledgeBasedChatLLM:
llm: object = None
embeddings: object = None
def init_model_config(
self,
large_language_model: str = init_llm,
embedding_model: str = init_embedding_model,
):
self.embeddings = HuggingFaceEmbeddings(
model_name=embedding_model_dict[embedding_model], )
self.embeddings.client = sentence_transformers.SentenceTransformer(
self.embeddings.model_name,
device=EMBEDDING_DEVICE,
cache_folder=os.path.join(MODEL_CACHE_PATH, self.embeddings.model_name),
trust_remote_code=True
)
self.llm = None
torch.cuda.empty_cache()
self.llm = ChatLLM()
if 'chatglm2' in large_language_model.lower():
self.llm.model_type = 'chatglm2'
self.llm.model_name_or_path = llm_model_dict['chatglm2'][
large_language_model]
elif 'chatglm' in large_language_model.lower():
self.llm.model_type = 'chatglm'
self.llm.model_name_or_path = llm_model_dict['chatglm'][
large_language_model]
elif 'belle' in large_language_model.lower():
self.llm.model_type = 'belle'
self.llm.model_name_or_path = llm_model_dict['belle'][
large_language_model]
elif 'vicuna' in large_language_model.lower():
self.llm.model_type = 'vicuna'
self.llm.model_name_or_path = llm_model_dict['vicuna'][
large_language_model]
elif 'internlm' in large_language_model.lower():
self.llm.model_type = 'internlm'
self.llm.model_name_or_path = llm_model_dict['internlm'][
large_language_model]
elif 'yuan2' in large_language_model.lower():
self.llm.model_type = 'yuan2'
self.llm.model_name_or_path = llm_model_dict['yuan2'][large_language_model]
self.llm.load_llm(llm_device=LLM_DEVICE, num_gpus=num_gpus)
(5)方法init_knowledge_vector_store用于初始化知识向量存储,并将其保存到本地。首先加载指定路径下的文档内容,然后使用预先初始化的词嵌入模型将文档转换为向量表示,并使用FAISS库将这些向量构建成一个向量存储。最后,将构建好的向量存储保存到本地,并返回该向量存储对象。
def init_knowledge_vector_store(self, filepath):
docs = self.load_file(filepath)
vector_store = FAISS.from_documents(docs, self.embeddings)
vector_store.save_local('faiss_index')
return vector_store