【RAG实战】LlamaIndex 深度集成:常用 Reader 全解析与自定义 Reader
·
LlamaIndex 深度集成:常用 Reader 全解析与自定义 Reader 实战
文章目录
LlamaIndex 深度集成:常用 Reader 全解析与自定义 Reader 实战
摘要:在 RAG 应用中,LlamaIndex 的 Reader 是数据入口。本文结合官方架构与项目实战,从源码层面解析常用 Reader,并给出自定义 Reader 的完整方案。
一、LlamaIndex Reader 体系架构
1.1 BaseReader:所有 Reader 的基座
# llama_index/core/readers/base.py
from abc import ABC, abstractmethod
from typing import List
from llama_index.core.schema import Document
class BaseReader(ABC):
@abstractmethod
def load_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 结果
简化源码逻辑如下:
class SimpleDirectoryReader(BaseReader):
def __init__(self, input_dir, file_extractor=None, ...):
self.input_dir = input_dir
# 用户自定义的 Reader 映射
self.file_extractor = file_extractor or {}
def _get_default_file_extractor(self) -> dict:
"""默认的扩展名-Reader 映射"""
return {
".pdf": PDFReader(),
".docx": DocxReader(),
".txt": TextReader(),
# ... 更多默认映射
}
def load_data(self, *args, **kwargs) -> List[Document]:
# 1. 收集文件
files = self._get_files()
# 2. 合并默认 + 自定义 file_extractor
extractor = {**self._get_default_file_extractor(), **self.file_extractor}
documents = []
for file in files:
ext = os.path.splitext(file)[1]
reader = extractor.get(ext) or self.default_reader
# 3. 调用 Reader.load_data
docs = reader.load_data(file)
documents.extend(docs)
return documents
关键结论:
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 映射 |
使用方法:
from llama_index.core import SimpleDirectoryReader
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
源码解读:
from llama_index.readers.file import DocxReader
class DocxReader(BaseReader):
def load_data(
self,
file: Path,
extra_info: Optional[Dict[str, Any]] = None,
**load_kwargs: Any,
) -> List[Document]:
# 使用 python-docx 解析 docx
# 将段落文本拼接为 Document
...
使用方法:
from llama_index.readers.file import DocxReader
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
源码解读:
from llama_index.readers.file import PDFReader
class PDFReader(BaseReader):
def load_data(
self,
file: Path,
extra_info: Optional[Dict[str, Any]] = None,
**load_kwargs: Any,
) -> List[Document]:
# 基于 PyPDF2 / pypdf 解析 PDF
# 默认按页拆分 Document
...
使用方法:
from llama_index.readers.file import PDFReader
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
源码解读:
from llama_parse import LlamaParse
class LlamaParse:
def __init__(self, api_key: str, result_type: str = "markdown", ...):
self.api_key = api_key
self.result_type = result_type
def load_data(self, file_path: str) -> List[Document]:
# 上传文件到 LlamaParse 云端
# 返回 Markdown/Text 结构
使用方法:
from llama_parse import LlamaParse
from llama_cloud_services.parse import ResultType
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
源码解读:
from llama_index.readers.web import BeautifulSoupWebReader
class BeautifulSoupWebReader(BaseReader):
def load_data(
self,
urls: List[str],
custom_hostname: Optional[str] = None,
**kwargs: Any,
) -> List[Document]:
# requests 获取 HTML
# BeautifulSoup 提取正文
使用方法:
from llama_index.readers.web import BeautifulSoupWebReader
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
源码解读:
from llama_index.readers.database import DatabaseReader
class DatabaseReader(BaseReader):
def __init__(self, uri: str, query: str):
self.uri = uri
self.query = query
def load_data(self) -> List[Document]:
# 使用 SQLAlchemy 执行 SQL
# 每行转换为 Document
使用方法:
from llama_index.readers.database import DatabaseReader
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 实现
"""
from pathlib import Path
from typing import List
from llama_cloud_services.parse import ResultType
from llama_index.core.readers.base import BaseReader
from llama_index.core.schema import Document
from llama_parse import LlamaParse
from utils.log_util import logger
def create_llama_parser(result_type: ResultType = ResultType.MD, split_by_page: bool = False):
"""工厂函数:创建 LlamaParse 解析器"""
return LlamaParse(
api_key="your-api-key",
result_type=result_type,
verbose=True,
)
class DocxLlamaParseReader(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 = None
def _get_parser(self) -> LlamaParse:
"""懒加载解析器实例"""
if self._parser is None:
self._parser = create_llama_parser(
result_type=self.result_type,
split_by_page=self.split_by_page,
)
return self._parser
def load_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_info or {}
metadata["file_path"] = file_path
metadata["source"] = "llamaparse"
for doc in documents:
doc.metadata.update(metadata)
logger.info(f"DocxLlamaParseReader 读取完成: {file_path}, 共 {len(documents)} 个 Document")
return documents
4.3 注入 SimpleDirectoryReader 使用
from llama_index.core import SimpleDirectoryReader
from module_rag.rag_common.file_read.llamaParse_realize.docx_reader import DocxLlamaParseReader
# 自定义 file_extractor
file_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 模板
from pathlib import Path
from typing import List
from llama_index.core.readers.base import BaseReader
from llama_index.core.schema import Document
class MyCustomReader(BaseReader):
def __init__(self, encoding: str = "utf-8"):
self.encoding = encoding
def load_data(
self,
file: Path,
extra_info: dict | None = None,
**load_kwargs,
) -> List[Document]:
file_path = Path(file)
# 1. 读取/处理文件
with open(file_path, "r", encoding=self.encoding) as f:
text = f.read()
# 2. 构建元数据
metadata = extra_info or {}
metadata.update({
"file_path": str(file_path),
"file_name": file_path.name,
"file_ext": file_path.suffix,
})
# 3. 返回 Document
return [Document(text=text, metadata=metadata)]
# 使用
reader = MyCustomReader()
docs = reader.load_data("example.txt")
五、自定义 Reader 的规范与最佳实践
5.1 必须继承 BaseReader
from llama_index.core.readers.base import BaseReader
class MyReader(BaseReader):
...
5.2 load_data 签名规范
def load_data(
self,
file: Path,
extra_info: Optional[dict] = None,
**load_kwargs,
) -> List[Document]:
...
5.3 元数据必须包含 file_path
metadata = extra_info or {}
metadata["file_path"] = str(file_path)
metadata["source"] = "my_reader"
5.4 懒加载昂贵资源
def _get_parser(self):
if self._parser is None:
self._parser = create_parser()
return self._parser
5.5 异常处理与日志
try:
documents = parser.load_data(file_path)
except Exception as e:
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 模块。
更多推荐

所有评论(0)