基于 InternLM 与 LangChain 搭建本地知识库助手:从向量库构建到 Gradio Web Demo 全流程实战
发布时间:2026/9/12 12:36:06来源:尧图网络
基于 InternLM 与 LangChain 搭建本地知识库助手从向量库构建到 Gradio Web Demo 全流程实战【免费下载链接】self-llm《开源大模型食用指南》针对中国宝宝量身打造的基于Linux环境快速微调全参数/Lora、部署国内外开源大模型LLM/多模态大模型MLLM教程项目地址: https://gitcode.com/GitHub_Trending/se/self-llm本文以开源大模型 InternLMInternLM-Chat-7B-V1.1为核心完整讲解如何将本地部署的 InternLM 接入 LangChain 框架实现文档加载 → 文本分块 → 向量化 → 向量数据库 → 检索问答链 → Gradio Web Demo的知识库助手全链路搭建。读完本文你将掌握 LangChain 自定义 LLM 类的封装方法、RAG 检索增强问答的核心实现以及如何用三份脚本复现一个可对话、可部署的本地知识库问答系统。本文配套代码位于仓库 models/InternLM/06-InternLM接入LangChain搭建知识库助手/其中 readme.md 对代码文件做了如下组织说明creat_db.py负责构建向量数据库LLM.py将 InternLM 封装为自定义 LLMrun_gradio.py负责启动 Gradio 服务。三个脚本分工明确正好对应本文的三条主线知识库搭建、LLM 接入、Web 部署。环境配置与依赖安装本教程建立在已完成 InternLM 本地部署的前提之上Transformers 方式加载模型模型参数存放于本地/root/autodl-tmp/model。若尚未部署可参考仓库中的 01-InternLM-Chat-7B Transformers 部署调用.md通过魔塔社区snapshot_download下载internlm-chat-7b权重并使用AutoTokenizer/AutoModelForCausalLM配合trust_remote_codeTrue完成加载与对话调用。在完成模型部署的基础上还需要安装以下依赖包pip install langchain0.0.292 pip install gradio4.4.0 pip install chromadb0.4.15 pip install sentence-transformers2.2.2 pip install unstructured0.10.30 pip install markdown3.3.7各依赖在链路中的职责如下依赖包版本在本项目中的职责langchain0.0.292提供文档加载器、文本分割器、Embeddings 接口、向量库封装与 RetrievalQA 链gradio4.4.0构建 Web 聊天界面Chatbot、Textbox、Button 组件chromadb0.4.15本地持久化向量数据库存储语料向量并支持相似度检索sentence-transformers2.2.2运行开源词向量模型完成文本到向量的编码unstructured0.10.30解析 Markdown / TXT 等非结构化文档markdown3.3.7UnstructuredMarkdownLoader 解析 .md 文件的底层依赖同时我们需要使用开源词向量模型 Sentence Transformerparaphrase-multilingual-MiniLM-L12-v2一个多语言句向量模型。可以仿照下载 InternLM 模型参数的方式将其下载到本地/root/autodl-tmp/embedding_model。这样后续所有向量化操作都在本地完成不依赖外部 API。第一步语料准备与知识库搭建知识库助手的核心价值在于让模型基于私有/指定语料回答。因此第一步是把选定的文档集切分成片段、向量化并持久化到向量数据库中。1.1 语料来源与获取本项目选用上海人工智能实验室开源的一系列大模型工具仓库作为语料库来源包括OpenCompass评测、lmdeploy部署、Xtuner微调、InternLM-XComposer多模态、Lagent智能体、InternLM大模型本体。将这些远程开源仓库 Clone 到本地以 AutoDL 数据盘/root/autodl-tmp为例# 进入到数据库盘 cd /root/autodl-tmp # 打开学术资源加速 source /etc/network_turbo # clone 上述开源仓库依次执行 git clone 对应仓库地址 git clone OpenCompass 仓库地址 git clone lmdeploy 仓库地址 git clone xtuner 仓库地址 git clone InternLM-XComposer 仓库地址 git clone lagent 仓库地址 git clone InternLM 仓库地址 # 关闭学术资源加速 unset http_proxy unset https_proxy为便于语料处理本文选用上述仓库中所有的 Markdown、TXT 文件作为示例语料。注意也可以将代码文件纳入知识库但需要针对代码文件格式额外做处理例如更换对应的文档加载器本文不展开。1.2 递归收集目标文件路径首先定义一个函数递归遍历指定文件夹返回其中所有后缀名为.md或.txt的文件绝对路径import os def get_files(dir_path): # argsdir_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这里使用os.walk自顶向下递归目录树endswith只保留.md与.txt两类文件。1.3 文档加载与解析拿到文件路径列表后借助 LangChain 的文档加载器Loader将磁盘文件解析为统一的纯文本对象。不同文件类型对应不同的 Loader需要根据后缀名分发from tqdm import tqdm from langchain.document_loaders import UnstructuredFileLoader from langchain.document_loaders import UnstructuredMarkdownLoader def get_text(dir_path): # argsdir_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 docsloader.load()返回的是一个Document对象列表每个Document包含page_content纯文本正文与metadata来源路径等元信息。1.4 文本分块Text Splitting直接对整篇文档做向量化既不经济也不利于精准检索因此需要先将长文档切成块。LangChain 提供了多种文本分割器此处使用RecursiveCharacterTextSplitter字符串递归分割器并选择分块大小为 500 字符、块与块之间重叠 150 字符from langchain.text_splitter import RecursiveCharacterTextSplitter text_splitter RecursiveCharacterTextSplitter( chunk_size500, chunk_overlap150) split_docs text_splitter.split_documents(docs)chunk_size500每个文本块的最大字符数。块越小检索越精准但会丢失上下文连贯性chunk_overlap150相邻块之间重叠的字符数用于保留跨块上下文的衔接避免关键信息恰好被切在边界处。两个参数可根据语料长度和检索效果动态调整是影响 RAG 效果的重要超参数。1.5 向量化与向量数据库构建文本块需要转换为向量才能被语义检索。LangChain 提供了直接引入 HuggingFace 开源社区模型的向量化接口from langchain.embeddings.huggingface import HuggingFaceEmbeddings embeddings HuggingFaceEmbeddings(model_name/root/autodl-tmp/embedding_model)向量数据库选择 Chroma本地持久化方案。基于分块后的文档与上述词向量模型将语料写入指定路径from langchain.vectorstores import Chroma # 定义持久化路径 persist_directory data_base/vector_db/chroma # 加载数据库 vectordb Chroma.from_documents( documentssplit_docs, embeddingembeddings, persist_directorypersist_directory # 允许我们将persist_directory目录保存到磁盘上 ) # 将加载的向量数据库持久化到磁盘上 vectordb.persist()1.6 完整脚本 creat_db.py将上述步骤整合即得到完整的知识库构建脚本与仓库中 creat_db.py 一致。需要说明的是仓库脚本中为快速验证流程对split_documents(docs[:10])仅切分前 10 个文档实际构建完整知识库时应去掉切片直接处理全部文档# 首先导入所需第三方库 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): # argsdir_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): # argsdir_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/InternLM, /root/autodl-tmp/InternLM-XComposer, /root/autodl-tmp/lagent, /root/autodl-tmp/lmdeploy, /root/autodl-tmp/opencompass, /root/autodl-tmp/xtuner ] # 加载目标文件 docs [] for dir_path in tar_dir: docs.extend(get_text(dir_path)) # 对文本进行分块 text_splitter RecursiveCharacterTextSplitter( chunk_size500, chunk_overlap150) split_docs text_splitter.split_documents(docs) # 加载开源词向量模型 embeddings HuggingFaceEmbeddings(model_name/root/autodl-tmp/embedding_model) # 构建向量数据库 # 定义持久化路径 persist_directory data_base/vector_db/chroma # 加载数据库 vectordb Chroma.from_documents( documentssplit_docs, embeddingembeddings, persist_directorypersist_directory # 允许我们将persist_directory目录保存到磁盘上 ) # 将加载的向量数据库持久化到磁盘上 vectordb.persist()运行该脚本后本地即生成已持久化的 Chroma 向量数据库位于data_base/vector_db/chroma。后续使用时直接导入该数据库即可无需重复构建。这也是把向量库构建独立成creat_db.py脚本的价值所在——一次构建、多次复用。第二步将 InternLM 封装为自定义 LLM 接入 LangChainLangChain 的应用构建依赖统一的LLM抽象接口。InternLM 并未原生提供 LangChain 封装因此我们需要基于本地部署的 InternLM 自定义一个 LLM 类将其接入 LangChain 框架。完成自定义之后就可以用完全一致的方式调用 LangChain 的接口而不必考虑底层模型调用的差异。自定义过程并不复杂从langchain.llms.base.LLM继承一个子类重写构造函数与_call函数即可对应仓库中的 LLM.pyfrom 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 InternLM_LLM(LLM): # 基于本地 InternLM 自定义 LLM 类 tokenizer : AutoTokenizer None model: AutoModelForCausalLM None def __init__(self, model_path :str): # model_path: InternLM 模型路径 # 从本地初始化模型 super().__init__() print(正在从本地加载模型...) self.tokenizer AutoTokenizer.from_pretrained(model_path, trust_remote_codeTrue) self.model AutoModelForCausalLM.from_pretrained(model_path, trust_remote_codeTrue).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 InternLM2.1 构造函数模型一次性加载在__init__中通过AutoTokenizer.from_pretrained与AutoModelForCausalLM.from_pretrained加载本地模型并以torch.bfloat16精度搬移到 CUDA最后调用eval()切换到推理模式。这里的trust_remote_codeTrue表示信任并加载模型自带的远程自定义代码InternLM 的modeling_internlm.py等这与仓库中 01-InternLM-Chat-7B Transformers 部署调用.md 的模型加载方式完全一致。将模型加载放在构造函数中的关键意义在于整个应用生命周期内模型只加载一次后续每次提问直接复用避免了每次调用都重新加载 14GB 权重带来的巨大时间开销。2.2_call函数LLM 类的核心调用入口_call是 LangChainLLM基类要求子类实现的核心方法LangChain 会通过它来完成对模型的真正调用。在该函数中我们调用已实例化模型的chat方法InternLM 自带的对话生成接口传入 prompt 与空历史history[]取返回的response作为结果返回。此外_llm_type属性返回自定义类型名InternLM用于标识该 LLM 实例的类型。在整体项目中这段代码封装为LLM.py后续通过from LLM import InternLM_LLM直接引入自定义的 LLM 类。第三步构建检索问答链 RetrievalQA知识库与 LLM 就绪后LangChain 通过RetrievalQA检索问答链对象封装 RAG检索增强生成全流程给定用户提问 → 语义检索相关文档 → 拼装为 Prompt → 交给 LLM 生成答案。RetrievalQA自动完成上述全部环节我们只需在初始化时填入已构建的向量数据库和自定义 LLM。3.1 加载已构建的向量数据库通过 Chroma 与上文定义的词向量模型直接加载持久化数据库from langchain.vectorstores import Chroma from langchain.embeddings.huggingface import HuggingFaceEmbeddings import os # 定义 Embeddings embeddings HuggingFaceEmbeddings(model_name/root/autodl-tmp/embedding_model) # 向量数据库持久化路径 persist_directory data_base/vector_db/chroma # 加载数据库 vectordb Chroma( persist_directorypersist_directory, embedding_functionembeddings )这里与构建阶段不同的是传入persist_directory直接读取磁盘上的向量库embedding_function需要与建库时使用同一个词向量模型否则查询向量与库内向量分布不一致检索会失真。vectordb对象可针对用户的query进行语义向量检索返回与提问相关的知识片段as_retriever()会将其包装为 LangChain 标准的 Retriever 接口。3.2 实例化自定义 LLM接着实例化基于 InternLM 自定义的 LLM 对象from LLM import InternLM_LLM llm InternLM_LLM(model_path /root/autodl-tmp/model) llm.predict(你是谁)predict是 LangChain LLM 基类提供的方法内部会调用我们重写的_call。这行代码可以验证 InternLM 已成功接入 LangChain 接口体系。3.3 构造 Prompt Template检索到相关文档后需要把它们与用户提问拼装成一个带知识的 Prompt。Prompt Template 本质上是一个带变量的字符串模板LangChain 会在检索后把文档片段与问题分别填入变量from langchain.prompts import PromptTemplate # 我们所构造的 Prompt 模板 template 使用以下上下文来回答最后的问题。如果你不知道答案就说你不知道不要试图编造答案。尽量使答案简明扼要。总是在回答的最后说“谢谢你的提问”。 {context} 问题: {question} 有用的回答: # 调用 LangChain 的方法来实例化一个 Template 对象该对象包含了 context 和 question 两个变量在实际调用时这两个变量会被检索到的文档片段和用户提问填充 QA_CHAIN_PROMPT PromptTemplate(input_variables[context,question],templatetemplate)该模板同时约束了模型的回答风格与事实边界要求模型基于{context}检索到的知识片段作答、不知道时明确承认而不是编造并在回答末尾附加固定礼貌用语。这些约束对知识库助手的可靠性至关重要。3.4 组装 RetrievalQA 检索问答链最后调用 LangChain 的检索问答链构造函数基于自定义 LLM、Prompt Template 和向量知识库构建完整的检索问答链from langchain.chains import RetrievalQA qa_chain RetrievalQA.from_chain_type( llm, retrievervectordb.as_retriever(), return_source_documentsTrue, chain_type_kwargs{prompt: QA_CHAIN_PROMPT} )关键参数说明llm传入自定义的InternLM_LLM实例retriever向量库的检索器负责根据 query 召回 Top-K 相关文档片段return_source_documentsTrue在结果中同时返回命中的源文档便于核查答案出处chain_type_kwargs{prompt: QA_CHAIN_PROMPT}指定链内使用的 Prompt 模板覆盖默认模板。得到的qa_chain对象即实现了核心功能——基于 InternLM 的专业知识库助手。3.5 效果对比检索问答链 vs 纯 LLM可以对比检索问答链与纯 LLM 的问答效果直观感受知识库的价值# 检索问答链回答效果 question 什么是InternLM result qa_chain({query: question}) print(检索问答链回答 question 的结果) print(result[result]) # 仅 LLM 回答效果 result_2 llm(question) print(大模型回答 question 的结果) print(result_2)注意调用方式的差异qa_chain({query: question})使用链的标准输入键query通过result[result]取答案纯 LLM 则直接llm(question)。检索问答链的回答会以语料库中的真实文档为依据而纯 LLM 只能依赖训练时学到的知识——这正是 RAG 能够消除幻觉、补充私有知识的原因。第四步基于 Gradio 部署 Web Demo核心功能验证通过后使用 Gradio 将其部署为 Web 页面搭建一个小型 Demo 便于测试与使用对应仓库中的 run_gradio.py。4.1 封装问答链加载函数先将上文的组装逻辑封装为一个返回检索问答链对象的函数load_chain并在 Gradio 启动的第一时间调用后续直接复用该对象进行问答避免重复加载模型from langchain.vectorstores import Chroma from langchain.embeddings.huggingface import HuggingFaceEmbeddings import os from LLM import InternLM_LLM from langchain.prompts import PromptTemplate from langchain.chains import RetrievalQA def load_chain(): # 加载问答链 # 定义 Embeddings embeddings HuggingFaceEmbeddings(model_name/root/autodl-tmp/embedding_model) # 向量数据库持久化路径 persist_directory data_base/vector_db/chroma # 加载数据库 vectordb Chroma( persist_directorypersist_directory, # 允许我们将persist_directory目录保存到磁盘上 embedding_functionembeddings ) # 加载自定义 LLM llm InternLM_LLM(model_path /root/autodl-tmp/model) # 定义一个 Prompt Template template 使用以下上下文来回答最后的问题。如果你不知道答案就说你不知道不要试图编造答 案。尽量使答案简明扼要。总是在回答的最后说“谢谢你的提问”。 {context} 问题: {question} 有用的回答: QA_CHAIN_PROMPT PromptTemplate(input_variables[context,question],templatetemplate) # 运行 chain qa_chain RetrievalQA.from_chain_type( llm, retrievervectordb.as_retriever(), return_source_documentsTrue, chain_type_kwargs{prompt: QA_CHAIN_PROMPT} ) return qa_chain4.2 Model_center 问答中心类定义一个类负责加载并存储检索问答链同时响应 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 def clear_history(self): self.chain.clear_history()qa_chain_self_answer接收用户问题与聊天历史调用问答链生成答案并追加到历史中返回给 Gradio 渲染对空输入直接返回对异常则把错误信息反馈到输入框clear_history清空后端存储的聊天记录。4.3 Gradio 界面搭建按 Gradio 的框架用法实例化 Web 界面并将按钮点击动作绑定到上述类的回答方法import gradio as gr # 实例化核心功能对象 model_center Model_center() # 创建一个 Web 界面 block gr.Blocks() with block as demo: with gr.Row(equal_heightTrue): with gr.Column(scale15): # 展示的页面标题 gr.Markdown(h1centerInternLM/center/h1 center书生蒲语/center ) with gr.Row(): with gr.Column(scale4): # 创建一个聊天机器人对象 chatbot gr.Chatbot(height450, show_copy_buttonTrue) # 创建一个文本框组件用于输入 prompt。 msg gr.Textbox(labelPrompt/问题) with gr.Row(): # 创建提交按钮。 db_wo_his_btn gr.Button(Chat) with gr.Row(): # 创建一个清除按钮用于清除聊天机器人组件的内容。 clear gr.ClearButton( components[chatbot], valueClear console) # 设置按钮的点击事件。当点击时调用上面定义的 qa_chain_self_answer 函数并传入用户的消息和聊天历史记录然后更新文本框和聊天机器人组件。 db_wo_his_btn.click(model_center.qa_chain_self_answer, inputs[ msg, chatbot], outputs[msg, chatbot]) # 点击后清空后端存储的聊天记录 clear.click(model_center.clear_history) gr.Markdown(提醒br 1. 初始化数据库时间可能较长请耐心等待。 2. 使用中如果出现异常将会在文本输入框进行展示请不要惊慌。 br ) gr.close_all() # 直接启动 demo.launch()界面元素说明gr.Chatbot渲染对话气泡并支持复制gr.Textbox承接用户输入gr.Button(Chat)的click事件把文本框与聊天记录作为输入交给qa_chain_self_answer输出更新回文本框与聊天组件ClearButton一键清屏。底部 Markdown 则给出两条使用提醒。4.4 启动与访问将上述代码整体保存为run_gradio.py与仓库中的脚本一致直接运行python run_gradio.py启动成功后默认会在 7860 端口运行demo.launch()默认行为。在 AutoDL 等云服务器上可使用类似于模型部署的方式将服务器端口映射到本地端口即可通过浏览器访问。交互效果如上文截图所示输入什么是InternLM系统会基于本地语料给出回答。若需指定端口或以分享链接方式启动可参考仓库脚本中被注释的备选写法demo.launch(shareTrue, server_portint(os.environ[PORT1]))。常见问题与排查思路依赖版本不匹配本教程的导入路径如langchain.llms.base.LLM、langchain.embeddings.huggingface与 LangChain 0.0.292 版本对应新版 LangChain 对模块路径有较大调整建议按教程锁定版本安装首次加载耗时启动时会依次加载词向量模型与 14GB 左右的 InternLM 权重初始化数据库/模型时间较长属正常现象请耐心等待检索效果不佳优先调整chunk_size与chunk_overlap或确认查询时使用的embedding_function与建库时一致显存不足InternLM_LLM.__init__中以torch.bfloat16精度加载模型若仍显存不足可考虑量化部署方式运行异常Demo 会将异常信息展示在文本输入框中可据此定位问题无需惊慌。总结本文基于仓库 models/InternLM/06-InternLM接入LangChain搭建知识库助手/ 的完整代码走通了语料收集 → 文档解析 → 文本分块 → 向量化 → Chroma 持久化 → 自定义 LLM 接入 → RetrievalQA 检索问答 → Gradio Web Demo的完整链路。三份脚本分工清晰、层层递进creat_db.py知识库构建产出可复用的持久化向量数据库LLM.pyInternLM 自定义 LLM 封装打通 LangChain 生态run_gradio.py检索问答链 Gradio 界面交付可交互的知识库助手。理解这条链路后你可以将语料替换为任意领域的私有文档产品文档、论文、公司知识库等即可快速复刻出一个基于 InternLM 的专属知识库问答系统。【免费下载链接】self-llm《开源大模型食用指南》针对中国宝宝量身打造的基于Linux环境快速微调全参数/Lora、部署国内外开源大模型LLM/多模态大模型MLLM教程项目地址: https://gitcode.com/GitHub_Trending/se/self-llm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网