基于 ChatGLM3-6B 与 LangChain 搭建 RAG 知识库助手:从向量数据库构建到 Gradio 部署全流程
2026/9/20 5:29:12 网站建设 项目流程

基于 ChatGLM3-6B 与 LangChain 搭建 RAG 知识库助手:从向量数据库构建到 Gradio 部署全流程

【免费下载链接】self-llm《开源大模型食用指南》针对中国宝宝量身打造的基于Linux环境快速微调(全参数/Lora)、部署国内外开源大模型(LLM)/多模态大模型(MLLM)教程项目地址: https://gitcode.com/GitHub_Trending/se/self-llm

本教程讲解如何在本地部署的 ChatGLM3-6B 基础上,利用 LangChain 框架完成一条完整的检索增强生成(RAG)知识库助手流水线:语料采集与清洗、文本分块与向量化、Chroma 向量数据库构建与持久化、自定义 LLM 类封装、检索问答链(RetrievalQA)搭建,以及基于 Gradio 的 Web Demo 部署。读完本文,你将掌握一套不依赖云端 API、完全基于本地开源模型的私有知识库问答方案,并理解 LangChain 在其中的底层调用关系。

前置环境与依赖安装

本文假设你已经完成了 ChatGLM3-6B 的本地部署(模型权重位于/root/autodl-tmp/ZhipuAI/chatglm3-6b),可参考仓库中 01-ChatGLM3-6B Transformer 部署调用 与 02-ChatGLM3-6B FastApi 部署调用 完成模型下载与推理验证。在 Transformer 部署篇中,模型通过modelscopesnapshot_download('ZhipuAI/chatglm3-6b', cache_dir='/root/autodl-tmp', ...)下载,因此本地模型路径为/root/autodl-tmp/ZhipuAI/chatglm3-6b,本文所有代码均沿用该路径。

在已完成模型部署的基础上,还需安装以下与 LangChain 知识库体系配套的依赖包(版本以仓库 05-ChatGLM3-6B接入LangChain搭建知识库助手 目录下的实现为准):

pip install langchain==0.0.292 pip install gradio==4.4.0 pip install chromadb==0.4.15 pip install sentence-transformers==2.2.2 pip install unstructured==0.10.30 pip install markdown==3.3.7

各依赖在流水线中的角色如下:

依赖包版本作用
langchain0.0.292提供文档加载器、文本分割器、Embedding 接口、向量库封装与检索问答链
gradio4.4.0搭建知识库助手的 Web 交互界面
chromadb0.4.15向量数据库,负责向量化后的文档存储与语义检索
sentence-transformers2.2.2本地运行开源词向量(Embedding)模型,将文本转换为语义向量
unstructured0.10.30提供对 Markdown、txt 等非结构化文件的解析能力
markdown3.3.7unstructured 解析 Markdown 文档时依赖的底层库

同时,需要使用开源词向量模型Sentence Transformer(HuggingFace 模型名为sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2)。该模型是一个多语言(含中文)的 MiniLM 语义向量模型,输出 384 维向量,能够较好地对中英文句子进行语义编码。可以将其模型参数以类似于下载 ChatGLM3-6B 模型参数的方式下载到本地/root/autodl-tmp/sentence-transformer,后续所有 Embedding 操作均从该本地路径加载,避免每次运行重复联网下载。

语料准备:克隆开源教程仓库作为知识库来源

本项目选择 Datawhale 系列 LLM 开源教程作为示例语料库,包括:

  • Self LLM:围绕开源大模型、针对国内初学者、基于 AutoDL 平台的中国宝宝专属大模型教程(即当前仓库本身的教程内容);
  • LLM Universe:面向小白开发者的大模型应用开发教程,结合个人知识库助手项目,通过一个课程完成大模型开发的重点入门;
  • LLM tutorial for Developers:面向开发者的 LLM 入门教程,基于吴恩达老师大模型系列课程内容实现;
  • So Large LLM:大规模预训练语言模型教程,覆盖数据准备、模型构建、训练策略到模型评估与改进,以及安全、隐私、环境与法律道德等维度;
  • Hugging LLM:介绍 ChatGPT 原理、使用和应用,降低使用门槛,让非 NLP 或算法专业人士也能无障碍使用 LLM 创造价值。

