Qwen1.5-7B-Chat 接入 LangChain 搭建知识库助手实战指南
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 modelscope1.16.1 pip install langchain0.2.3 pip install transformers4.43.2 pip install accelerate0.32.1各依赖在本文中的职责如下依赖包版本作用modelscope1.16.1从 ModelScope 模型库下载 Qwen1.5-7B-Chat 权重langchain0.2.3提供 LLM 基类、向量库、检索问答链等框架能力transformers4.43.2加载与推理 Qwen1.5 因果语言模型及分词器accelerate0.32.1支持device_mapauto自动设备分配加载大模型提示上述版本号来自仓库教程的锁定版本若你的环境中 langchain 已升级到 0.3.xlangchain.llms.base的导入路径可能发生变化如langchain_community.llms建议优先按本文锁定版本安装保证代码可复现。另外考虑到部分同学配置环境可能会遇到一些问题仓库作者在 AutoDL 平台准备了 Qwen1.5 的环境镜像该镜像适用于该仓库除 Qwen-GPTQ 和 vLLM 外的所有部署环境可通过创建 AutoDL 示例直接使用。1.2 安装知识库相关的附加依赖仓库中配套的完整知识库示例models/Qwen/07-Qwen-7B-Chat 接入langchain搭建知识库助手还涉及向量数据库、文档加载、词向量模型与 Web 界面如需跑通完整链路可参考其依赖组合安装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其中sentence-transformers用于加载开源词向量模型例如paraphrase-multilingual-MiniLM-L12-v2chromadb作为本地持久化的向量数据库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, revisionmaster)然后在终端中执行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_fastFalse) self.model AutoModelForCausalLM.from_pretrained(mode_name_or_path, torch_dtypetorch.bfloat16, device_mapauto) 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, tokenizeFalse, add_generation_promptTrue) model_inputs self.tokenizer([input_ids], return_tensorspt).to(cuda) generated_ids self.model.generate(model_inputs.input_ids,max_new_tokens512) 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_tokensTrue)[0] return response property def _llm_type(self) - str: return Qwen1_5_LLM3.1 构造函数加载本地模型在上述类定义中我们分别重写了构造函数和_call函数构造函数在对象实例化的一开始加载本地部署的 Qwen1.5 模型从而避免每一次调用都需要重新加载模型带来的时间过长。其中AutoTokenizer.from_pretrained(mode_name_or_path, use_fastFalse)加载分词器use_fastFalse使用非 Rust 加速版本的慢速分词器确保与 Qwen1.5 的对话模板完全兼容AutoModelForCausalLM.from_pretrained(..., torch_dtypetorch.bfloat16, device_mapauto)以 bfloat16 半精度加载因果语言模型device_mapauto由 accelerate 自动将各层分配到可用设备GPU/CPU显存不足时部分层会自动落到 CPUGenerationConfig.from_pretrained(mode_name_or_path)读取模型自带的生成配置如max_length、top_p、temperature等保证生成行为与官方默认一致。_call函数这是 LLM 类的核心函数LangChain 会调用该函数来调用 LLM。其内部逻辑为将用户提问包装成[{role: user, content: prompt}]对话结构使用tokenizer.apply_chat_template(messages, tokenizeFalse, add_generation_promptTrue)按 Qwen1.5 的 ChatML 对话模板生成完整的模型输入文本包含 system/user 标记与生成起始符将文本 tokenize 后搬到 CUDA调用model.generate(..., max_new_tokens512)生成回答通过zip(model_inputs.input_ids, generated_ids)剔除输入前缀仅保留新生成的 tokenbatch_decode(..., skip_special_tokensTrue)解码为纯文本返回。_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_maskmodel_inputs.attention_mask。四、构建向量知识库接入 LangChain 只是第一步本教程的目标是搭建知识库助手。知识库的核心链路是加载文档 → 文本分块 → 向量化 → 存入向量数据库。仓库中的 Qwen 系列完整示例提供了可直接复用的构建脚本 creat_db.py下面结合该脚本讲解每一步。4.1 递归收集文档文件使用os.walk递归遍历目标文件夹收集所有后缀为.md或.txt的文件路径import os def get_files(dir_path): # argsdir_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 LoaderMarkdown 使用UnstructuredMarkdownLoaderTXT 使用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_size500, chunk_overlap150) split_docs text_splitter.split_documents(docs)接着加载开源词向量模型进行向量化。LangChain 提供了直接引入 HuggingFace 开源社区模型的接口HuggingFaceEmbeddingsfrom 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( documentssplit_docs, embeddingembeddings, persist_directorypersist_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))运行该脚本后即可在本地构建已持久化的向量数据库后续直接导入该数据库即可无需重复构建。五、构建检索问答链RetrievalQALangChain 通过提供检索问答链对象来实现对 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_directorypersist_directory, embedding_functionembeddings )得到的vectordb对象可以针对用户的 query 进行语义向量检索得到与用户提问相关的知识片段。5.2 定义 Prompt 模板构建检索问答链还需要一个 Prompt Template。该模板基于带变量的字符串在检索之后LangChain 会将检索到的相关文档片段填入模板的变量中从而构建带知识的 Promptfrom langchain.prompts import PromptTemplate template 使用以下上下文来回答最后的问题。如果你不知道答案就说你不知道不要试图编造答案。尽量使答案简明扼要。总是在回答的最后说谢谢你的提问。 {context} 问题: {question} 有用的回答: QA_CHAIN_PROMPT PromptTemplate(input_variables[context,question],templatetemplate)注意模板中明确要求如果你不知道答案就说你不知道不要试图编造答案这是 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, retrievervectordb.as_retriever(), return_source_documentsTrue, 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_directorypersist_directory, embedding_functionembeddings ) 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], templatetemplate) # 运行 chain 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}) 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_heightTrue): with gr.Column(scale15): gr.Markdown(h1centerQwen1.5 知识库助手/center/h1 center基于 LangChain 检索增强生成/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 ) # threads to consume the request gr.close_all() # 启动新的 Gradio 应用设置分享功能为 True并使用环境变量 PORT1 指定服务器端口。 # demo.launch(shareTrue, server_portint(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),仅供参考