步骤一:数据准备与预处理
- 目标:将我们的原始知识文档(例如,一组关于某个开源项目的介绍文档,可能是TXT或PDF格式)加载进来,并将其分割成适合RAG系统处理的、带有元数据的文本块(chunks)。
具体操作:
1. 数据加载(Data Loading)
我们需要从文件系统中加载文档。Lang Chain提供了多种DocumentLoader`来处理不同类型的文件。
首先,假设我们有一个名为knowledge_base`的文件夹,里面存放了我们的知识文档。例如,可以创建两个简单的.txt文件:
knowledge_base/project_intro.txt:
RAGFlow是一个基于Python的开源检索增强生成框架。它致力于简化RAG应用的开发、评估和部署流程。
RAGFlow的核心特性包括模块化设计、多种检索策略支持以及易于扩展的接口。
该项目由ABCLab于2024年发起。
knowledge_base/features.txt:
RAGFlow的主要功能包括:
- 数据接⼊:⽀持PDF, TXT, Markdown等多种⽂档格式。
- 智能分块:提供多种⽂本分割策略,如递归分割、语义分割。
- 向量化与索引:集成主流Embedding模型和向量数据库。
- 灵活检索:⽀持向量检索、关键词检索及混合检索。
- LLM集成:⽅便对接OpenAI、HuggingFace等多种⼤语⾔模型。
- 评估套件:内置常⽤RAG评估指标和⼯具。
现在,我们使⽤LangChain的 DirectoryLoader
和 TextLoader
来加载这些⽂档
ini
from langchain_community.document_loaders import DirectoryLoader, TextLoader
from langchain_core.documents import Document # Document type
from typing import List
# 定义知识库⽬录
knowledge_base_path = "./knowledge_base"
# 注意:请确保在Python脚本的同级⽬录下创建 knowledge_base ⽂件夹,并将上述txt⽂件放⼊其
# 使⽤DirectoryLoader加载⽬录下所有.txt⽂件,指定使⽤TextLoader
loader = DirectoryLoader(
knowledge_base_path,
glob="**/*.txt", # 匹配所有txt⽂件
loader_cls=TextLoader, # 使⽤TextLoader加载
loader_kwargs={'encoding': 'utf-8'}, # TextLoader的参数,确保UTF-8编码
use_multithreading=True, # 可以加速加载多个⽂件
show_progress=True # 显⽰加载进度
)
documents: List[Document] = loader.load()
if documents:print(f"成功加载 {len(documents)} 个⽂档.")
for i, doc in enumerate(documents):
print(f"\n--- ⽂档 {i+1}: {doc.metadata.get('source', '未知来源')} ---
print(f"内容预览 (前100字符): {doc.page_content[:100]}")
else:
print(f"未能从 '{knowledge_base_path}' 加载到任何⽂档。请检查路径和⽂件。")
# 对于PDF⽂件加载,可以使⽤PyPDFLoader (需要 pip install pypdf)
# from langchain_community.document_loaders import PyPDFLoader
# pdf_loader = PyPDFLoader("path/to/your/document.pdf")
# pdf_documents = pdf_loader.load()
说明:DirectoryLoader可以方便地加载整个目录的文件。每个加载的Document对象通常包含两部分:page_content(文档的文本内容)和metadata(一个字典,通常包含如 source等元信息,即文件名)。
2.文本分割(Text Splitting)
长文档需要被切分成更小的语义单元(chunks), 以便Embedding模型处理并提高检索的精确性。LangChain的 RecursiveCharacterTextSplitter是一个常用的选择,它会尝试按预设的一系列分隔符(如换行符、句号等)递归地分割文本,以求在满足大小限制的同时尽量保持语义的完整性。

上图展⽰了⽂本分割的过程。⻓⽂档被切分成较⼩的⽂本块 (chunks)。相邻⽂本块之间通常设置重叠(overlap)部分(⽰例中红⾊字体为上⼀块的重叠内容,绿⾊字体为与下⼀块的重叠内容),以确保语义的连续性,避免关键信息在分割点被割裂。
ini
from langchain.text_splitter import RecursiveCharacterTextSplitter
# 初始化⽂本分割器
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=200, # 每个块的最⼤字符数。应根据Embedding模型和LLM的上下⽂窗⼝进⾏调
# 对于中⽂,⼀个汉字通常算1个token,但具体取决于模型的分词器。
# BGE等模型通常有512 tokens的输⼊限制。
chunk_overlap=20, # 相邻块之间的重叠字符数,帮助保留上下⽂连贯性。
separators=["\n\n", "\n", "。", "!", "?", ",", "、", " ", ""], # 中⽂场景
length_function=len, # 使⽤字符⻓度计算chunk_size
add_start_index=True # 在metadata中添加块在原⽂中的起始位置,可选
)
if documents: # 确保前⾯加载成功
document_chunks: List[Document] = text_splitter.split_documents(document
print(f"\n⽂档被分割成 {len(document_chunks)} 个⽂本块.")
if document_chunks:
print(f"第⼀个⽂本块预览: {document_chunks[0].page_content}")
print(f"第⼀个⽂本块元数据: {document_chunks[0].metadata}")
else:
print("没有⽂档可供分割。")
document_chunks = [] # 初始化为空列表以防后续代码出错
说明::chunk_size 和 chunk_overlap 的选择对RAG性能有显著影响,需要根据具体数据、Embedding模型的能力以及LLM的上下文窗口大小进行实验调整。对于中文文本,合理设置separators尤为重要,以尽可能在自然的语义边界(如段落、句子)进行切分。