首先将上述远程开源仓库 Clone 到本地(AutoDL 环境下先开启学术资源加速,克隆完毕后关闭):

# 进入到数据库盘 cd /root/autodl-tmp # 打开学术资源加速 source /etc/network_turbo # clone 上述开源仓库 git clone https://github.com/datawhalechina/self-llm.git git clone https://github.com/datawhalechina/llm-universe.git git clone https://github.com/datawhalechina/prompt-engineering-for-developers.git git clone https://github.com/datawhalechina/so-large-lm.git git clone https://github.com/datawhalechina/hugging-llm.git # 关闭学术资源加速 unset http_proxy && unset https_proxy

语料选择说明:为方便处理,选用上述仓库中所有的markdowntxt文件作为示例语料库。也可以选用代码文件加入知识库,但代码文件格式(缩进、特殊符号、不同语言语法)需要额外的解析处理,本文不做展开。

知识库搭建:从原始文件到持久化向量数据库

知识库搭建是 RAG 的第一环,本质上是将人类可读的文档转化为机器可检索的语义向量。完整流程为:递归收集目标文件 → 用对应 Loader 解析为纯文本 → 文本分块 → 文本块向量化 → 写入 Chroma 向量数据库并持久化。仓库中对应完整实现见 create_db.py。

第一步:递归收集目标文件路径

定义一个函数,递归遍历指定文件夹路径,返回其中所有后缀名为.md.txt的文件路径:

import os def get_files(dir_path): # args:dir_path,目标文件夹路径 file_list = [] for filepath, dirnames, filenames in os.walk(dir_path): # os.walk 函数将递归遍历指定文件夹 for filename in filenames: # 通过后缀名判断文件类型是否满足要求 if filename.endswith(".md"): # 如果满足要求,将其绝对路径加入到结果列表 file_list.append(os.path.join(filepath, filename)) elif filename.endswith(".txt"): file_list.append(os.path.join(filepath, filename)) return file_list

这里使用 Python 标准库os.walk,它会自顶向下遍历目录树,每次返回当前目录路径、子目录列表和文件列表三要素,因此天然适配任意深度的文档目录结构。

第二步:按文件类型加载为纯文本

得到所有目标文件路径后,使用 LangChain 提供的 FileLoader 家族来加载目标文件,得到由文件解析出的纯文本对象。不同类型文件需要对应不同的 Loader,因此判断文件类型后针对性调用,并通过load()方法得到加载后的纯文本对象:

from tqdm import tqdm from langchain.document_loaders import UnstructuredFileLoader from langchain.document_loaders import UnstructuredMarkdownLoader def get_text(dir_path): # args:dir_path,目标文件夹路径 # 首先调用上文定义的函数得到目标文件路径列表 file_lst = get_files(dir_path) # docs 存放加载之后的纯文本对象 docs = [] # 遍历所有目标文件 for one_file in tqdm(file_lst): file_type = one_file.split('.')[-1] if file_type == 'md': loader = UnstructuredMarkdownLoader(one_file) elif file_type == 'txt': loader = UnstructuredFileLoader(one_file) else: # 如果是不符合条件的文件,直接跳过 continue docs.extend(loader.load()) return docs

UnstructuredMarkdownLoaderUnstructuredFileLoader均基于unstructured库实现:前者针对 Markdown 的标题、列表、代码块等结构做针对性解析,后者作为通用文件加载器兜底处理 txt 等纯文本文件。两者load()返回的 Document 对象包含page_content(纯文本)与metadata(来源路径等)两个核心字段,后续分块与向量化均基于该对象进行。

第三步:文本分块

得到docs(Document 对象列表)之后,就可以引入 LangChain 框架构建向量数据库。构建前需要先对文本分块,再对文本块向量化。分块的目的是把长文档切成语义相对完整、长度可控的片段,保证检索精度与向量化效率的平衡。

LangChain 提供了多种文本分块工具,此处使用字符串递归分割器RecursiveCharacterTextSplitter,它按优先级依次尝试多种分隔符(段落、句子、单词等)递归切分,直到所有块满足长度要求。此处选择分块大小 500、块重叠长度 150:

from langchain.text_splitter import RecursiveCharacterTextSplitter text_splitter = RecursiveCharacterTextSplitter( chunk_size=500, chunk_overlap=150) split_docs = text_splitter.split_documents(docs)

