RAG(三)文档的加载 基于 LangChain+FAISS 构建本地文档向量检索系统

一、场景背景与技术价值

在日常工作中,我们常常需要从大量本地文档(如 TXT、MD、PDF)中快速找到与问题相关的内容,传统的 "关键词搜索" 容易遗漏语义相关的信息,而基于向量检索的方式能实现语义级别的精准匹配

本文将实战搭建一个轻量级本地文档向量检索系统,核心解决两个问题:

  1. 如何批量加载本地目录中的文档(以 TXT 为例);
  2. 如何将文档转为向量并实现带相似度评分的语义检索。

核心技术栈:

  • LangChain 文档加载器DirectoryLoader/TextLoader实现本地文档的便捷加载;
  • BCE 嵌入模型:本地部署的中文嵌入模型,将文本转为语义向量;
  • FAISS:Facebook 开源的向量检索库,支持高效的相似度匹配并返回评分;
  • HuggingFaceEmbeddings:LangChain 封装的嵌入模型调用接口,适配本地模型。

二、核心原理与流程设计

向量检索的核心逻辑是 "文本→向量→相似度计算",本次实战的完整流程为:

  1. 文档加载:用 LangChain 加载本地单个 / 多个 TXT 文档;
  2. 文本向量化:通过本地 BCE 模型将文档内容转为归一化的语义向量;
  3. 向量存储:用 FAISS 构建本地向量库,存储文档向量;
  4. 语义检索:输入查询语句,转为向量后与库中向量计算相似度,返回匹配结果 + 评分。

三、完整实战步骤与代码解析

3.1 环境准备

首先安装所需依赖,执行以下命令:

复制代码
# LangChain核心依赖(含文档加载、向量库组件)
pip install langchain langchain-community langchain-huggingface
# 向量检索库
pip install faiss-cpu
# 文档加载依赖(Unstructured)
pip install unstructured
# 进度条依赖(可选,提升体验)
pip install tqdm
# HuggingFace相关依赖(加载本地嵌入模型)
pip install transformers torch sentence-transformers

同时准备本地资源:

  • BCE 嵌入模型 :下载maidalun/bce-embedding-base_v1到本地(如D:\本地模型\maidalun\bce-embedding-base_v1);
  • 测试文档 :在项目目录下创建txt文件夹,放入a.txta1.txta2.txt三个测试文档(例如包含 "公鸡爱吃小米和玉米""母鸡下蛋需要安静环境""鸭子喜欢在水里觅食" 等内容)。

3.2 完整代码实现

复制代码
# 1. 导入核心库
from langchain_community.document_loaders import DirectoryLoader, TextLoader
from langchain_community.vectorstores import FAISS
from langchain_huggingface import HuggingFaceEmbeddings

# --------------------------
# 步骤1:加载本地文档(两种方式)
# --------------------------
# 方式1:加载单个TXT文档(指定编码避免乱码)
loader = TextLoader(".\\txt\\a.txt", encoding="utf8")
doc = loader.load()

loader1 = TextLoader(".\\txt\\a1.txt", encoding="utf8")
doc1 = loader1.load()

loader2 = TextLoader(".\\txt\\a2.txt", encoding="utf8")
doc2 = loader2.load()

# 方式2:批量加载目录下所有TXT文档(推荐,简化代码)
# loader = DirectoryLoader(
#     ".\\txt",  # 文档目录
#     glob="**/*.txt",  # 匹配所有TXT文件
#     encoding="utf8",  # 编码格式
#     show_progress=True  # 显示加载进度条(需安装tqdm)
# )
# docs = loader.load()
# print(f"批量加载文档数量:{len(docs)}")

# --------------------------
# 步骤2:初始化本地BCE嵌入模型
# --------------------------
# 本地模型路径(替换为你的实际路径)
model_name = r'D:\本地模型\maidalun\bce-embedding-base_v1'

# 嵌入模型配置:向量归一化(提升相似度计算准确性)
encode_kwargs = {'normalize_embeddings': True}

embeddings = HuggingFaceEmbeddings(
    model_name=model_name,
    encode_kwargs=encode_kwargs,
    model_kwargs={'device': 'cpu'}  # 指定运行设备(CPU/GPU)
)

# --------------------------
# 步骤3:构建FAISS向量库
# --------------------------
# 将加载的文档转为向量并存储到FAISS
# 方式1:单个文档组合(对应步骤1的方式1)
vectors = FAISS.from_documents([doc[0], doc1[0], doc2[0]], embeddings)

# 方式2:批量文档加载(对应步骤1的方式2)
# vectors = FAISS.from_documents(docs, embeddings)

