【RAG实战】LlamaIndex 深度集成:常用 Reader 全解析与自定义 Reader
LlamaIndex 深度集成:常用 Reader 全解析与自定义 Reader 实战
文章目录
- LlamaIndex 深度集成:常用 Reader 全解析与自定义 Reader 实战
- LlamaIndex 深度集成:常用 Reader 全解析与自定义 Reader 实战
- 一、LlamaIndex Reader 体系架构
- 1.1 BaseReader:所有 Reader 的基座
- 1.2 SimpleDirectoryReader 的核心调度逻辑
- 二、LlamaIndex 常用 Reader 盘点
- 2.1 SimpleDirectoryReader —— 目录批量读取
- 2.2 DocxReader —— Word 文档读取
- 2.3 PDFReader —— PDF 文件读取
- 2.4 LlamaParseReader / LlamaParse —— 云端高质量解析
- 2.5 BeautifulSoupWebReader —— 网页正文提取
- 2.6 DatabaseReader —— 数据库读取
- 2.7 其他常用 Reader
- 三、Reader 对比与选型建议
- 四、自定义 Reader 深度实战
- 4.1 为什么需要自定义 Reader?
- 4.2 项目实战:封装 LlamaParse 为 DOC/DOCX Reader
- 4.3 注入 SimpleDirectoryReader 使用
- 4.4 更简单的自定义 Reader 模板
- 五、自定义 Reader 的规范与最佳实践
- 5.1 必须继承 BaseReader
- 5.2 load_data 签名规范
- 5.3 元数据必须包含 file_path
- 5.4 懒加载昂贵资源
- 5.5 异常处理与日志
- 5.6 通过 file_extractor 注入
- 六、本项目文件读取架构总结
- 七、总结
LlamaIndex 深度集成:常用 Reader 全解析与自定义 Reader 实战
摘要:在 RAG 应用中,LlamaIndex 的 Reader 是数据入口。本文结合官方架构与项目实战,从源码层面解析常用 Reader,并给出自定义 Reader 的完整方案。
一、LlamaIndex Reader 体系架构
1.1 BaseReader:所有 Reader 的基座
# llama_index/core/readers/base.pyfromabcimportABC,abstractmethodfromtypingimportListfromllama_index.core.schemaimportDocumentclassBaseReader(ABC):@abstractmethoddefload_data(self,*args:Any,**load_kwargs:Any)->List[Document]:"""加载数据并返回 Document 列表"""源码解读:
BaseReader是抽象基类,只约束一个load_data方法Document是 LlamaIndex 的核心数据结构,包含text和metadata- 任何自定义 Reader 只需实现
load_data,返回List[Document]
1.2 SimpleDirectoryReader 的核心调度逻辑
SimpleDirectoryReader是目录级入口,它的核心作用是:
- 扫描目录:递归或非递归收集文件
- 扩展名匹配:根据文件后缀选择 Reader
- Reader 分发:通过
file_extractor映射执行具体 Reader - Document 组装:合并所有 Reader 结果
简化源码逻辑如下:
classSimpleDirectoryReader(BaseReader):def__init__(self,input_dir,file_extractor=None,...):self.input_dir=input_dir# 用户自定义的 Reader 映射self.file_extractor=file_extractoror{}def_get_default_file_extractor(self)->dict:"""默认的扩展名-Reader 映射"""return{".pdf":PDFReader(),".docx":DocxReader(),".txt":TextReader(),# ... 更多默认映射}defload_data(self,*args,**kwargs)->List[Document]:# 1. 收集文件files=self._get_files()# 2. 合并默认 + 自定义 file_extractorextractor={**self._get_default_file_extractor(),**self.file_extractor}documents=[]forfileinfiles:ext=os.path.splitext(file)[1]reader=extractor.get(ext)orself.default_reader# 3. 调用 Reader.load_datadocs=reader.load_data(file)documents.extend(docs)returndocuments关键结论:
file_extractor优先级高于默认 Reader- 扩展名小写匹配,如
.docx、.pdf - 自定义 Reader 必须继承
BaseReader
二、LlamaIndex 常用 Reader 盘点
2.1 SimpleDirectoryReader —— 目录批量读取
概念:
SimpleDirectoryReader是 LlamaIndex 最常用的本地文件加载器,支持自动识别多种格式。
源码位置:
llama-index-core/llama_index/core/readers/file/base.py核心参数:
| 参数 | 类型 | 说明 |
|---|---|---|
input_dir | str | 输入目录 |
input_files | List[str] | 指定文件列表 |
required_exts | List[str] | 只读取指定后缀 |
exclude | List[str] | 排除文件,支持通配符 |
recursive | bool | 是否递归子目录 |
filename_as_id | bool | 文件名作为 doc_id |
file_extractor | dict | 自定义 Reader 映射 |
使用方法:
fromllama_index.coreimportSimpleDirectoryReader documents=SimpleDirectoryReader(input_dir="./knowledge_base",recursive=True,required_exts=[".pdf",".docx",".txt"],exclude=["*.tmp","*.log"],filename_as_id=True,).load_data()源码级建议:
# file_extractor 的本质是一个字典# key: 文件扩展名(带点,如 .pdf)# value: BaseReader 实例file_extractor={".pdf":PDFReader(),".docx":DocxReader(),".doc":MyCustomDocReader(),# 自定义 Reader}2.2 DocxReader —— Word 文档读取
源码位置:
llama-index-readers-file/llama_index/readers/file/docs/base.py源码解读:
fromllama_index.readers.fileimportDocxReaderclassDocxReader(BaseReader):defload_data(self,file:Path,extra_info:Optional[Dict[str,Any]]=None,**load_kwargs:Any,)->List[Document]:# 使用 python-docx 解析 docx# 将段落文本拼接为 Document...使用方法:
fromllama_index.readers.fileimportDocxReader reader=DocxReader()docs=reader.load_data(file="report.docx")深度集成建议:
- 简单 Word 文档可用
- 复杂表格、图文混排建议使用
LlamaParseReader - 可通过
file_extractor注入
2.3 PDFReader —— PDF 文件读取
源码位置:
llama-index-readers-file/llama_index/readers/file/docs/base.py源码解读:
fromllama_index.readers.fileimportPDFReaderclassPDFReader(BaseReader):defload_data(self,file:Path,extra_info:Optional[Dict[str,Any]]=None,**load_kwargs:Any,)->List[Document]:# 基于 PyPDF2 / pypdf 解析 PDF# 默认按页拆分 Document...使用方法:
fromllama_index.readers.fileimportPDFReader reader=PDFReader()docs=reader.load_data(file="paper.pdf")使用建议:
- 文字型 PDF 效果好
- 扫描版、复杂排版效果差
- 生产环境建议用
LlamaParse或MinerU
2.4 LlamaParseReader / LlamaParse —— 云端高质量解析
源码位置:
llama-parse/llama_parse/__init__.py llama-cloud-services/llama_cloud_services/parse/__init__.py源码解读:
fromllama_parseimportLlamaParseclassLlamaParse:def__init__(self,api_key:str,result_type:str="markdown",...):self.api_key=api_key self.result_type=result_typedefload_data(self,file_path:str)->List[Document]:# 上传文件到 LlamaParse 云端# 返回 Markdown/Text 结构使用方法:
fromllama_parseimportLlamaParsefromllama_cloud_services.parseimportResultType parser=LlamaParse(api_key="your-api-key",result_type=ResultType.MD,verbose=True,)documents=parser.load_data("complex.pdf")深度集成建议:
- 本项目将其包装为
DocxLlamaParseReader和DocLlamaParseReader - 通过
file_extractor注入SimpleDirectoryReader,实现自动调用
2.5 BeautifulSoupWebReader —— 网页正文提取
源码位置:
llama-index-readers-web/llama_index/readers/web/beautiful_soup_web/base.py源码解读:
fromllama_index.readers.webimportBeautifulSoupWebReaderclassBeautifulSoupWebReader(BaseReader):defload_data(self,urls:List[str],custom_hostname:Optional[str]=None,**kwargs:Any,)->List[Document]:# requests 获取 HTML# BeautifulSoup 提取正文使用方法:
fromllama_index.readers.webimportBeautifulSoupWebReader reader=BeautifulSoupWebReader()docs=reader.load_data(urls=["https://example.com/article"])使用建议:
- 网页 RAG 首选
- 可配合爬虫框架批量抓取
- 注意请求频率和反爬
2.6 DatabaseReader —— 数据库读取
源码位置:
llama-index-readers-database/llama_index/readers/database/base.py源码解读:
fromllama_index.readers.databaseimportDatabaseReaderclassDatabaseReader(BaseReader):def__init__(self,uri:str,query:str):self.uri=uri self.query=querydefload_data(self)->List[Document]:# 使用 SQLAlchemy 执行 SQL# 每行转换为 Document使用方法:
fromllama_index.readers.databaseimportDatabaseReader reader=DatabaseReader(uri="mysql+pymysql://user:pass@localhost/db",query="SELECT id, content FROM articles",)docs=reader.load_data()2.7 其他常用 Reader
| Reader | 包名 | 用途 |
|---|---|---|
JSONReader | llama-index-readers-file | 读取 JSON 文件 |
CSVReader/PandasCSVReader | llama-index-readers-file | 读取 CSV 文件 |
MarkdownReader | llama-index-readers-file | 读取 Markdown 文件 |
UnstructuredReader | llama-index-readers-file | 多格式复杂解析 |
WikipediaReader | llama-index-readers-wikipedia | 维基百科 |
SimpleWebPageReader | llama-index-readers-web | 简单网页读取 |
NotionPageReader | llama-index-readers-notion | Notion 页面 |
GithubRepositoryReader | llama-index-readers-github | GitHub 仓库 |
三、Reader 对比与选型建议
| Reader | 数据源 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
SimpleDirectoryReader | 本地目录 | 批量、自动识别 | 复杂版式弱 | 本地文档批量入库 |
DocxReader | .docx | 轻量本地 | 复杂排版差 | 简单 Word |
PDFReader | 轻量本地 | 扫描版差 | 文字型 PDF | |
LlamaParse | 多格式 | 版式解析强 | 需 API Key | 复杂文档 |
BeautifulSoupWebReader | 网页 | 自动正文提取 | 受反爬限制 | 网页 RAG |
DatabaseReader | 数据库 | 对接业务数据 | 需写 SQL | 企业数据 |
JSONReader/CSVReader | 结构化文件 | 简单易用 | 需规范格式 | 表格数据 |
四、自定义 Reader 深度实战
4.1 为什么需要自定义 Reader?
- 默认 Reader 不支持某些格式(如
.doc旧版 Word) - 需要统一元数据(如注入
file_path、source) - 需要接入外部解析服务(如 LlamaParse、MinerU)
- 需要预处理文本(如过滤页眉页脚、提取特定章节)
4.2 项目实战:封装 LlamaParse 为 DOC/DOCX Reader
本项目中的完整实现:
""" DOCX 文件 LlamaParse 读取器(适配 SimpleDirectoryReader) 基于 llama_index.core.readers.base.BaseReader 实现 """frompathlibimportPathfromtypingimportListfromllama_cloud_services.parseimportResultTypefromllama_index.core.readers.baseimportBaseReaderfromllama_index.core.schemaimportDocumentfromllama_parseimportLlamaParsefromutils.log_utilimportloggerdefcreate_llama_parser(result_type:ResultType=ResultType.MD,split_by_page:bool=False):"""工厂函数:创建 LlamaParse 解析器"""returnLlamaParse(api_key="your-api-key",result_type=result_type,verbose=True,)classDocxLlamaParseReader(BaseReader):""" DOCX 文件读取器 通过 LlamaParse 官方 API 解析,可被 SimpleDirectoryReader 通过 file_extractor 注入 """def__init__(self,split_by_page:bool=True,result_type:ResultType=ResultType.MD):self.split_by_page=split_by_page self.result_type=result_type self._parser:LlamaParse|None=Nonedef_get_parser(self)->LlamaParse:"""懒加载解析器实例"""ifself._parserisNone:self._parser=create_llama_parser(result_type=self.result_type,split_by_page=self.split_by_page,)returnself._parserdefload_data(self,file:Path,extra_info:dict|None=None,**load_kwargs)->List[Document]:file_path=str(file)parser=self._get_parser()logger.info(f"DocxLlamaParseReader 开始读取:{file_path}")documents=parser.load_data(file_path)# 注入元数据metadata=extra_infoor{}metadata["file_path"]=file_path metadata["source"]="llamaparse"fordocindocuments:doc.metadata.update(metadata)logger.info(f"DocxLlamaParseReader 读取完成:{file_path}, 共{len(documents)}个 Document")returndocuments4.3 注入 SimpleDirectoryReader 使用
fromllama_index.coreimportSimpleDirectoryReaderfrommodule_rag.rag_common.file_read.llamaParse_realize.docx_readerimportDocxLlamaParseReader# 自定义 file_extractorfile_extractor={".doc":DocLlamaParseReader(split_by_page=True),".docx":DocxLlamaParseReader(split_by_page=True),}reader=SimpleDirectoryReader(input_dir="./docs",required_exts=[".doc",".docx"],file_extractor=file_extractor,)documents=reader.load_data()4.4 更简单的自定义 Reader 模板
frompathlibimportPathfromtypingimportListfromllama_index.core.readers.baseimportBaseReaderfromllama_index.core.schemaimportDocumentclassMyCustomReader(BaseReader):def__init__(self,encoding:str="utf-8"):self.encoding=encodingdefload_data(self,file:Path,extra_info:dict|None=None,**load_kwargs,)->List[Document]:file_path=Path(file)# 1. 读取/处理文件withopen(file_path,"r",encoding=self.encoding)asf:text=f.read()# 2. 构建元数据metadata=extra_infoor{}metadata.update({"file_path":str(file_path),"file_name":file_path.name,"file_ext":file_path.suffix,})# 3. 返回 Documentreturn[Document(text=text,metadata=metadata)]# 使用reader=MyCustomReader()docs=reader.load_data("example.txt")五、自定义 Reader 的规范与最佳实践
5.1 必须继承 BaseReader
fromllama_index.core.readers.baseimportBaseReaderclassMyReader(BaseReader):...5.2 load_data 签名规范
defload_data(self,file:Path,extra_info:Optional[dict]=None,**load_kwargs,)->List[Document]:...5.3 元数据必须包含 file_path
metadata=extra_infoor{}metadata["file_path"]=str(file_path)metadata["source"]="my_reader"5.4 懒加载昂贵资源
def_get_parser(self):ifself._parserisNone:self._parser=create_parser()returnself._parser5.5 异常处理与日志
try:documents=parser.load_data(file_path)exceptExceptionase:logger.error(f"读取失败:{file_path}, 错误:{e}")return[]5.6 通过 file_extractor 注入
file_extractor={".doc":DocLlamaParseReader(),".docx":DocxLlamaParseReader(),".pdf":PDFReader(),}六、本项目文件读取架构总结
module_rag/rag_common/file_read/ ├── __init__.py # 统一入口,支持多种解析方式 ├── base_reader.py # 基于 SimpleDirectoryReader 的封装 ├── self_define/ # 本地 Reader 配置 └── llamaParse_realize/ # 基于 LlamaParse 的自定义 Reader ├── __init__.py # file_extractor 配置 ├── doc_reader.py # .doc 自定义 Reader └── docx_reader.py # .docx 自定义 Reader核心调用链:
common_read_file_content(file_path,read_method="LlamaParse")↓ _get_file_extractor_by_method("LlamaParse")↓ get_file_extractor()# 返回 {".doc": DocLlamaParseReader(), ".docx": DocxLlamaParseReader()}↓ base_read_file(file_path,file_extractor)↓ SimpleDirectoryReader(input_files=[file_path],file_extractor=file_extractor).load_data()↓ DocxLlamaParseReader.load_data(file)# 自定义 Reader↓ 返回 List[Document]七、总结
| Reader | 定位 |
|---|---|
SimpleDirectoryReader | 本地目录批量加载入口 |
DocxReader/PDFReader | 轻量本地文档解析 |
LlamaParse | 复杂版式云端解析 |
BeautifulSoupWebReader | 网页知识库 |
DatabaseReader | 企业业务库 |
| 自定义 Reader | 特殊格式和业务需求 |
LlamaIndex 的 Reader 机制非常清晰:继承BaseReader,实现load_data,通过file_extractor注入SimpleDirectoryReader。掌握这一点,就能无缝扩展任意数据源,深度集成 LlamaIndex。
如果你需要,我可以把文中示例整理成一个可运行的module_rag/rag_common/file_read/custom_reader_demo.py文件,并接入你现有的file_read模块。
