本地部署 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
参数禁用文件软链接,这样下载路径下所见即所得,详细解释请见上面提到的教程。
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
EMBEDDING_DEVICE = "cpu"
LLM_DEVICE = "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)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理