專案程式碼解讀
# 官方庫
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 RecursiveCharacterTextSplitter
import faiss
import numpy as np
from tqdm import tqdm
# 內部庫
from client import get_client
from conversation import postprocess_text, preprocess_text, Conversation, Role
client = 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 = None
self.embeddings_size = 768
self.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 = None def __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,從而進入初始化例項的程式碼中。