参数说明:

  • chunk_size=500:每个文本块的最大字符数。块越小检索越精准但上下文越碎片化,块越大上下文越完整但检索噪声越高,500 是一个兼顾中英文场景的常用取值;
  • chunk_overlap=150:相邻块之间的重叠字符数。重叠可以让被切断的语义(如一句话跨两个块)在相邻块中都有完整表达,避免检索时漏掉关键信息,一般取chunk_size的 20%~30%。

第四步:文本向量化

选用开源词向量模型 Sentence Transformer 对文本块进行向量化。LangChain 提供了直接引入 HuggingFace 开源社区模型进行向量化的接口HuggingFaceEmbeddings

from langchain.embeddings.huggingface import HuggingFaceEmbeddings embeddings = HuggingFaceEmbeddings(model_name="autodl-tmp/sentence-transformer")

model_name指向本地已下载的词向量模型目录。HuggingFaceEmbeddings在底层会调用sentence-transformers库加载该模型,并对输入的每个文本块输出一个固定维度的语义向量;在后续检索时,用户问题也会被编码为同一向量空间中的向量,通过向量相似度匹配最相关的知识片段。

第五步:写入 Chroma 向量数据库并持久化

选择 Chroma 作为向量数据库,基于分块后的文档与加载的开源向量化模型,将语料写入指定路径下的向量数据库:

from langchain.vectorstores import Chroma # 定义持久化路径 persist_directory = 'data_base/vector_db/chroma' # 加载数据库 vectordb = Chroma.from_documents( documents=split_docs, embedding=embeddings, persist_directory=persist_directory # 允许我们将persist_directory目录保存到磁盘上 ) # 将加载的向量数据库持久化到磁盘上 vectordb.persist()

Chroma.from_documents会把split_docs中每个文本块经embeddings向量化后写入本地数据库,persist_directory指定数据库落盘目录;vectordb.persist()将内存中的数据库显式写入磁盘。

完整脚本:一键构建知识库

将上述代码整合,即得到知识库搭建的完整脚本:

# 首先导入所需第三方库 from langchain.document_loaders import UnstructuredFileLoader from langchain.document_loaders import UnstructuredMarkdownLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain.vectorstores import Chroma from langchain.embeddings.huggingface import HuggingFaceEmbeddings from tqdm import tqdm import os # 获取文件路径函数 def get_files(dir_path): # args:dir_path,目标文件夹路径 file_list = [] for filepath, dirnames, filenames in os.walk(dir_path): # os.walk 函数将递归遍历指定文件夹 for filename in filenames: # 通过后缀名判断文件类型是否满足要求 if filename.endswith(".md"): # 如果满足要求,将其绝对路径加入到结果列表 file_list.append(os.path.join(filepath, filename)) elif filename.endswith(".txt"): file_list.append(os.path.join(filepath, filename)) return file_list # 加载文件函数 def get_text(dir_path): # args:dir_path,目标文件夹路径 # 首先调用上文定义的函数得到目标文件路径列表 file_lst = get_files(dir_path) # docs 存放加载之后的纯文本对象 docs = [] # 遍历所有目标文件 for one_file in tqdm(file_lst): file_type = one_file.split('.')[-1] if file_type == 'md': loader = UnstructuredMarkdownLoader(one_file) elif file_type == 'txt': loader = UnstructuredFileLoader(one_file) else: # 如果是不符合条件的文件,直接跳过 continue docs.extend(loader.load()) return docs # 目标文件夹 tar_dir = [ "/root/autodl-tmp/self-llm", "/root/autodl-tmp/llm-universe", "/root/autodl-tmp/prompt-engineering-for-developers", "/root/autodl-tmp/so-large-lm", "/root/autodl-tmp/hugging-llm", ] # 加载目标文件 docs = [] for dir_path in tar_dir: docs.extend(get_text(dir_path)) # 对文本进行分块 text_splitter = RecursiveCharacterTextSplitter( chunk_size=500, chunk_overlap=150) split_docs = text_splitter.split_documents(docs) # 加载开源词向量模型 embeddings = HuggingFaceEmbeddings(model_name="/root/autodl-tmp/sentence-transformer") # 构建向量数据库 # 定义持久化路径 persist_directory = 'data_base/vector_db/chroma' # 加载数据库 vectordb = Chroma.from_documents( documents=split_docs, embedding=embeddings, persist_directory=persist_directory # 允许我们将persist_directory目录保存到磁盘上 ) # 将加载的向量数据库持久化到磁盘上 vectordb.persist()

