基于 LangChain 和 Hugging Face 的 RAG 问答系统实现
数据下载
通过网盘分享的文件:Guide.zip
链接: https://pan.baidu.com/s/15aNMvgSYUBU3WYVlbAS-Ag?pwd=e2nm 提取码: e2nm
--来自百度网盘超级会员v1的分享
一、任务介绍
本项目实现了一个完整的 RAG (Retrieval-Augmented Generation,检索增强生成) 问答系统。该系统能够从本地文档中提取知识,构建向量数据库,并基于检索到的相关文档片段生成准确的答案。
核心功能
- 文档加载与处理:从指定目录加载 Markdown 文档,并进行智能文本拆分
- 向量化存储:将文档转换为向量表示,使用 FAISS 向量数据库进行高效存储和检索
- 智能检索:根据用户问题从向量数据库中检索最相关的文档片段
- 答案生成:结合检索到的上下文,使用大语言模型生成准确、可靠的答案
二、任务背景
2.1 为什么需要 RAG?
传统的大语言模型虽然能够生成流畅的文本,但存在以下局限性:
- 知识更新滞后:模型训练时的知识是静态的,无法获取最新信息
- 领域知识不足:通用模型对特定领域(如企业内部文档、技术手册)的理解有限
- 幻觉问题:模型可能生成看似合理但实际错误的信息
- 上下文限制:模型无法直接访问外部知识库
2.2 RAG 的优势
RAG 通过结合**检索(Retrieval)和生成(Generation)**两个阶段,有效解决了上述问题:
- ✅ 实时知识更新:可以随时更新文档库,无需重新训练模型
- ✅ 领域定制化:基于特定领域的文档构建知识库
- ✅ 可追溯性:答案来源于具体文档,可验证和追溯
- ✅ 成本效益:无需微调大模型,即可获得领域专业知识
2.3 技术选型
本项目采用以下技术栈:
- LangChain:提供文档处理、向量存储、链式调用等高级抽象
- Hugging Face:提供嵌入模型和生成模型
- FAISS:Facebook 开源的向量相似度搜索库,支持高效检索
- Transformers:加载和使用预训练的大语言模型
三、应用场景
3.1 企业内部知识库问答
- 场景:企业拥有大量内部文档(技术文档、产品手册、政策文件等)
- 应用:员工可以通过自然语言提问,快速获取准确信息
- 优势:减少信息查找时间,提高工作效率
3.2 技术文档智能助手
- 场景:软件开发团队需要快速查找 API 文档、配置说明等
- 应用:开发者可以直接提问,系统从文档中检索并生成答案
- 优势:降低学习成本,提高开发效率
3.3 客户服务支持
- 场景:客服团队需要快速回答客户常见问题
- 应用:基于产品文档、FAQ 等构建知识库,提供 24/7 自动问答
- 优势:减少人工客服压力,提高响应速度
3.4 学术研究辅助
- 场景:研究人员需要从大量论文中查找相关信息
- 应用:将论文库向量化,支持语义检索和问答
- 优势:快速定位相关研究,提高文献调研效率
四、代码结构详解
4.1 整体架构流程
文档加载 → 文本拆分 → 向量化 → 向量数据库 → 检索器 → 问答链 → 答案生成
4.2 代码模块划分
模块一:环境配置与路径设置(第 1-18 行)
python
# 配置 Hugging Face 镜像和超时设置
os.environ['HF_ENDPOINT'] = 'https://hf-mirror.com'
os.environ['HF_HUB_DOWNLOAD_TIMEOUT'] = '300'
# 定义文档路径
DOC_PATH = os.path.join(script_dir, "Guide")
功能说明:
- 配置 Hugging Face 镜像站点,解决国内访问问题
- 设置下载超时时间,避免网络问题导致失败
- 动态获取脚本目录,设置文档路径
模块二:文档加载与预处理(第 20-34 行)
python
# 文档加载
loader = DirectoryLoader(DOC_PATH, glob="10*.md", loader_cls=TextLoader)
documents = loader.load()
# 文本拆分
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=100
)
docs = text_splitter.split_documents(documents)
功能说明:
- DirectoryLoader:批量加载指定目录下的 Markdown 文件
- RecursiveCharacterTextSplitter :递归字符文本拆分器
chunk_size=500:每个文档块的最大字符数chunk_overlap=100:相邻块之间的重叠字符数(保持上下文连贯性)
设计考虑:
- 文档拆分是 RAG 的关键步骤,合理的块大小和重叠度影响检索质量
- 重叠设计确保重要信息不会因拆分而丢失
模块三:向量化与向量数据库构建(第 36-50 行)
python
# 创建嵌入模型
embedding_model = HuggingFaceEmbeddings(
model_name="chuxin-llm/Chuxin-Embedding",
model_kwargs={'device': 'cpu'},
encode_kwargs={'normalize_embeddings': True}
)
# 构建向量数据库
vectorstore = FAISS.from_documents(docs, embedding_model)
功能说明:
- HuggingFaceEmbeddings :将文本转换为高维向量(embedding)
- 使用中文优化的嵌入模型
Chuxin-Embedding normalize_embeddings=True:归一化向量,提高相似度计算准确性
- 使用中文优化的嵌入模型
- FAISS :Facebook AI Similarity Search
- 高效的向量相似度搜索库
- 支持大规模向量数据库的快速检索
技术细节:
- 向量化将文本转换为数值表示,使语义相似性可计算
- FAISS 使用近似最近邻算法,在精度和速度之间取得平衡
模块四:检索器创建(第 59-60 行)
python
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
功能说明:
- 将向量数据库转换为检索器对象
k=3:检索最相关的 3 个文档片段- 检索器使用余弦相似度或点积计算文档与问题的相似度
模块五:大语言模型加载(第 62-99 行)
python
# 加载分词器和模型
tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(
model_path,
torch_dtype="auto",
device_map="auto",
trust_remote_code=True
)
# 创建文本生成管道
generator = pipeline(
"text-generation",
model=model,
tokenizer=tokenizer,
max_length=4096,
pad_token_id=tokenizer.eos_token_id
)
功能说明:
- AutoTokenizer:自动加载适合模型的分词器
- AutoModelForCausalLM :加载因果语言模型(用于文本生成)
torch_dtype="auto":自动选择数据类型(float16/float32)device_map="auto":自动选择设备(CPU/GPU)
- pipeline :封装模型和分词器,提供统一的文本生成接口
max_length=4096:限制生成文本的最大长度
模型选择:
- 使用
Mistral-7B-Instruct-v0.3-GPTQ-4bit:量化模型,降低显存需求 - 支持本地和远程模型加载
模块六:问答链构建(第 122-142 行)
python
# 包装为 LangChain LLM 接口
llm = HuggingFacePipeline(pipeline=generator)
# 自定义提示模板
custom_prompt = PromptTemplate(
template="""Use the following pieces of context to answer the question...
{context}
Question: {question}
Answer:"""
)
# 构建问答链
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=retriever,
# 可以选择传入自定义提示模板(传入的话记得取消注释),
#如果不需要可以删除这个参数
hain_type_kwargs={"prompt": custom_prompt}
)
功能说明:
- HuggingFacePipeline :将 Hugging Face 的 pipeline 包装为 LangChain 的 LLM 接口
- 实现接口适配,使 Hugging Face 模型能在 LangChain 框架中使用
- PromptTemplate :定义提示模板
{context}:检索到的文档片段{question}:用户问题
- RetrievalQA :检索增强生成链
chain_type="stuff":将所有检索到的文档直接堆叠作为上下文- 自动完成:检索 → 构建提示 → 生成答案的完整流程
工作流程:
- 用户提问 → 2. 检索器从向量数据库获取相关文档 → 3. 将文档和问题组合成提示 → 4. LLM 生成答案
模块七:问答执行(第 149-154 行)
python
query = "Top-K 和 Top-P 的区别是什么?"
answer = qa_chain.run(query)
print(answer)
功能说明:
- 执行完整的 RAG 流程
- 系统自动检索相关文档并生成答案
4.3 关键技术点总结
| 模块 | 关键技术 | 作用 |
|---|---|---|
| 文档处理 | RecursiveCharacterTextSplitter | 智能文本拆分,保持上下文 |
| 向量化 | HuggingFaceEmbeddings | 文本转向量,语义表示 |
| 向量存储 | FAISS | 高效向量检索 |
| 模型加载 | Transformers Pipeline | 简化模型使用 |
| 链式调用 | LangChain RetrievalQA | 自动化 RAG 流程 |
4.4 系统优势
- 模块化设计:各模块职责清晰,易于维护和扩展
- 框架集成:充分利用 LangChain 和 Hugging Face 的生态优势
- 灵活配置:支持本地/远程模型、CPU/GPU 运行
- 中文优化:使用中文嵌入模型,适配中文场景
4.5 可扩展方向
- 多模态支持:扩展支持图片、表格等非文本内容
- 多轮对话:添加对话历史管理,支持上下文理解
- 答案评估:添加答案质量评估和置信度评分
- 增量更新:支持向量数据库的增量更新,无需重建
- 多检索策略:结合关键词检索和语义检索,提高召回率
完整代码
python
import os
from langchain.document_loaders import DirectoryLoader, TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.prompts import PromptTemplate
from langchain_community.vectorstores import FAISS # 修复过时的导入
from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
from langchain.chains import RetrievalQA
from langchain_huggingface import HuggingFaceEmbeddings, HuggingFacePipeline
# 配置 Hugging Face 镜像站点和超时设置(解决连接超时问题)
# 如果在中国大陆,可以使用镜像站点加速下载
os.environ['HF_ENDPOINT'] = 'https://hf-mirror.com' # 使用 Hugging Face 镜像
# 增加超时时间(秒)
os.environ['HF_HUB_DOWNLOAD_TIMEOUT'] = '300' # 设置下载超时为 300 秒
# 定义文件所在的路径(基于脚本文件所在目录)
script_dir = os.path.dirname(os.path.abspath(__file__))
DOC_PATH = os.path.join(script_dir, "Guide")
# 使用 DirectoryLoader 从指定路径加载文件。"*.md" 表示加载所有 .md 格式的文件,这里仅导入文章 10(避免文章 20 的演示内容对结果的影响)
# 使用 TextLoader 来加载 Markdown 文件,避免需要 unstructured[md] 依赖
loader = DirectoryLoader(DOC_PATH, glob="10*.md", loader_cls=TextLoader, loader_kwargs={"encoding": "utf-8"})
# 加载目录中的指定的 .md 文件并将其转换为文档对象列表
documents = loader.load()
# 文本处理
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=500, # 尝试调整它
chunk_overlap=100, # 尝试调整它
#length_function=len, # 可以省略
#separators=["\n\n", "\n", " ", "。", ""] # 可以省略
)
docs = text_splitter.split_documents(documents)
# 生成嵌入(使用 Hugging Face 模型)
# 指定要加载的预训练模型的名称,参考排行榜:https://huggingface.co/spaces/mteb/leaderboard
model_name = "chuxin-llm/Chuxin-Embedding"
# 创建 Hugging Face 的嵌入模型实例,这个模型将用于将文本转换为向量表示(embedding)
# 增加超时时间和重试配置,解决连接超时问题
embedding_model = HuggingFaceEmbeddings(
model_name=model_name,
model_kwargs={'device': 'cpu'}, # 或 'cuda' 如果有 GPU
encode_kwargs={'normalize_embeddings': True},
# 如果使用镜像站点,可以通过环境变量设置,或者手动指定
)
# 建立向量数据库
vectorstore = FAISS.from_documents(docs, embedding_model)
# 保存向量数据库(可选)
#vectorstore.save_local("faiss_index")
# 加载向量数据库(可选)
# 注意参数 allow_dangerous_deserialization,确保你完全信任需要加载的数据库(当然,自己生成的不需要考虑这一点)
#vectorstore = FAISS.load_local("faiss_index", embedding_model, allow_dangerous_deserialization=True)
# 创建检索器
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
# 加载文本生成模型
# 本地
# model_path = './Mistral-7B-Instruct-v0.3-GPTQ-4bit'
# 远程
model_path = 'neuralmagic/Mistral-7B-Instruct-v0.3-GPTQ-4bit'
# 加载
# 加载模型和分词器
# AutoTokenizer.from_pretrained():加载分词器
# 将文本转换为模型可处理的 token ID
# 将模型输出转换回文本
# 增加超时配置,解决连接超时问题
tokenizer = AutoTokenizer.from_pretrained(
model_path,
trust_remote_code=True
)
# AutoModelForCausalLM.from_pretrained():加载生成式语言模型
# 用于根据输入生成文本
model = AutoModelForCausalLM.from_pretrained(
model_path,
torch_dtype="auto", # 自动选择模型的权重数据类型
device_map="auto", # 自动选择可用的设备(CPU/GPU)
trust_remote_code=True
)
# 创建文本生成管道
# 将模型和分词器封装为 pipeline,简化调用
# "text-generation":指定为文本生成任务
# max_length=4096:限制生成文本的最大长度
# pad_token_id=tokenizer.eos_token_id:使用结束符作为填充符
generator = pipeline(
"text-generation", # 指定任务类型为文本生成
model=model,
tokenizer=tokenizer,
max_length=4096, # 指定生成文本的最大长度
pad_token_id=tokenizer.eos_token_id
)
# 1. 文档检索 → 2. 生成答案
# ↓ ↓
# 向量数据库 语言模型(这部分代码)
# 1====向量数据库检索相关文档片段(前面代码已完成)
# 2====语言模型根据检索到的上下文生成答案(这两段代码负责)
# 3====例如,当用户问 "Top-K 和 Top-P 的区别是什么?" 时:
# 4====检索器从向量数据库找到相关文档
# 5====语言模型读取这些文档,生成答案
# 6====这两段代码是 RAG 系统的"生成器"部分,负责根据检索到的文档生成最终答案。
# Hugging Face Pipeline (generator)
# ↓
# HuggingFacePipeline 包装器 (llm)
# ↓
# LangChain RetrievalQA (qa_chain)
# 包装为 LangChain 的 LLM 接口
# 将 Hugging Face 的 pipeline 包装为 LangChain 的 LLM 接口,使其能在 LangChain 的链中使用。
llm = HuggingFacePipeline(pipeline=generator)
custom_prompt = PromptTemplate(
template="""Use the following pieces of context to answer the question at the end. If you don't know the answer, just say that you don't know, don't try to make up an answer.
{context}
Question: {question}
Answer:""",
input_variables=["context", "question"]
)
# 构建问答链
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff", # 直接堆叠所有检索到的文档
retriever=retriever, # 使用先前定义的检索器来获取相关文档
# chain_type_kwargs={"prompt": custom_prompt} # 可以选择传入自定义提示模板(传入的话记得取消注释),如果不需要可以删除这个参数
)
# generator:Hugging Face 的"原生接口"
# llm:LangChain 的"标准接口"
# HuggingFacePipeline:转换器/适配器
# 这样,LangChain 的 RetrievalQA 就能使用 Hugging Face 的模型进行文本生成。
# 提出问题
query = "Top-K 和 Top-P 的区别是什么?"
# 获取答案
answer = qa_chain.run(query)
print(answer)
总结
本 RAG 系统实现了一个完整的文档问答解决方案,从文档处理到答案生成,每个环节都经过精心设计。通过结合向量检索和大语言模型,实现了既准确又高效的智能问答能力。该系统可以轻松适配不同的应用场景,是企业级知识管理和智能助手的基础架构。