Qwen1.5-7B-Chat 接入 LangChain 搭建知识库助手实战指南
【免费下载链接】self-llm《开源大模型食用指南》针对中国宝宝量身打造的基于Linux环境快速微调(全参数/Lora)、部署国内外开源大模型(LLM)/多模态大模型(MLLM)教程项目地址: https://gitcode.com/GitHub_Trending/se/self-llm
导读
本文以开源大模型实战仓库 self-llm 中《Qwen1.5-7B-Chat 接入 LangChain 搭建知识库助手》教程为核心,完整讲解如何基于本地部署的 Qwen1.5-7B-Chat 自定义 LLM 类并接入 LangChain 框架,进而搭建基于 RAG(检索增强生成)的知识库问答助手。读者学完本文后,将掌握:LangChain 自定义 LLM 类的原理与写法、Qwen1.5 对话模板的调用方式、向量知识库的构建流程、检索问答链(RetrievalQA)的搭建,以及基于 Gradio 的 Web Demo 部署方案。文中全部配置、代码与命令均来自仓库实际内容,可直接在 Linux + CUDA 环境中复现。
一、环境准备
本文基础环境如下(与仓库教程一致):
---------------- ubuntu 22.04 python 3.12 cuda 12.1 pytorch 2.3.0 ----------------本文默认学习者已安装好以上 PyTorch(CUDA) 环境,如未安装请自行安装。
1.1 pip 换源与依赖安装
使用清华 PyPI 源加速下载并安装依赖包:
# 升级pip python -m pip install --upgrade pip # 更换 pypi 源加速库的安装 pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple pip install modelscope==1.16.1 pip install langchain==0.2.3 pip install transformers==4.43.2 pip install accelerate==0.32.1各依赖在本文中的职责如下:
| 依赖包 | 版本 | 作用 |
|---|---|---|
| modelscope | 1.16.1 | 从 ModelScope 模型库下载 Qwen1.5-7B-Chat 权重 |
| langchain | 0.2.3 | 提供 LLM 基类、向量库、检索问答链等框架能力 |
| transformers | 4.43.2 | 加载与推理 Qwen1.5 因果语言模型及分词器 |
| accelerate | 0.32.1 | 支持device_map="auto"自动设备分配加载大模型 |
提示:上述版本号来自仓库教程的锁定版本,若你的环境中 langchain 已升级到 0.3.x,
langchain.llms.base的导入路径可能发生变化(如langchain_community.llms),建议优先按本文锁定版本安装,保证代码可复现。
另外,考虑到部分同学配置环境可能会遇到一些问题,仓库作者在 AutoDL 平台准备了 Qwen1.5 的环境镜像,该镜像适用于该仓库除 Qwen-GPTQ 和 vLLM 外的所有部署环境,可通过创建 AutoDL 示例直接使用。
1.2 安装知识库相关的附加依赖
仓库中配套的完整知识库示例(models/Qwen/07-Qwen-7B-Chat 接入langchain搭建知识库助手)还涉及向量数据库、文档加载、词向量模型与 Web 界面,如需跑通完整链路,可参考其依赖组合安装:
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其中sentence-transformers用于加载开源词向量模型(例如paraphrase-multilingual-MiniLM-L12-v2),chromadb作为本地持久化的向量数据库,unstructured负责解析 Markdown / TXT 文档,gradio用于启动 Web Demo。
二、模型下载
使用modelscope中的snapshot_download函数下载模型,第一个参数为模型名称,参数cache_dir为自定义的模型下载路径,参数revision为模型仓库分支版本,master代表主分支,也是一般模型上传的默认分支。
先切换到autodl-tmp目录:cd /root/autodl-tmp,然后新建名为model_download.py的 python 文件,并在其中输入以下内容并保存:
# model_download.py from modelscope import snapshot_download model_dir = snapshot_download('qwen/Qwen1.5-7B-Chat', cache_dir='/root/autodl-tmp', revision='master')然后在终端中执行python model_download.py下载模型。注意该模型权重文件比较大(约 15GB 量级),需要耐心等待一段时间直到模型下载完成。
注意:记得修改
cache_dir为你的模型下载路径哦~
下载完成后,模型会被保存在/root/autodl-tmp/qwen/Qwen1.5-7B-Chat目录下,后续自定义 LLM 类时直接以该路径作为model_name_or_path传入即可。
三、自定义 LLM 类:将 Qwen1.5 接入 LangChain
为便捷构建 LLM 应用,我们需要基于本地部署的 Qwen1.5-LM,自定义一个 LLM 类,将 Qwen1.5 接入到 LangChain 框架中。完成自定义 LLM 类之后,可以以完全一致的方式调用 LangChain 的接口,而无需考虑底层模型调用的不一致。
基于本地部署的 Qwen1.5 自定义 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, GenerationConfig, LlamaTokenizerFast import torch class Qwen1_5_LLM(LLM): # 基于本地 Qwen1.5 自定义 LLM 类 tokenizer: AutoTokenizer = None model: AutoModelForCausalLM = None def __init__(self, mode_name_or_path :str): super().__init__() print("正在从本地加载模型...") self.tokenizer = AutoTokenizer.from_pretrained(mode_name_or_path, use_fast=False) self.model = AutoModelForCausalLM.from_pretrained(mode_name_or_path, torch_dtype=torch.bfloat16, device_map="auto") self.model.generation_config = GenerationConfig.from_pretrained(mode_name_or_path) print("完成本地模型的加载") def _call(self, prompt : str, stop: Optional[List[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any): messages = [{"role": "user", "content": prompt }] input_ids = self.tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True) model_inputs = self.tokenizer([input_ids], return_tensors="pt").to('cuda') generated_ids = self.model.generate(model_inputs.input_ids,max_new_tokens=512) generated_ids = [ output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids) ] response = self.tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0] return response @property def _llm_type(self) -> str: return "Qwen1_5_LLM"3.1 构造函数:加载本地模型
在上述类定义中,我们分别重写了构造函数和_call函数:
构造函数:在对象实例化的一开始加载本地部署的 Qwen1.5 模型,从而避免每一次调用都需要重新加载模型带来的时间过长。其中:
AutoTokenizer.from_pretrained(mode_name_or_path, use_fast=False)加载分词器,use_fast=False使用非 Rust 加速版本的慢速分词器,确保与 Qwen1.5 的对话模板完全兼容;AutoModelForCausalLM.from_pretrained(..., torch_dtype=torch.bfloat16, device_map="auto")以 bfloat16 半精度加载因果语言模型,device_map="auto"由 accelerate 自动将各层分配到可用设备(GPU/CPU),显存不足时部分层会自动落到 CPU;GenerationConfig.from_pretrained(mode_name_or_path)读取模型自带的生成配置(如max_length、top_p、temperature等),保证生成行为与官方默认一致。
_call函数:这是 LLM 类的核心函数,LangChain 会调用该函数来调用 LLM。其内部逻辑为:- 将用户提问包装成
[{"role": "user", "content": prompt}]对话结构; - 使用
tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)按 Qwen1.5 的 ChatML 对话模板生成完整的模型输入文本(包含 system/user 标记与生成起始符); - 将文本 tokenize 后搬到 CUDA,调用
model.generate(..., max_new_tokens=512)生成回答; - 通过
zip(model_inputs.input_ids, generated_ids)剔除输入前缀,仅保留新生成的 token,batch_decode(..., skip_special_tokens=True)解码为纯文本返回。
- 将用户提问包装成
_llm_type属性:返回"Qwen1_5_LLM",用于标识该自定义 LLM 的类型。
从源码结构看,这种"继承
LLM基类 + 重写_call"的方式正是 LangChain 官方支持的自定义 LLM 标准模式:_call负责单次推理,_llm_type用于类型标识,其余缓存、回调、流式等能力由基类统一管理。
在整体项目中,我们将上述代码封装为LLM.py,后续将直接从该文件中引入自定义的 LLM 类(仓库中 Qwen 系列的完整实现见 models/Qwen/07-Qwen-7B-Chat 接入langchain搭建知识库助手/LLM.py,其思路一致:在构造函数中加载 tokenizer 与模型,在_call中调用模型的chat方法并返回响应文本)。
3.2 调用验证
然后就可以像使用任何其他的 langchain 大模型功能一样使用了:
from LLM import Qwen1_5_LLM llm = Qwen1_5_LLM(mode_name_or_path = "/root/autodl-tmp/qwen/Qwen1.5-7B-Chat") llm("你是谁")如上图所示,模型能够正常返回通义千问的自我介绍。终端中出现的The attention mask and the pad token id were not set...是 transformers 的常规提示,表示未显式传入attention_mask时框架会自行推断,一般不影响生成结果;同时框架会为 open-end generation 自动设置pad_token_id(对 Qwen1.5 为151645)。如需消除该提示,可在generate时显式传入attention_mask=model_inputs.attention_mask。
四、构建向量知识库
接入 LangChain 只是第一步,本教程的目标是"搭建知识库助手"。知识库的核心链路是:加载文档 → 文本分块 → 向量化 → 存入向量数据库。仓库中的 Qwen 系列完整示例提供了可直接复用的构建脚本 creat_db.py,下面结合该脚本讲解每一步。
4.1 递归收集文档文件
使用os.walk递归遍历目标文件夹,收集所有后缀为.md或.txt的文件路径:
import os def get_files(dir_path): # args:dir_path,目标文件夹路径 file_list = [] for filepath, dirnames, filenames in os.walk(dir_path): 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_list4.2 按文件类型加载为纯文本
LangChain 针对不同文件类型提供对应的 Document Loader:Markdown 使用UnstructuredMarkdownLoader,TXT 使用UnstructuredFileLoader,调用load()后得到由纯文本对象(Document)组成的列表:
from tqdm import tqdm from langchain.document_loaders import UnstructuredFileLoader from langchain.document_loaders import UnstructuredMarkdownLoader def get_text(dir_path): file_lst = get_files(dir_path) 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 docs4.3 文本分块与向量化
文档过长时直接向量化会超出模型上下文限制且检索精度下降,因此需要先分块。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)接着加载开源词向量模型进行向量化。LangChain 提供了直接引入 HuggingFace 开源社区模型的接口HuggingFaceEmbeddings:
from langchain.embeddings.huggingface import HuggingFaceEmbeddings embeddings = HuggingFaceEmbeddings(model_name="/root/autodl-tmp/embedding_model")词向量模型(例如paraphrase-multilingual-MiniLM-L12-v2)可提前下载到本地路径,推荐使用 hf-mirror 镜像加速:
import os os.environ['HF_ENDPOINT'] = 'https://hf-mirror.com' os.system('huggingface-cli download --resume-download sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2 --local-dir /root/autodl-tmp/embedding_model')4.4 构建并持久化 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()将上述所有步骤整合为一个脚本(完整实现见 creat_db.py),并把目标语料目录填入tar_dir列表:
tar_dir = [ "/root/autodl-tmp/qwen", "/root/autodl-tmp/Qwen", ] docs = [] for dir_path in tar_dir: docs.extend(get_text(dir_path))运行该脚本后,即可在本地构建已持久化的向量数据库,后续直接导入该数据库即可,无需重复构建。
五、构建检索问答链(RetrievalQA)
LangChain 通过提供检索问答链对象来实现对 RAG 全流程的封装。我们可以调用一个 LangChain 提供的RetrievalQA对象,通过初始化时填入已构建的数据库和自定义 LLM 作为参数,简便地完成"检索 → 组装 Prompt → 交给 LLM 问答"的全流程。
5.1 加载已构建的向量数据库
通过Chroma与上文定义的词向量模型直接加载持久化数据库:
from langchain.vectorstores import Chroma from langchain.embeddings.huggingface import HuggingFaceEmbeddings # 定义 Embeddings embeddings = HuggingFaceEmbeddings(model_name="/root/autodl-tmp/embedding_model") # 向量数据库持久化路径 persist_directory = 'data_base/vector_db/chroma' # 加载数据库 vectordb = Chroma( persist_directory=persist_directory, embedding_function=embeddings )得到的vectordb对象可以针对用户的 query 进行语义向量检索,得到与用户提问相关的知识片段。
5.2 定义 Prompt 模板
构建检索问答链还需要一个 Prompt Template。该模板基于带变量的字符串,在检索之后,LangChain 会将检索到的相关文档片段填入模板的变量中,从而构建带知识的 Prompt:
from langchain.prompts import PromptTemplate template = """使用以下上下文来回答最后的问题。如果你不知道答案,就说你不知道,不要试图编造答案。尽量使答案简明扼要。总是在回答的最后说"谢谢你的提问!"。 {context} 问题: {question} 有用的回答:""" QA_CHAIN_PROMPT = PromptTemplate(input_variables=["context","question"],template=template)注意:模板中明确要求"如果你不知道答案,就说你不知道,不要试图编造答案",这是 RAG 应用中抑制幻觉(Hallucination)的常见做法——只基于检索到的上下文作答,超出知识库范围时如实承认。
5.3 实例化自定义 LLM 与检索问答链
from LLM import Qwen1_5_LLM llm = Qwen1_5_LLM(mode_name_or_path="/root/autodl-tmp/qwen/Qwen1.5-7B-Chat")然后调用 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 | 传入的自定义 LLM 对象(即 Qwen1_5_LLM 实例) |
retriever | 检索器,这里为向量数据库的as_retriever(),负责语义检索相关文档 |
return_source_documents | 为 True 时返回检索到的源文档,便于溯源与调试 |
chain_type_kwargs | 透传给问答链的参数,这里注入自定义的prompt模板 |
5.4 对比检索问答链与纯 LLM 效果
得到的qa_chain对象即可实现核心功能——基于 Qwen1.5 模型的专业知识库助手。我们可以对比该检索问答链和纯 LLM 的问答效果:
question = "什么是QwenLM" result = qa_chain({"query": question}) print("检索问答链回答 question 的结果:") print(result["result"]) # 仅 LLM 回答效果 result_2 = llm(question) print("大模型回答 question 的结果:") print(result_2)可以看到,使用检索问答链生成的答案更接近知识库里的内容——这正是 RAG 的价值:让大模型基于私有/领域文档作答,而不是只依赖预训练知识。
六、部署 Gradio Web Demo
在完成核心功能后,可以基于 Gradio 框架将其部署到 Web 网页,搭建一个小型 Demo,便于测试与使用。仓库中的完整脚本见 run_gradio.py。
6.1 封装检索问答链的加载函数
将上文代码封装为一个返回已构建检索问答链对象的函数,并在启动 Gradio 的第一时间调用,从而避免重复加载模型:
# 导入必要的库 import gradio as gr from langchain.vectorstores import Chroma from langchain.embeddings.huggingface import HuggingFaceEmbeddings import os from LLM import Qwen1_5_LLM from langchain.prompts import PromptTemplate def load_chain(): # 加载问答链 embeddings = HuggingFaceEmbeddings(model_name="/root/autodl-tmp/embedding_model") persist_directory = 'data_base/vector_db/chroma' vectordb = Chroma( persist_directory=persist_directory, embedding_function=embeddings ) llm = Qwen1_5_LLM(mode_name_or_path="/root/autodl-tmp/qwen/Qwen1.5-7B-Chat") template = """使用以下上下文来回答最后的问题。如果你不知道答案,就说你不知道,不要试图编造答案。尽量使答案简明扼要。总是在回答的最后说"谢谢你的提问!"。 {context} 问题: {question} 有用的回答:""" QA_CHAIN_PROMPT = PromptTemplate(input_variables=["context","question"], template=template) # 运行 chain 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}) return qa_chain6.2 定义模型中心类
定义一个类,负责加载并存储检索问答链,并响应 Web 界面里调用检索问答链进行回答的动作:
class Model_center(): """ 存储问答 Chain 的对象 """ 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"])) return "", chat_history except Exception as e: return e, chat_history def clear_history(self): self.chain.clear_history()6.3 组装 Gradio 界面
按照 Gradio 的框架使用方法,实例化一个 Web 界面并将点击动作绑定到上述类的回答方法:
model_center = Model_center() block = gr.Blocks() with block as demo: with gr.Row(equal_height=True): with gr.Column(scale=15): gr.Markdown("""<h1><center>Qwen1.5 知识库助手</center></h1> <center>基于 LangChain 检索增强生成</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]) # 点击后清空后端存储的聊天记录 clear.click(model_center.clear_history) gr.Markdown("""提醒:<br> 1. 初始化数据库时间可能较长,请耐心等待。 2. 使用中如果出现异常,将会在文本输入框进行展示,请不要惊慌。 <br> """) # threads to consume the request gr.close_all() # 启动新的 Gradio 应用,设置分享功能为 True,并使用环境变量 PORT1 指定服务器端口。 # demo.launch(share=True, server_port=int(os.environ['PORT1'])) # 直接启动 demo.launch()6.4 启动服务
将上述代码封装为run_gradio.py脚本,直接在终端运行:
python run_gradio.py即可在本地启动知识库助手的 Web Demo,默认会在 7860 端口运行。若在远程服务器(如 AutoDL)上运行,需要像部署其他服务一样将服务器端口映射到本地端口后访问。
七、总结
本文围绕"Qwen1.5-7B-Chat 接入 LangChain 搭建知识库助手"这一主题,完整走通了从环境准备、模型下载,到自定义 LLM 类、构建向量知识库、搭建检索问答链、部署 Web Demo 的全链路:
- 接入层:继承
langchain.llms.base.LLM重写构造函数与_call,将本地 Qwen1.5-7B-Chat 封装为 LangChain 标准 LLM,上层调用与模型实现彻底解耦; - 知识层:通过
UnstructuredMarkdownLoader/UnstructuredFileLoader加载文档,RecursiveCharacterTextSplitter分块,HuggingFaceEmbeddings向量化,写入并持久化 Chroma 向量数据库; - 应用层:用
RetrievalQA一键串联"检索 + 提示词组装 + 生成",并通过 Gradio 提供可视化对话界面。
仓库中 Qwen 系列的完整可运行代码(LLM.py、creat_db.py、run_gradio.py)与本教程的 Qwen1.5 版本思路完全一致,差异仅在于 Qwen1.5 使用apply_chat_template构造对话输入、以model.generate直接生成,而 Qwen(一代)使用model.chat接口——理解这一点后,即可轻松将该方案迁移到 Qwen 系列其他模型(如 Qwen2、Qwen2.5、Qwen3)乃至其他对话模型。
【免费下载链接】self-llm《开源大模型食用指南》针对中国宝宝量身打造的基于Linux环境快速微调(全参数/Lora)、部署国内外开源大模型(LLM)/多模态大模型(MLLM)教程项目地址: https://gitcode.com/GitHub_Trending/se/self-llm
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考