运行上述脚本(即执行仓库中的 create_db.py),即可在本地构建已持久化的向量数据库。后续直接导入该数据库即可使用,无需重复构建——这正是 RAG 相比每次全量检索的优势:文档处理只做一次,之后所有查询都基于索引好的向量库

ChatGLM3-6B 接入 LangChain:自定义 LLM 类

为便捷构建 LLM 应用,需要基于本地部署的 ChatGLM3-6B 自定义一个 LLM 类,将 ChatGLM 接入 LangChain 框架。完成自定义后,可以以完全一致的方式调用 LangChain 的接口,而无需考虑底层模型调用的不一致——这是 LangChain 的核心抽象思想:无论底层是闭源 API 还是本地开源模型,上层应用代码完全复用

仓库中 LLM.py 给出了完整实现。基于本地部署的 ChatGLM3-6B 自定义 LLM 类并不复杂,只需从langchain.llms.base.LLM类继承一个子类,并重写构造函数与_call函数:

from langchain.llms.base import LLM from typing import Any, List, Optional from langchain.callbacks.manager import CallbackManagerForLLMRun from transformers import AutoTokenizer, AutoModelForCausalLM import torch class ChatGLM_LLM(LLM): # 基于本地 ChatGLM3-6B 自定义 LLM 类 tokenizer : AutoTokenizer = None model: AutoModelForCausalLM = None def __init__(self, model_path :str): # model_path: ChatGLM3-6B 模型路径 # 从本地初始化模型 super().__init__() print("正在从本地加载模型...") self.tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True) self.model = AutoModelForCausalLM.from_pretrained(model_path, trust_remote_code=True).to(torch.bfloat16).cuda() self.model = self.model.eval() print("完成本地模型的加载") def _call(self, prompt : str, stop: Optional[List[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any): # 重写调用函数 response, history = self.model.chat(self.tokenizer, prompt , history=[]) return response @property def _llm_type(self) -> str: return "ChatGLM3-6B"

实现要点逐项拆解:

  • 构造函数:在对象实例化的一开始就加载本地部署的 ChatGLM3-6B 模型,避免每一次调用都需要重新加载模型带来的长时间等待。加载时使用trust_remote_code=True(ChatGLM3-6B 依赖仓库自带的远程代码完成模型结构定义),并以torch.bfloat16精度加载到 CUDA 上,显著降低显存占用,随后eval()切换到推理模式;
  • _call函数:这是 LLM 类的核心函数,LangChain 会调用该函数来调用 LLM。函数内部调用已实例化模型的chat(tokenizer, prompt, history=[])方法完成对话生成,并返回字符串形式的回答。stoprun_manager**kwargs是 LangChain 调用 LLM 时可能传入的标准参数,子类按接口签名保留即可;
  • _llm_type属性:返回模型类型标识,LangChain 内部用于区分不同的 LLM 实现。

值得说明的是,这种"继承 LLM 基类 + 重写_call"的自定义模式在整个仓库中是通用的。例如 InternLM 版知识库助手 中的InternLM_LLM类与该实现结构完全一致,仅模型路径和_llm_type返回值不同。这也印证了 LangChain 的设计哲学:LLM 接入层与上层应用逻辑解耦

在整体项目中,将上述代码封装为llm.py,后续直接从该文件中引入自定义的 LLM 类即可。

构建检索问答链:一键完成 RAG 全流程

LangChain 通过提供检索问答链对象来实现对 RAG 全流程的封装。即调用一个 LangChain 提供的RetrievalQA对象,在初始化时填入已构建的向量数据库和自定义 LLM 作为参数,即可简便地完成检索增强问答全流程——LangChain 会自动完成基于用户提问进行检索、获取相关文档、拼接为合适的 Prompt 并交给 LLM 问答的全部环节。

加载向量数据库

首先将上文构建的向量数据库导入进来,直接通过Chroma以及上文定义的词向量模型加载已构建的数据库:

