01 LangChain-Chatchat项目介绍
Langchain-Chatchat 是一个基于 ChatGLM 大语言模型与 Langchain 应用框架实现,开源、可离线部署的检索增强生成 (RAG) 大模型的本地知识库问答应用项目。目前(截止20241113)LangChain-Chatchat源码的github项目已经有5.6K的fork和32K的star了,可以说非常流行。github地址如下:
https://github.com/chatchat-space/Langchain-Chatchat
02 LangChain-Chatchat项目实现原理
本项目实现原理如下图所示,主要包括三块,第一块是构建本地知识库的向量检索库。为了提升LLM在专业领域的问答效果,将本地知识库中和问题相关的知识作为上下文添加到问题中提供给LLM,丰富的上下文可以提供更多的相关知识,让LLM生成效果更好。主要过程包括加载文件 -> 读取文本 -> 文本分割 -> 文本向量化 ;第二块就是将问题去本地知识的向量检索库中去查找最相关的tok个知识。主要包括问句向量化 -> 在文本向量中匹配出与问句向量最相似的 top k 个;第三块就是将匹配的相关知识作为上下文和问题一起添加到prompt模本中提交给LLM来生成答案。
从文档处理角度看实现流程如下图所示:
下面是一个实际的例子说明,比如我们基于书、教材、行业规范、操作手册等构建了一个知识库,然后用户输入"太平天国运动的历史影响",就会去知识库中找到和该问题相关的知识。将找到的相关知识作为上下文和问题一起添加到提示词模版中,调用像GPT-4、文心一言等大语言模型来生成答案。示例说明如下图所示:
03 LangChain-Chatchat项目部署
如果是想供个人学习使用而非业务需求,可以考虑在autoDL上租GPU配置,亲测3分钟内启动使用。因为有已经配好的镜像可以直接使用,所以流程非常简单,部署链接如下:
https://blog.youkuaiyun.com/wuexp/article/details/133928455
3.1 软硬件部署要求
软件要求如下图所示:
硬件要求如下图所示:
3.2 详细部署流程
3.2.1 下载项目文件和模型
LangChain-Chatchat运行至少需要两个模型(默认是用于对话的chatglm3-6b和用于embedding的bge-large-zh)。模型等大文件可以在HuggingFace上下载,建议手动下载大文件后再上传,速度更快。
具体步骤:
# 方法一:开启 git lfs 后直接 git clone 仓库
git lfs install
git clone https://huggingface.co/baichuan-inc/Baichuan2-13B-Chat
# 方法二:下载仓库基本信息,不下载大文件,然后再通过手动下载大文件
# 在model/下开一个jupyter notebook,通过如下代码连接huggingface
import os
os.environ['http_proxy'] = 'http://nbproxy.mlp.oppo.local:8888'
os.environ['https_proxy'] = 'http://nbproxy.mlp.oppo.local:8888'
# clone小文件
!GIT_LFS_SKIP_SMUDGE=1 git clone https://huggingface.co/BAAI/bge-reranker-large
# 手动下载大文件后上传
方法一的方式会将仓库中的 git 记录一并下载,导致下载下来的文件比较大,建议是采用方法二的方式,速度更快整体文件更小。
修改读取模型的位置为模型的绝对路径:/Langchain-Chatchat/configs/model_config.py
(至少需要修改chatglm3-6b和bge-large-zh的模型路径)
保留你需要使用的模型图片:
注意事项:不要直接git clone,无法clone大文件。对比hugging face上的文件大小,确保文件是完整的,不要漏下,issues上不少报错是由于文件没下载完整。
3.2.2 查看系统环境并安装cuda版本的pytorch
主要是查看系统cuda的版本及安装情况
(1)查看系统支持cuda最大版本,输入命令nvidia-smi:
其中红线框中的数字表明此服务器中支持的最大cuda版本,因此服务器中可以安装此版本及以下版本。
(2)安装cuda版本的pytorch
# 以cuda11.3为例
!pip install torch==1.11.0+cu113 torchvision==0.12.0+cu113 torchaudio==0.11.0 --extra-index-url https://download.pytorch.org/whl/cu113
3.2.3 安装依赖requirements.txt
安装之前需要把文件中与torch有关的语句隐掉:
输入命令安装相关依赖:
pip install -r requirements.txt
3.2.4 初始化知识库
如果是第一次运行本项目,知识库尚未建立,或者配置文件中的知识库类型、Embedding模型发生变化,需要以下命令初始化或重建知识库:
python init_database.py --recreate-vs
3.2.5 启动项目
启动前,确保已经按照参数配置正确配置各config模块。一键启动脚本 startup.py, 一键启动所有 Fastchat 服务、API 服务、WebUI 服务,示例代码:
python startup.py -a
可选参数包括 -a (或–all-webui), --all-api, --llm-api, -c (或–controller), --openai-api, -m (或–model-worker), --api, --webui,其中:
- –all-webui 为一键启动 WebUI 所有依赖服务;
- –all-api 为一键启动 API 所有依赖服务;
- –llm-api 为一键启动 Fastchat 所有依赖的 LLM 服务;
- –openai-api 为仅启动 FastChat 的 controller 和 openai-api-server 服务;
其他为单独服务启动选项。
若想指定非默认模型,需要用 --model-name 选项,示例:
$ python startup.py --all-webui --model-name Qwen-7B-Chat
3.3 部署过程中可能需要的问题
3.3.1 异常1:能打开页面但页面显示TypeError
报错描述:单独运行streamlit run webui.py时出现TypeError: ‘NoneType’ object is not iterable(能成功打开界面,但界面上显示报错)
具体报错:
File "/root/miniconda3/lib/python3.8/site-packages/streamlit/runtime/scriptrun
exec(code, module.dict)
File "/root/autodl-tmp/Langchain-Chatchat/webui.py", line 64, in
pages[selected_page]["func"](api=api, is_lite=is_lite)
File "/root/autodl-tmp/Langchain-Chatchat/webui_pages/dialogue/dialogue.py",
running models = list(api.list running models())
'NoneType' object is not iterable
解决方案:关于该报错,github上的issues上很多人遇到,可能的解决方案有以下几种:
- 因为没有运行后端,无法找到对应模型:解决方案是用python startup.py -a的方式一键启动所有 Fastchat 服务、API 服务、WebUI 服务,不要单独启动WebUI服务(虽然网上很多教程说可以单独启动)。
- 关闭代理:由于内网问题,设置如下代理可能会报此错误,删除如下代码即可
- 安装pip install httpx==0.25.0
- 重启项目
3.3.2 异常2:API通信遇到错误
报错描述:首次对话没问题,继续对话出现API通信遇到错误
具体报错:
ERROR: RemoteProtocolError: API通信遇到错误:peer closed connection without sending complete message body (incomplete chunked read)
解决方案:
主要是联网的问题,issues上有很多类似的报错
由于oppo在starfire上访问外网(如huggingface)需要加入下代码:
os.environ['http_proxy'] = 'http://nbproxy.mlp.oppo.local:8888'
os.environ['https_proxy'] = 'http://nbproxy.mlp.oppo.local:8888'
3.3.3 异常3:lib报错
报错描述:ImportError: libffi.so.6: cannot open shared object file: No such file or directory
解决方案:
sudo ln -s /usr/lib/x86_64-linux-gnu/libffi.so.6 /usr/lib/x86_64-linux-gnu/libffi.so.7
参考:https://stackoverflow.com/questions/61875869/ubuntu-20-04-upgrade-python-missing-libffi-so-6#;https://www.jianshu.com/p/20e92799504d
3.3.4 其他报错
ImportError: cannot import name ‘DEFAULT_CIPHERS’ from 'urllib3.util.ssl
pip install urllib3==1.25.11
cannot import name ‘ddg’ from ‘duckduckgo_search’
pip install duckduckgo-search==2.9.0
AttributeError: ‘Chatbot’ object has no attribute ‘style’
!pip install gradio==3.48.0
AttributeError: module ‘lib’ has no attribute ‘X509_V_FLAG_CB_ISSUER_CHECK’
https://blog.youkuaiyun.com/weixin_45666566/article/details/133960436
pydantic.errors.PydanticUserError: If you use @root_validator
with pre=False (the default) you MUST specify skip_on_failure=True
. Note that @root_validator
is deprecated and should be replaced with @model_validator
.
pip install pydantic==1.10.13
ImportError: cannot import name ‘RetrievalQA’ from ‘langchain.chains’ (/opt/conda/lib/python3.7/site-packages/langchain/chains/init.py)
LangChain版本不对
KeyError: ‘st.session_state has no key “conversation_ids”. Did you forget to initialize it? More info: https://docs.streamlit.io/library/advanced-features/session-state#initialization’
打开webui应使用命令streamlit run webui.py,根据报错信息,推测使用的是命令 python webui.py
libGL.so.1: cannot open shared object file: No such file or directory
pip install opencv-python-headless
RuntimeError: The NVIDIA driver on your system is too old (found version 11040). Please update your GPU driver by downloading and installing a new version from the URL: http://www.nvidia.com/Download/index.aspx Alternatively, go to: https://pytorch.org to install a PyTorch version that has been compiled with your version of the CUDA driver.
原因:pytorch和cuda版本不适配
问题排查:
NVIDIA-smi # 查看GPU的版本
# 看torch是否可用
import torch
print(torch.__version__)
print(torch.cuda.is_available()) # 如果返回False就是pytorch版本和cuda版本不匹配
解决方案:(根据对应的cuda版本安装pytorch对应版本)
pip install torch==1.11.0+cu113 torchvision==0.12.0+cu113 torchaudio==0.11.0 --extra-index-url https://download.pytorch.org/whl/cu113
知识库页面的表格出
解决方案:https://github.com/chatchat-space/Langchain-Chatchat/issues/2795
04 LangChain-Chatchat项目源码解析
4.1 技术路线图
-
Langchain 应用
-
-
本地数据接入
-
- 接入非结构化文档
- 结构化数据接入
-
分词及召回
-
- 接入不同类型 TextSplitter
- 优化依据中文标点符号设计的 ChineseTextSplitter
-
搜索引擎接入
-
- Bing 搜索
- DuckDuckGo 搜索
- Metaphor 搜索
-
Agent 实现
-
- 基础React形式的Agent实现,包括调用计算器等
- Langchain 自带的Agent实现和调用
- 智能调用不同的数据库和联网知识
-
-
LLM 模型接入
-
- 支持通过调用 FastChat api 调用 llm
- 支持 ChatGLM API 等 LLM API 的接入
- 支持 Langchain 框架支持的LLM API 接入
-
Embedding 模型接入
-
- 支持调用 HuggingFace 中各开源 Emebdding 模型
- 支持 OpenAI Embedding API 等 Embedding API 的接入
- 支持 智谱AI、百度千帆、千问、MiniMax 等在线 Embedding API 的接入
-
基于 FastAPI 的 API 方式调用
-
Web UI
-
- 基于 Streamlit 的 Web UI
4.2 代码结构
4.2.1 webui.py
由于startup.py是以子进程的方式启动各个服务,为了将各组件解耦,就需要为每个组件编写单独的启动脚本。
就webui.py来说,它主要对外提供webui界面,通过下面的命令启动。
streamlit run webui.py --server.address 0.0.0.0 --server.port 6006 --server.enableCORS=false --server.enableXsrfProtection=false
webui.py内部基于streamlit框架实现了用户交互界面,包括前端和后端两个部分。用python实现的后端部分代码存放在webui_pages/目录下。而前端代码则是由streamlit框架进行绘制。
4.2.2 文档数据上传
解析文件路径:server/knowledge_base/utils.py
各个解析方法加载路径:document_loaders/mypdfloader.py
Langchian-Chatchat中对于不同类型的文件提供了不同的处理方式:
LOADER_DICT = {"UnstructuredHTMLLoader": ['.html'],
"UnstructuredMarkdownLoader": ['.md'],
"CustomJSONLoader": [".json"],
"CSVLoader": [".csv"],
"RapidOCRPDFLoader": [".pdf"],
"RapidOCRLoader": ['.png', '.jpg', '.jpeg', '.bmp'],
"UnstructuredFileLoader": ['.eml', '.msg', '.rst',
'.rtf', '.txt', '.xml',
'.docx', '.epub', '.odt',
'.ppt', '.pptx', '.tsv'],
}
作者撰写了单独的方法解析各个文件:
FilteredCSVloader.py
mydocloader.py
myimgloader.py
mypdfloader.py
mypptloader.py
ocr.py
下面以PDF解析方式为例(加载器为RapidOCRPDFLoader),处理方式为:
- 首先使用fitz(即pyMuPDF)的open方法解析PDF文件;
- 然后对于每一页的文本内容,通过get_text方法进行获取,而对于图片内容通过get_images方法进行获取,获取后通过RapidOCR对图片中的文本内容进行提取;
- 最后将从图片中提取的文本和原始的文本内容进行拼接,得到最终的所有文本内容。然后进行下一步的分词和文本切割。
4.2.3 文本切割
分词器支持LangChain定义的分词器和自定义的分词器。
LangChain定义的分词器有如下几种:
- RecursiveCharacterTextSplitter():按字符串分割文本,递归地尝试按不同的分隔符进行分割文本。
- CharacterTextSplitter():按字符来分割文本。
- MarkdownHeaderTextSplitter():基于指定的标题来分割markdown 文件
- TokenTextSplitter():按token来分割文本。
- SentenceTransformersTokenTextSplitter() : 按token来分割文本
- Language() - 用于 CPP、Python、Ruby、Markdown 等。
- NLTKTextSplitter():使用 NLTK(自然语言工具包)按句子分割文本
- SpacyTextSplitter() - 使用 Spacy按句子的切割文本。
自定义的分词器:
- AliTextSplitter
- ChineseRecursiveTextSplitter
- ChineseTextSplitter
默认使用作者自定义的分词器ChineseRecursiveTextSplitter,主要是通过[“\n\n”,“\n”,“。|!|?”, “.\s|!\s|?\s”,“;|;\s”,“,|,\s”]进行分割。
4.2.3 中文标题增强
可以使用这些识别的标题来增强后续文档内容的上下文。
使用is_possible_title函数使用特定标准来确定文本是否是潜在标题。这些包括检查文本长度,检查文本是否以标点符号结尾,检查文本长度是否超过设定值等。若识别出文本可能为潜在的中文标题,那么会作为page_content加入。
doc.page_content = f"下文与({title})有关。{doc.page_content}"
05 LangChain-Chatchat构建知识问答
知识库对话功能的后端是在server/chat/knowledge_base_chat.py方法中实现的。
主要包括以下输入参数:
- query:查询语句
- knowledge_base_name:知识库名称
- top_k:匹配向量数
- score_threshold:知识库匹配相关度阈值,取值范围在0-2之间,SCORE越小,相关度越高,取到2相当于不筛选。
- history:历史对话记录
- model_name:LLM 模型名称
- local_doc_url:是否返回原知识文件路径
整体构建步骤如下:
- 在方法内部,首先通过search_docs方法查询向量数据库,召回top_k个相似文档。
- 如果启用了reranker,则使用reranker对文档进行重新排序
- 然后通过换行符将docs拼接成context,这就是输入大模型的知识上下文。
- 如果没有找到相关的文档,则使用empty的prompt模板
- 随后使用模型代理和历史对话记录,初始化chain对象。最后将查询语句和上下文信息输入chain对象,通过asyncio库异步调用大模型进行处理。
- 末尾部分,将知识出处放入返回的json中。
5.1 从向量库中召回k个相似文档(search_docs)
# 在另一个线程中搜索相关文档
docs = await run_in_threadpool(search_docs,
query=query,
knowledge_base_name=knowledge_base_name,
top_k=top_k,
score_threshold=score_threshold)
search_docs的具体实现方法:/server/knowledge_base/kb_service/faiss_kb_service.py,具体流程如下:
- 将用户问题query向量化;
- 通过向量库的similarity_search_with_score_by_vector函数,传入embeddings、top_k和score_threshold。(返回结果数由top_k确定,最小相似度分数由score_threshold确定)。其中计算相似度通过余弦相似度进行匹配,score含义(distance score):L2距离,因此score分数越小,代表二者越相近;
- 返回元组列表,每个元组包含一个Document对象和一个表示query和文档相似度的分数。
embeddings = embed_func.embed_query(query)
with self.load_vector_store().acquire() as vs:
docs = vs.similarity_search_with_score_by_vector(embeddings, k=top_k, score_threshold=score_threshold)
LangChain-chatchat目前支持的向量数据库:(默认使用FAISS)
5.2 如果启用了reranker,则使用reranker对文档进行重新排序
if USE_RERANKER:
reranker_model_path = MODEL_PATH["reranker"].get(RERANKER_MODEL,"BAAI/bge-reranker-large")
reranker_model = LangchainReranker(top_n=top_k,
device=embedding_device(),
max_length=RERANKER_MAX_LENGTH,
model_name_or_path=reranker_model_path
)
docs = reranker_model.compress_documents(documents=docs,
query=query)
reranker的流程如下所示:
- 创建句子对:创建一个由query(查询)和page_content(文档内容)组成的句子对
- sentence_pairs = [[query, _doc] for _doc in _docs]
- 使用模型进行预测:由bge-reranker模型返回一个得分,表示query与page_content的相关性results = self._model.predict(sentences=sentence_pairs)
- 选择前n个文档values, indices = results.topk(top_k)
- 为文档分配相关性得分,将其添加到文档的元数据中
关于启动reranker是否能提升性能,答案是不一定。
issues上大家的实践经验对rerank的优化性能褒贬不一,有的使用rerank后效果提升,有的未提升。原因可能如下:
- 现有框架中,对于相似性越高的docs,会放在prompt的越前面,因为:“通过在开头放置更多相似的文档或上下文,该模型确保在生成响应时以更高的优先级考虑最相关的信息。“
- 有相关的研究表明,越相似的docs应该放在prompt的越后面。因为:检索返回的条目在prompt中的顺序对结果有影响。我们发现把最相似的排在后面,指标获得比较大的提升。原因可能是,相关的条目排在后面,距离question更近,LLM模型不容易遗忘,并且更容易找出答案。
5.3 然后通过换行符将docs拼接成context,这就是输入大模型的知识上下文
context = "\n".join([doc.page_content for doc in docs])
如果没有找到相关的文档,则使用empty的prompt模板
"empty": # 搜不到知识库的时候使用
'请你回答我的问题:\n'
'{{ question }}\n\n'
5.4 随后使用模型代理和历史对话记录,初始化chain对象
最后将查询语句和上下文信息输入chain对象,通过asyncio库异步调用大模型进行处理。
# 将用户输入转换为消息模板
input_msg = History(role="user", content=prompt_template).to_msg_template(False)
# 创建聊天提示
chat_prompt = ChatPromptTemplate.from_messages(
[i.to_msg_template() for i in history] + [input_msg])
# 创建LLM链
chain = LLMChain(prompt=chat_prompt, llm=model)
# 开始一个在后台运行的任务
task = asyncio.create_task(wrap_done(
chain.acall({"context": context, "question": query}),
callback.done),
)
5.5 末尾部分,将知识出处放入返回的json中。
# 生成源文档列表
source_documents = []
for inum, doc in enumerate(docs):
filename = doc.metadata.get("source")
parameters = urlencode({"knowledge_base_name": knowledge_base_name, "file_name": filename})
base_url = request.base_url
url = f"{base_url}knowledge_base/download_doc?" + parameters
text = f"""出处 [{inum + 1}] [{filename}]({url}) \n\n{doc.page_content}\n\n"""
source_documents.append(text)
# 如果没有找到相关文档,则添加红色提示
if len(source_documents) == 0:
source_documents.append(f"<span style='color:red'>未找到相关文档,该回答为大模型自身能力解答!</span>")
06 LangChain-Chatchat的prompt设计
主要包括有上下文提供和无上下文提供两种prompt:
"knowledge_base_chat": {
"default":
'<指令>根据已知信息,简洁和专业的来回答问题。如果无法从中得到答案,请说 “根据已知信息无法回答该问题”,'
'不允许在答案中添加编造成分,答案请使用中文。 </指令>\n'
'<已知信息>{{ context }}</已知信息>\n'
'<问题>{{ question }}</问题>\n',
"text":
'<指令>根据已知信息,简洁和专业的来回答问题。如果无法从中得到答案,请说 “根据已知信息无法回答该问题”,答案请使用中文。 </指令>\n'
'<已知信息>{{ context }}</已知信息>\n'
'<问题>{{ question }}</问题>\n',
"empty": # 搜不到知识库的时候使用
'请你回答我的问题:\n'
'{{ question }}\n\n',
}
07 LangChain-Chatchat其他项
7.1 LangChain-Chatchat效果这么好了,还有什么可以优化的方向?
issues上很多人提到,想要该项目效果更好,可以做的就是使用文档整理格式+分词器设计,效果还可以。目前项目使用的Chinese分割方式可以处理所有文档,但效果并不好,issues上有很多人开发的分词方式。
7.2 为什么LangChain-Chatchat速度这么快?
总结
本篇主要介绍了LangChain-Chatchat项目,可用于构建离线部署的检索增强生成 (RAG) 大模型的本地知识库问答应用项目,分别从项目介绍、实现原理、部署流程、源码解析以及构建知识问答等详细介绍了LangChain-Chatchat项目。对于想基于本地知识库构建知识问答项目的小伙伴可能有帮助。
如何学习AI大模型?
作为一名热心肠的互联网老兵,我决定把宝贵的AI知识分享给大家。 至于能学习到多少就看你的学习毅力和能力了 。我已将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。
这份完整版的大模型 AI 学习资料已经上传优快云,朋友们如果需要可以微信扫描下方优快云官方认证二维码免费领取【保证100%免费
】

一、全套AGI大模型学习路线
AI大模型时代的学习之旅:从基础到前沿,掌握人工智能的核心技能!
二、640套AI大模型报告合集
这套包含640份报告的合集,涵盖了AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师,还是对AI大模型感兴趣的爱好者,这套报告合集都将为您提供宝贵的信息和启示。
三、AI大模型经典PDF籍
随着人工智能技术的飞速发展,AI大模型已经成为了当今科技领域的一大热点。这些大型预训练模型,如GPT-3、BERT、XLNet等,以其强大的语言理解和生成能力,正在改变我们对人工智能的认识。 那以下这些PDF籍就是非常不错的学习资源。
四、AI大模型商业化落地方案
作为普通人,入局大模型时代需要持续学习和实践,不断提高自己的技能和认知水平,同时也需要有责任感和伦理意识,为人工智能的健康发展贡献力量。