本地部署 Langchain-Chatchat & ChatGLM

 

1. 环境配置

  • 首先,确保你的机器安装了 Python 3.8 - 3.11 (我们强烈推荐使用 Python3.11)。
$ python --version
Python 3.11.7

接着,创建一个虚拟环境,并在虚拟环境内安装项目的依赖

# 拉取仓库
$ git clone https://github.com/chatchat-space/Langchain-Chatchat.git

# 进入目录
$ cd Langchain-Chatchat

# 安装全部依赖
$ pip install -r requirements.txt 
$ pip install -r requirements_api.txt
$ pip install -r requirements_webui.txt  

# 默认依赖包括基本运行环境(FAISS向量库)。如果要使用 milvus/pg_vector 等向量库,请将 requirements.txt 中相应依赖取消注释再安装。

请注意,LangChain-Chatchat 0.2.x 系列是针对 Langchain 0.0.x 系列版本的,如果你使用的是 Langchain 0.1.x 系列版本,需要降级您的Langchain版本。

2. 模型下载

如需在本地或离线环境下运行本项目,需要首先将项目所需的模型下载至本地,通常开源 LLM 与 Embedding 模型可以从 HuggingFace 下载。

以本项目中默认使用的 LLM 模型 THUDM/ChatGLM3-6B 与 Embedding 模型 BAAI/bge-large-zh 为例:

下载模型需要先安装 Git LFS ,然后运行

$ git lfs install
$ git clone https://huggingface.co/THUDM/chatglm3-6b
$ git clone https://huggingface.co/BAAI/bge-large-zh

3. 初始化知识库和配置文件

按照下列方式初始化自己的知识库和简单的复制配置文件

$ python copy_config_example.py
$ python init_database.py --recreate-vs

4. 一键启动

按照以下命令启动项目

$ python startup.py -a

5. 安装报错处理

1.ModuleNotFoundError: No module named 'pwd'

关于 ModuleNotFoundError: No module named 'pwd' ,可如下解决(我刚刚用这个方法解决了)

首先,创建 pwd.py 文件,内容如下

from os import * 
from pwd import * 

def get_username():
    return getpwuid(getuid())[0]
 

然后,将 pwd.py 文件拷贝到 Python安装路径/Lib/ 下

最后,重新运行 python init_database.py --recreate-vs

 

2.huggingface网站无法打开

huggingface-cli 是 Hugging Face 官方提供的命令行工具,自带完善的下载功能。

2.1. 安装依赖

pip install -U huggingface_hub

2.2 设置环境变量
Linux

export HF_ENDPOINT=https://hf-mirror.com

Windows Powershell

$env:HF_ENDPOINT = "https://hf-mirror.com"

建议将上面这一行写入 ~/.bashrc
2.3 下载模型

huggingface-cli download --resume-download gpt2 --local-dir gpt2

2.4 下载数据集

huggingface-cli download --repo-type dataset --resume-download wikitext --local-dir wikitext

可以添加 --local-dir-use-symlinks False 参数禁用文件软链接,这样下载路径下所见即所得,详细解释请见上面提到的教程。

3.CPU部署
1.下载对应的torch
pip install torch==2.1.2+cpu torchvision==0.16.2+cpu torchaudio==2.1.2 -f https://download.pytorch.org/whl/torch_stable.html

2.修改configs/model_config.py文件,将EMBEDDING_DEVICE和LLM_DEVICE的值从'auto'改为‘cpu’.
EMBEDDING_DEVICE = "cpu"
LLM_DEVICE = "cpu"

3.修改configs/server_config.py文件,将chatglm3-6b的device值从'cuda'改为‘cpu’.
    "chatglm3-6b": {
        "device": "cpu",
    }

6. PDF解析优化

1.在Langchain-Chatchat\document_loaders目录下新建一个myminerpdfloader.py文件用于解析PDF。


import re

from bs4 import BeautifulSoup
from typing import List

from langchain.document_loaders import PDFMinerPDFasHTMLLoader
from langchain.docstore.document import Document