from langchain.vectorstores import Chroma from langchain.embeddings.huggingface import HuggingFaceEmbeddings import os # 定义 Embeddings embeddings = HuggingFaceEmbeddings(model_name="/root/autodl-tmp/sentence-transformer") # 向量数据库持久化路径 persist_directory = 'data_base/vector_db/chroma' # 加载数据库 vectordb = Chroma( persist_directory=persist_directory, embedding_function=embeddings )

注意此处与构建时不同:构建用的是Chroma.from_documents(传入文档从零建库),加载则直接实例化Chroma(传入persist_directory读取磁盘上已有的库)。得到的vectordb对象可以针对用户的query进行语义向量检索,得到与用户提问相关的知识片段,其检索接口通过as_retriever()暴露给检索链。

实例化自定义 LLM

实例化基于 ChatGLM3-6B 自定义的 LLM 对象:

from LLM import ChatGLM_LLM llm = ChatGLM_LLM(model_path = "/root/autodl-tmp/ZhipuAI/chatglm3-6b") llm.predict("你是谁")

llm.predict是 LangChain LLM 基类提供的高层调用入口,它最终会调用我们重写的_call方法;通过这一行即可验证自定义 LLM 接入是否成功。

构造 Prompt Template

构建检索问答链还需要一个 Prompt Template。Template 本质上是一个带变量的字符串,在检索之后,LangChain 会将检索到的相关文档片段填入 Template 的变量中,从而构建带知识的 Prompt:

from langchain.prompts import PromptTemplate # 我们所构造的 Prompt 模板 template = """使用以下上下文来回答最后的问题。如果你不知道答案,就说你不知道,不要试图编造答案。尽量使答案简明扼要。总是在回答的最后说"谢谢你的提问!"。 {context} 问题: {question} 有用的回答:""" # 调用 LangChain 的方法来实例化一个 Template 对象,该对象包含了 context 和 question 两个变量,在实际调用时,这两个变量会被检索到的文档片段和用户提问填充 QA_CHAIN_PROMPT = PromptTemplate(input_variables=["context","question"],template=template)

模板设计有三个关键点:

  • {context}知识插槽:检索阶段命中的相关文档片段会被填入此处,这是 RAG 与纯 LLM 问答的根本区别;
  • {question}问题插槽:填充用户的原始提问;
  • 模板中的约束语句("不知道就说不知道"、"不要试图编造答案"、"简明扼要")用于抑制幻觉,引导模型在缺乏知识时诚实回答而非编造,这是知识库助手工程化的常见 Prompt 策略。

组装 RetrievalQA 检索问答链

最后调用 LangChain 提供的检索问答链构造函数,基于自定义 LLM、Prompt Template 和向量知识库构建检索问答链:

from langchain.chains import RetrievalQA qa_chain = RetrievalQA.from_chain_type(llm,retriever=vectordb.as_retriever(),return_source_documents=True,chain_type_kwargs={"prompt":QA_CHAIN_PROMPT})

参数说明:

  • llm:自定义的 ChatGLM3-6B LLM 对象,负责最终的答案生成;
  • retriever=vectordb.as_retriever():将向量数据库包装为检索器,负责按语义相似度检索相关文档片段;
  • return_source_documents=True:返回结果中附带命中的源文档,便于追溯答案依据;
  • chain_type_kwargs={"prompt": QA_CHAIN_PROMPT}:将自定义 Prompt Template 注入检索链,覆盖默认提示词。

得到的qa_chain对象即实现核心功能——基于 ChatGLM3-6B 的专业知识库助手。可以对比该检索问答链和纯 LLM 的问答效果:

# 检索问答链回答效果 question = "什么是 Self LLM" result = qa_chain({"query": question}) print("检索问答链回答 question 的结果:") print(result["result"]) # 仅 LLM 回答效果 result_2 = llm(question) print("大模型回答 question 的结果:") print(result_2)

这里需要说明调用方式:RetrievalQA链统一接收{"query": 问题}格式的输入字典(result["result"]为最终答案,return_source_documents=True时还可通过result["source_documents"]查看命中的知识片段);而自定义 LLM 对象llm(question)是纯模型推理,没有检索环节。两者对比即可直观验证 RAG 的效果差异:未检索的模型可能答非所问或编造答案,接入知识库后则能给出基于语料的准确回答。

