项目代码解读
# 官方库
import os
import shutil
import time
from datetime import datetime
import logging
import pickle
from glob import glob
from typing import List# 第三方库
# langchain库
from langchain.document_loaders import UnstructuredPowerPointLoader, UnstructuredWordDocumentLoader, \UnstructuredPDFLoader, UnstructuredFileLoader
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.text_splitter import RecursiveCharacterTextSplitterimport faiss
import numpy as np
from tqdm import tqdm# 内部库
from client import get_client
from conversation import postprocess_text, preprocess_text, Conversation, Roleclient = get_client()
from langchain.embeddings import HuggingFaceEmbeddings
:这行代码导入了langchain
库中的HuggingFaceEmbeddings
类。from langchain.text_splitter import RecursiveCharacterTextSplitter
:这行代码导入了langchain
库中的RecursiveCharacterTextSplitter
类。
Embedding模型
我们在做RGA开发时又会涉及到向量数据库,在创建向量数据库时又需要使用Embedding模型对文本进行向量化处理,目前市面上的大模型如OpenAI,Gemini的模型都提供可供调用的Embedding模型,但这些大公司的Embedding模型都存在着种种限制条件比如OpenAI的Embedding模型不是免费的,它是根据token数量来收费的,这显而易见会增加我们的使用成本,而Gemini的Embedding模型目前可以免费使用,但是它又有每分钟不得超过60次调用的限制,这些种种的限制会给我们在创建向量数据库时带来很大的麻烦,为此我们需要找到一种免费的高性能的Embedding模型,经过我在网上不予余力的搜索,
终于找到了一个比较适合我们中国人使用的Embedding模型:BAAI的Embedding模型,而且它是我们中国人自己开发的,同时支持中文和英文。大家可以在github或者Huggingface上面查看他们模型的相关信息。
对于本项目,基于中文文档的需要,最终我们选择了这个模型:
shibing624/text2vec: text2vec, text to vector. 文本向量表征工具,把文本转化为向量矩阵,实现了Word2Vec、RankBM25、Sentence-BERT、CoSENT等文本表征、文本相似度计算模型,开箱即用。 (github.com)
langchain.text_splitter
RecursiveCharacterTextSplitter可以处理通用文本。它以一个字符列表作为参数,按照顺序尝试基于这些字符进行分割,直到块足够小。默认的字符列表是["\n\n", "\n", " ", ""]. 这样做的效果是尽可能保持段落(然后是句子,最后是单词)在一起,因为从通用的角度来看,这些通常是语义上最相关的文本片段。
class ChatDoc:# 实现单例模式_instance = None# 向量数据库的基路径db_base_path = "data/db"def __init__(self) -> None:self.llm = client# 向量数据库的引用self.vector_db = None# 字符串数据库的引用self.string_db = None# 文件列表self.files = Noneself.embeddings_size = 768self.embeddings = HuggingFaceEmbeddings(model_name="./embedding")# 初始化完成后打印一条消息,表示聊天机器人初始化成功。print("chatbot init success!")
_instance = None
:这行代码定义了一个私有静态变量_instance
,其初始值为None
。这用于实现单例模式。
模型参数:
db_base_path = "data/db"
:向量数据库的基路径。
self.llm = client
:模型ChatGLM3入口地址。
self.vector_db = None
:向量数据库的引用。
self.string_db = None
:字符串数据库的引用。
self.files = None
:文件列表的引用。
self.embeddings_size = 768
:这行代码初始化了一个名为self.embeddings_size
的实例变量,其值为768。这可能是嵌入的大小。
self.embeddings = HuggingFaceEmbeddings(model_name="./embedding")
:初始化embeddings
实例变量,参数是模型路径。
单例模式
python __instance
简单而言,单例模式就是保证某个实例在项目的整个生命周期中只存在一个,在项目的任意位置使用,都是同一个实例。
@classmethod
def get_instance(cls):if cls._instance is None:cls._instance = ChatDoc()return cls._instance
如果cls._instance
尚未初始化,则执行。
cls._instance = ChatDoc()
:这行代码创建一个新的ChatDoc
实例,并将其存储在cls._instance
变量中。这是单例实例的初始化。
return cls._instance
:这行代码返回cls._instance
变量,即单例实例。
通过使用get_instance
方法,您可以确保在整个程序中只有一个ChatDoc
实例被创建和访问。这有助于节省资源,因为您不需要为每个请求创建新的实例,并且可以保证所有操作都使用相同的实例。
边界情况
class Singleton(object):_instance = Nonedef __new__(cls, *args, **kw):if cls._instance is None:cls._instance = object.__new__(cls, *args, **kw)return cls._instance
这种写法有两个问题。
1.单例模式对应类实例化时无法传入参数,将上面的代码扩展成下面形式。
此时会抛出
TypeError: object.__new__() takes exactly one argument (the type to instantiate)
错误2.多个线程实例化Singleton类时,可能会出现创建多个实例的情况,因为很有可能多个线程同时判断cls._instance is None,从而进入初始化实例的代码中。