山东大学项目实训-基于LLM的中文法律文书生成系统(十九)- RAG(5)

项目代码解读

# 官方库
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()
  1. from langchain.embeddings import HuggingFaceEmbeddings:这行代码导入了langchain库中的HuggingFaceEmbeddings类。
  2. 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上面查看他们模型的相关信息。

img

对于本项目,基于中文文档的需要,最终我们选择了这个模型:

shibing624/text2vec: text2vec, text to vector. 文本向量表征工具,把文本转化为向量矩阵,实现了Word2Vec、RankBM25、Sentence-BERT、CoSENT等文本表征、文本相似度计算模型,开箱即用。 (github.com)

image-20240624005542326

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.单例模式对应类实例化时无法传入参数,将上面的代码扩展成下面形式。

python __instance_单例模式

此时会抛出TypeError: object.__new__() takes exactly one argument (the type to instantiate)错误

2.多个线程实例化Singleton类时,可能会出现创建多个实例的情况,因为很有可能多个线程同时判断cls._instance is None,从而进入初始化实例的代码中。

posted @ 2024-06-24 08:01  H1S96  阅读(133)  评论(0)    收藏  举报