部署 Web Demo:基于 Gradio 的小型对话应用

在完成核心功能后,可以基于 Gradio 框架将其部署到 Web 网页,搭建一个小型 Demo,便于测试与使用。仓库中完整实现见 run_gradio.py。

封装检索问答链加载函数

首先将上文代码封装为一个返回已构建检索问答链对象的函数,并在启动 Gradio 的第一时间调用该函数得到检索问答链对象,后续直接使用该对象进行问答对话,从而避免在每次提问时重复加载模型

from langchain.vectorstores import Chroma from langchain.embeddings.huggingface import HuggingFaceEmbeddings import os from LLM import ChatGLM_LLM from langchain.prompts import PromptTemplate from langchain.chains import RetrievalQA def load_chain(): # 加载问答链 # 定义 Embeddings embeddings = HuggingFaceEmbeddings(model_name="/root/autodl-tmp/sentence-transformer") # 向量数据库持久化路径 persist_directory = 'data_base/vector_db/chroma' # 加载数据库 vectordb = Chroma( persist_directory=persist_directory, # 允许我们将persist_directory目录保存到磁盘上 embedding_function=embeddings ) # 加载自定义 LLM llm = ChatGLM_LLM(model_path = "/root/autodl-tmp/ZhipuAI/chatglm3-6b") # 定义一个 Prompt Template template = """使用以下上下文来回答最后的问题。如果你不知道答案,就说你不知道,不要试图编造答 案。尽量使答案简明扼要。总是在回答的最后说"谢谢你的提问!"。 {context} 问题: {question} 有用的回答:""" QA_CHAIN_PROMPT = PromptTemplate(input_variables=["context","question"],template=template) # 运行 chain qa_chain = RetrievalQA.from_chain_type(llm,retriever=vectordb.as_retriever(),return_source_documents=True,chain_type_kwargs={"prompt":QA_CHAIN_PROMPT}) return qa_chain

load_chain一次性完成"加载向量库 → 加载 ChatGLM3-6B → 组装检索问答链"的全部初始化工作,是 Web 服务启动时的关键耗时环节。

封装对话响应类

定义一个类,负责加载并存储检索问答链,并响应 Web 界面里调用检索问答链进行回答的动作:

class Model_center(): """ 存储检索问答链的对象 """ def __init__(self): # 构造函数,加载检索问答链 self.chain = load_chain() def qa_chain_self_answer(self, question: str, chat_history: list = []): """ 调用问答链进行回答 """ if question == None or len(question) < 1: return "", chat_history try: chat_history.append( (question, self.chain({"query": question})["result"])) # 将问答结果直接附加到问答历史中,Gradio 会将其展示出来 return "", chat_history except Exception as e: return e, chat_history

类设计要点:

  • 构造函数中调用load_chain()完成模型与知识库的初始化,实例化一次、全程复用;
  • qa_chain_self_answer接收用户提问与对话历史,调用self.chain({"query": question})["result"]获取检索增强回答,并将(问题, 回答)二元组追加到chat_history——这正是 GradioChatbot组件渲染对话气泡所期望的数据结构;
  • 对空提问与异常做了兜底处理:空提问直接返回原历史,异常则把错误信息回显到输入框,避免界面崩溃。

构建 Gradio 界面

按照 Gradio 框架的使用方法,实例化 Web 界面并将点击动作绑定到上述类的回答方法:

import gradio as gr # 实例化核心功能对象 model_center = Model_center() # 创建一个 Web 界面 block = gr.Blocks() with block as demo: with gr.Row(equal_height=True): with gr.Column(scale=15): # 展示的页面标题 gr.Markdown("""<h1><center>Self LLM</center></h1> <center>Self LLM</center> """) with gr.Row(): with gr.Column(scale=4): # 创建一个聊天机器人对象 chatbot = gr.Chatbot(height=450, show_copy_button=True) # 创建一个文本框组件,用于输入 prompt。 msg = gr.Textbox(label="Prompt/问题") with gr.Row(): # 创建提交按钮。 db_wo_his_btn = gr.Button("Chat") with gr.Row(): # 创建一个清除按钮,用于清除聊天机器人组件的内容。 clear = gr.ClearButton( components=[chatbot], value="Clear console") # 设置按钮的点击事件。当点击时,调用上面定义的 qa_chain_self_answer 函数,并传入用户的消息和聊天历史记录,然后更新文本框和聊天机器人组件。 db_wo_his_btn.click(model_center.qa_chain_self_answer, inputs=[ msg, chatbot], outputs=[msg, chatbot]) gr.Markdown("""提醒:<br> 1. 初始化数据库时间可能较长,请耐心等待。 2. 使用中如果出现异常,将会在文本输入框进行展示,请不要惊慌。 <br> """) gr.close_all() # 直接启动 demo.launch()