# --------------------------
# 步骤4:语义检索(带相似度评分)
# --------------------------
# 普通相似度检索(仅返回文档)
# result = vectors.similarity_search("公鸡爱吃什么")
# print("普通检索结果:", result)

# 带评分的相似度检索(推荐,可判断匹配程度)
result_with_score = vectors.similarity_search_with_score("公鸡爱吃什么")

# 格式化输出检索结果
print("\n" + "="*50 + " 带相似度得分输出 " + "="*50)
for idx, (doc, score) in enumerate(result_with_score, 1):
    print(f"\n【匹配结果 {idx}】")
    print(f"相似度得分(越低越相似):{score:.4f}") 
    print(f"文本内容:{doc.page_content}")

3.3 核心代码解析

1. 文档加载的两种方式
方式 1:单个文档加载(TextLoader)
复制代码
loader = TextLoader(".\\txt\\a.txt", encoding="utf8")
doc = loader.load()
  • TextLoader:专门用于加载纯文本文件(TXT),需指定encoding="utf8"避免中文乱码;
  • load()方法返回一个Document对象列表,每个对象包含page_content(文本内容)和metadata(元数据,如文件路径)。
方式 2:批量目录加载(DirectoryLoader)
复制代码
loader = DirectoryLoader(".\\txt", glob="**/*.txt", show_progress=True)
docs = loader.load()
  • glob="**/*.txt":递归匹配目录下所有 TXT 文件(**表示子目录),也可改为**/*.md匹配 Markdown 文件;
  • show_progress=True:加载大量文档时显示进度条,提升体验(需安装tqdm);
  • 优势:无需逐个加载文件,适合海量本地文档场景。
2. 本地嵌入模型配置
复制代码
embeddings = HuggingFaceEmbeddings(
    model_name=model_name,
    encode_kwargs={'normalize_embeddings': True},
    model_kwargs={'device': 'cpu'}
)
  • normalize_embeddings=True:对生成的向量做归一化处理,使向量长度为 1,此时余弦相似度等价于点积,计算更高效且结果更稳定;
  • model_kwargs={'device': 'cpu'}:指定模型运行在 CPU 上(若无 GPU),有 GPU 可改为'cuda'提升速度;
  • 核心价值:无需调用第三方 API,完全离线完成文本向量化,数据更安全。
3. FAISS 向量检索核心方法
复制代码
result = vectors.similarity_search("公鸡爱吃什么")
  • 返回与查询语句语义最相似的文档列表(默认返回 4 个);
  • 优点:简单易用;缺点:无相似度评分,无法判断匹配程度。
(2)带评分检索:similarity_search_with_score
复制代码
result_with_score = vectors.similarity_search_with_score("公鸡爱吃什么")
  • 返回元组列表(文档对象, 相似度得分)
  • 得分说明:FAISS 默认使用 L2 距离(欧式距离),得分越低表示语义越相似(得分 0 表示完全匹配);
  • 核心价值:可根据得分筛选结果(如只保留得分 < 1.0 的文档),提升检索精准度。

四、运行结果与关键说明

4.1 预期运行结果

假设测试文档内容:

  • a.txt:公鸡爱吃小米、玉米和虫子,每天需要充足的谷物投喂;
  • a1.txt:母鸡下蛋期间需要补充钙,喜欢在安静的鸡舍觅食;
  • a2.txt:鸭子喜欢在水里找小鱼吃,和公鸡的食性差异很大。

运行代码后输出:

复制代码
================================================== 带相似度得分输出 ==================================================

【匹配结果 1】
相似度得分(越低越相似):0.1250
文本内容:公鸡爱吃小米、玉米和虫子,每天需要充足的谷物投喂;

【匹配结果 2】
相似度得分(越低越相似):0.8925
文本内容:母鸡下蛋期间需要补充钙,喜欢在安静的鸡舍觅食;

【匹配结果 3】
相似度得分(越低越相似):1.5680
文本内容:鸭子喜欢在水里找小鱼吃,和公鸡的食性差异很大。

4.2 关键说明

  1. 得分解读:L2 距离得分 <0.5 表示高度相似,0.5~1.0 表示中度相似,>1.0 表示低相似;
  2. 乱码问题 :加载文档时必须指定encoding="utf8"(或对应文档的编码,如gbk),否则中文会出现乱码;
  3. 模型路径model_name必须指向本地 BCE 模型的根目录(包含config.jsonpytorch_model.bin等文件)。

五、扩展方向

  1. 支持更多文档格式 :替换TextLoaderPyPDFLoader(PDF)、DocxLoader(Word)、MarkdownLoader(MD),实现多格式文档加载;
  2. 文本分割优化 :结合RecursiveCharacterTextSplitter对长文档分割,避免单篇文档过长导致嵌入效果下降;
  3. 检索参数调优 :通过similarity_search_with_score("查询语句", k=2)指定返回结果数量(k 值);
  4. 结合大模型生成:将检索结果作为上下文,调用本地 / 云端大模型生成回答,升级为完整的 RAG 系统;
  5. 向量库持久化 :通过vectors.save_local("faiss_index")保存向量库,下次使用时通过FAISS.load_local加载,无需重复向量化。

六、核心总结

  1. LangChain 提供了TextLoader/DirectoryLoader等开箱即用的文档加载工具,能快速处理本地单 / 多文档加载,解决了传统爬虫 / 文件读取的繁琐问题;
  2. 本地 BCE 嵌入模型 + FAISS 的组合,实现了完全离线的语义检索,数据不落地第三方,兼顾效率与安全性;
  3. similarity_search_with_score是 FAISS 的核心实用方法,通过相似度得分可精准筛选匹配结果,是构建高质量 RAG 系统的基础。

该系统是本地知识库的核心基础,可直接适配企业内部文档检索、个人知识库管理等场景,只需替换文档目录和检索语句,即可快速落地使用。

可以运行的代码如下:

复制代码
from langchain_community.document_loaders import DirectoryLoader
from langchain_community.vectorstores import FAISS
#loader = DirectoryLoader("../", glob="**/*.txt")
#默认情况下不会显示进度条。要显示进度条,请安装 tqdm 库(例如 pip install tqdm),并将 show_progress 参数设置为 True。
#loader = DirectoryLoader("../", glob="**/*.md", show_progress=True)
#docs = loader.load()
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_community.document_loaders import TextLoader
loader = TextLoader(".\\txt\\a.txt", encoding="utf8")
doc = loader.load()