class MinerPDFLoader(PDFMinerPDFasHTMLLoader):
    def load(self) -> List[Document]:
        loader = PDFMinerPDFasHTMLLoader(self.file_path)
        data = loader.load()[0]
        # print(data)
        
        soup = BeautifulSoup(data.page_content,'html.parser')
        content = soup.find_all('div')
        # print(content)

        cur_fs = None
        cur_text = ''
        snippets = []   # first collect all snippets that have the same font size
        for c in content:
            sp = c.find('span')
            if not sp:
                continue
            st = sp.get('style')
            if not st:
                continue
            fs = re.findall('font-size:(\d+)px',st)
            if not fs:
                continue
            fs = int(fs[0])
            if not cur_fs:
                cur_fs = fs
            
            # removing page number
            pn = re.findall("^(\d+) \n$", c.text)
            if pn:
                continue
            # removing duplicate snippets (as headers/footers in a PDF appear on multiple pages so if we find duplicates it's safe to assume that it is redundant info)
            # if (cur_text,cur_fs) in snippets:
            #     continue

            if fs == cur_fs:
                cur_text += c.text
            else:
                snippets.append((cur_text,cur_fs))
                cur_fs = fs
                cur_text = c.text
        snippets.append((cur_text,cur_fs))

        cur_idx = -1
        semantic_snippets = []
        # Assumption: headings have higher font size than their respective content
        for s in snippets:
            # if current snippet's font size > previous section's heading => it is a new heading
            if not semantic_snippets or s[1] > semantic_snippets[cur_idx].metadata['heading_font']:
                metadata={'heading':s[0], 'content_font': 0, 'title_font': 0, 'heading_font': s[1]}
                metadata.update(data.metadata)
                semantic_snippets.append(Document(page_content='',metadata=metadata))
                cur_idx += 1
                continue   
            
            # if current snippet's font size <= previous section's content => content belongs to the same section (one can also create
            # a tree like structure for sub sections if needed but that may require some more thinking and may be data specific)
            if not semantic_snippets[cur_idx].metadata['title_font']:
                semantic_snippets[cur_idx].page_content += s[0]
                semantic_snippets[cur_idx].metadata['title_font'] = s[1]
                # print(semantic_snippets[cur_idx].metadata['title_font'])
                # cur_idx += 1
                continue

            if s[1] < semantic_snippets[cur_idx].metadata['title_font']:
                semantic_snippets[cur_idx].page_content += s[0]
                semantic_snippets[cur_idx].metadata['content_font'] = max(s[1], semantic_snippets[cur_idx].metadata['content_font'])
                # print(semantic_snippets[cur_idx].metadata['title_font'])
                continue
            
            if s[1] == semantic_snippets[cur_idx].metadata['title_font']:
                metadata={'heading':semantic_snippets[cur_idx].metadata['heading'], 'content_font': 0, 'title_font': semantic_snippets[cur_idx].metadata['title_font'], 'heading_font': semantic_snippets[cur_idx].metadata['heading_font']}
                metadata.update(data.metadata)
                semantic_snippets.append(Document(page_content=s[0],metadata=metadata))
                cur_idx += 1
                continue   
            #二层标题
            #首先选取heading跟title
                #如果heading为空,则当前行为heading,转下一行
                #如果title为空,则当前行为title,转下一行
            #如果当前字体
                #小于title 合并 保持heading 和title大小不变
                #等于title换行 保持heading 和title大小不变
                #大于tile换行 重置heading为当前行
            
            # if current snippet's font size > previous section's content but less tha previous section's heading than also make a new 
            # section (e.g. title of a pdf will have the highest font size but we don't want it to subsume all sections)
            metadata={'heading':s[0], 'content_font': 0, 'title_font': 0, 'heading_font': s[1]}
            metadata.update(data.metadata)
            semantic_snippets.append(Document(page_content=s[0], metadata=metadata))
            cur_idx += 1

        return semantic_snippets


if __name__ == "__main__":
    loader = MinerPDFLoader(file_path="/Users/tonysong/Desktop/test.pdf")
    docs = loader.load()
    print(docs)

2.修改document_loaders\__init__.py文件,添加import MinerPDFLoader

from .mypdfloader import RapidOCRPDFLoader
from .myimgloader import RapidOCRLoader
from .mydocloader import RapidOCRDocLoader
from .mypptloader import RapidOCRPPTLoader
from .myminerpdfloader import MinerPDFLoader


3.修改server\knowledge_base\utils.py文件,修改PDF文件的加载类为MinerPDFLoader,并将类MinerPDFLoader添加import列表

# 修改PDF文件的加载类为MinerPDFLoader
LOADER_DICT = { 
               # "RapidOCRPDFLoader": [".pdf"],
               "MinerPDFLoader": [".pdf"],
}
 
#将类MinerPDFLoader添加import列表
 try:
        if loader_name in ["RapidOCRPDFLoader", "RapidOCRLoader", "FilteredCSVLoader",
                           "RapidOCRDocLoader", "RapidOCRPPTLoader", "MinerPDFLoader"]:
            document_loaders_module = importlib.import_module('document_loaders')
        else:
            document_loaders_module = importlib.import_module('langchain.document_loaders')
        DocumentLoader = getattr(document_loaders_module, loader_name)
posted @   有何m不可  阅读(851)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示