界面组成与交互逻辑:

  • gr.Chatbot(height=450, show_copy_button=True):聊天记录展示区,支持一键复制回答;
  • gr.Textbox(label="Prompt/问题"):用户输入框;
  • gr.Button("Chat"):提交按钮,通过click事件绑定model_center.qa_chain_self_answer,以msg(输入文本)和chatbot(聊天历史)作为输入,输出同样更新这两个组件,形成"提问 → 检索增强回答 → 追加到对话历史"的闭环;
  • gr.ClearButton:一键清空对话记录;
  • 底部的 Markdown 提示区告知用户初始化耗时较长、异常会回显在输入框等注意事项。

启动与访问

将上述代码封装为run_gradio.py脚本,直接通过python命令运行:

python run_gradio.py

即可在本地启动知识库助手的 Web Demo,默认在7860 端口运行(Gradio 默认端口)。AutoDL 环境下,使用类似于端口映射的方式将服务器端口映射到本地端口即可在浏览器访问(详见 02-AutoDL开放端口),得到上文的 Web 对话界面。启动后即可针对"什么是 Self LLM"等语料库中的内容进行提问,模型会基于检索到的知识片段作答,而不是凭空发挥。

方案总结与运行注意事项

至此,一条完整的"ChatGLM3-6B + LangChain 本地知识库助手"流水线已经打通。整体架构与对应仓库文件如下:

环节核心技术仓库对应实现
文档解析UnstructuredMarkdownLoader/UnstructuredFileLoadercreate_db.py
文本分块RecursiveCharacterTextSplitter(chunk_size=500, overlap=150)create_db.py
向量化HuggingFaceEmbeddings+ sentence-transformers 多语言模型create_db.py
向量存储与检索Chroma 持久化向量数据库create_db.py
模型接入自定义ChatGLM_LLM(继承 LLM 基类)LLM.py
RAG 编排RetrievalQA+ 自定义 Prompt Templaterun_gradio.py
Web 交互Gradio Blocksrun_gradio.py

运行过程中需注意以下前提与限制:

  1. 显存要求:ChatGLM3-6B 以bfloat16精度加载到 CUDA,连同词向量模型与向量数据库,建议使用 24G 显存及以上的 GPU 机器(如 3090),与 ChatGLM3-6B 部署篇保持一致;
  2. 路径一致性:模型路径(/root/autodl-tmp/ZhipuAI/chatglm3-6b)、词向量模型路径(/root/autodl-tmp/sentence-transformer)与向量库持久化路径(data_base/vector_db/chroma)需与本地实际目录一致;
  3. 数据库复用:向量数据库构建一次后可长期复用,后续启动 Web Demo 时直接加载persist_directory即可,无需重新跑建库脚本;
  4. LangChain 版本锁定:本方案基于langchain==0.0.292的 API 风格(如langchain.llms.base.LLMlangchain.vectorstores.Chroma),新版本 LangChain 对模块路径有调整,升级版本时需相应适配导入语句。

这套方案的扩展价值在于:将语料目录tar_dir替换为任意领域文档集合,即可快速构建法律、医疗、金融等垂直领域的私有知识库问答助手;将自定义 LLM 类替换为仓库中其他模型(如 InternLM 版)的同构实现,上层 RAG 流水线无需任何改动——这正是 LangChain 抽象层带来的可移植性收益。

【免费下载链接】self-llm《开源大模型食用指南》针对中国宝宝量身打造的基于Linux环境快速微调(全参数/Lora)、部署国内外开源大模型(LLM)/多模态大模型(MLLM)教程项目地址: https://gitcode.com/GitHub_Trending/se/self-llm

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询