loader1 = TextLoader(".\\txt\\a1.txt", encoding="utf8")
doc1 = loader1.load()

loader2 = TextLoader(".\\txt\\a2.txt", encoding="utf8")
doc2 = loader2.load()
model_name = r'D:\本地模型\maidalun\bce-embedding-base_v1'
# D:\大模型\RAG_Project\maidalun\bce-embedding-base_v1
# 生成的嵌入向量将被归一化,有助于向量比较
encode_kwargs = {'normalize_embeddings': True}

embeddings = HuggingFaceEmbeddings(
    model_name=model_name
)

vectors = FAISS.from_documents([doc[0], doc1[0],doc2[0]], embeddings)
#result = vectors.similarity_search("公鸡爱吃什么")
#print(result)

result_with_score = vectors.similarity_search_with_score("公鸡爱吃什么")
print("\n" + "="*50 + " 带相似度得分输出 " + "="*50)
for idx, (doc, score) in enumerate(result_with_score, 1):
    print(f"\n【匹配结果 {idx}】")
    print(f"相似度得分(越低越相似):{score:.4f}")
    print(f"文本内容:{doc.page_content}")
相关推荐
Chasing Aurora7 小时前
Python后端开发之旅(三)
开发语言·python·langchain·protobuf
sensen_kiss8 小时前
INT303 Big Data Analysis 大数据分析 Pt.11 模型选择和词向量(Word Embeddings)
大数据·数据挖掘·数据分析
laocooon5238578868 小时前
数据收集, 数据清洗,数据分析,然后可视化,都涉及哪些知识
数据挖掘·数据分析
AI Echoes10 小时前
LangChain 非分割类型的文档转换器使用技巧
人工智能·python·langchain·prompt·agent
民乐团扒谱机11 小时前
【微实验】数模美赛备赛MATLAB实战:一文速通各种“马尔可夫”(Markov Model)
开发语言·人工智能·笔记·matlab·数据挖掘·马尔科夫链·线性系统
kimi-22212 小时前
KV Cache(键值缓存)技术
langchain·transformer
_爱明12 小时前
评估回归模型的指标与理解
人工智能·数据挖掘·回归
醉卧考场君莫笑13 小时前
数据分析常用方法:上
数据挖掘·数据分析
小王毕业啦13 小时前
2003-2023年 285个地级市邻接矩阵、经济地理矩阵等8个矩阵数据
大数据·人工智能·数据挖掘·数据分析·数据统计·社科数据·实证数据
2501_9418036213 小时前
在奥斯陆智能水利场景中构建实时水资源调度与高并发水质数据分析平台的工程设计实践经验分享
数据挖掘·数据分析·云计算