catchadmin / docloader
A PHP document loader and splitter for RAG applications
v1.0.0
2026-02-04 04:17 UTC
Requires
- php: ^8.1
- html2text/html2text: ^4.3
- league/commonmark: ^2.4
- symfony/process: ^5.0|^6.0|^7.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.0
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^10.0
This package is not auto-updated.
Last update: 2026-02-09 02:01:47 UTC
README
DocLoader
一个用于 RAG(检索增强生成)应用的 PHP 文档加载与分块工具库
特性
- 🔥 支持多种文档格式:TXT、PDF、HTML、Markdown
- 🌍 支持中英文混合文本分割
- ✂️ 智能分块策略,适配 RAG 向量化需求
- 📑 Markdown 层级分块,保留文档结构信息
- 🔧 高度可扩展,支持自定义 Reader 和 Splitter
- 📦 零配置开箱即用
安装
composer require catchadmin/docloader
系统依赖
PDF 解析需要安装 Poppler Utils(pdftotext):
| 系统 | 安装命令 |
|---|---|
| Ubuntu/Debian | sudo apt-get install poppler-utils |
| CentOS/RHEL | sudo yum install poppler-utils |
| macOS | brew install poppler |
| Windows | 下载 Poppler 并添加 bin 目录到 PATH |
基础使用
从字符串加载
use Catch\DocLoader\Loader\StringLoader;
$documents = StringLoader::for('这是一段文本内容...')
->getDocuments();
// 设置来源名称
$documents = StringLoader::for('内容', 'my-document')
->getDocuments();
从文件加载
use Catch\DocLoader\Loader\FileLoader;
// 加载单个文件
$documents = FileLoader::for('/path/to/file.txt')
->getDocuments();
// 加载整个目录(递归)
$documents = FileLoader::for('/path/to/documents/')
->getDocuments();
加载 PDF 文件
use Catch\DocLoader\Loader\FileLoader;
use Catch\DocLoader\Reader\PdfReader;
$documents = FileLoader::for('/path/to/document.pdf')
->addReader('pdf', new PdfReader())
->getDocuments();
自定义 pdftotext 路径
// Windows 示例
$pdfReader = new PdfReader('C:\poppler\bin\pdftotext.exe');
$documents = FileLoader::for('/path/to/document.pdf')
->addReader('pdf', $pdfReader)
->getDocuments();
加载 HTML 文件
use Catch\DocLoader\Loader\FileLoader;
use Catch\DocLoader\Reader\HtmlReader;
$documents = FileLoader::for('/path/to/page.html')
->addReader(['html', 'htm'], new HtmlReader())
->getDocuments();
加载 Markdown 文件
use Catch\DocLoader\Loader\FileLoader;
use Catch\DocLoader\Reader\MarkdownReader;
$documents = FileLoader::for('/path/to/document.md')
->addReader('md', new MarkdownReader())
->getDocuments();
// 配置选项
$mdReader = new MarkdownReader(
preserveHeadings: true, // 是否保留标题标记(默认 true)
useGfm: true // 是否使用 GitHub Flavored Markdown(默认 true)
);
$documents = FileLoader::for('/path/to/docs/')
->addReader('md', $mdReader)
->getDocuments();
进阶使用
文本分块策略
DelimiterSplitter(分隔符分块)
按指定分隔符分割文本,适合结构化文档:
use Catch\DocLoader\Splitter\DelimiterSplitter;
$splitter = new DelimiterSplitter(
maxLength: 500, // 每块最大字符数
separator: "\n", // 分隔符(默认空格)
wordOverlap: 20 // 词重叠数(保持上下文连贯)
);
$documents = FileLoader::for($path)
->withSplitter($splitter)
->getDocuments();
SentenceSplitter(句子分块)- 推荐用于 RAG
按句子边界分割,支持中英文,适合自然语言文档:
use Catch\DocLoader\Splitter\SentenceSplitter;
$splitter = new SentenceSplitter(
maxWords: 150, // 每块最大词数
overlapWords: 20 // 重叠词数
);
$documents = FileLoader::for($path)
->withSplitter($splitter)
->getDocuments();
RAG 推荐参数: | 参数 | 推荐值 | 说明 | |-----|-------|------| | maxWords | 100-200 | 约 300-600 中文字符 | | overlapWords | 15-30 | 约 10-20% 重叠 |
MarkdownSplitter(Markdown 层级分块)- 推荐用于知识库
按 Markdown 标题层级智能分块,保留文档结构信息:
use Catch\DocLoader\Splitter\MarkdownSplitter;
$splitter = new MarkdownSplitter(
maxLevel: 3, // 最深切分到 H3
minChunkSize: 50, // 最小块字符数
includeHeadingInContent: true // chunk 内容包含标题
);
$documents = FileLoader::for('/knowledge-base/')
->addReader('md', new MarkdownReader())
->withSplitter($splitter)
->getDocuments();
分块示例:
# 第一章 → Chunk1: {level:1, title:"第一章", path:["第一章"]}
内容...
## 1.1 节 → Chunk2: {level:2, title:"1.1 节", path:["第一章","1.1 节"], parentId:Chunk1.id}
内容...
### 1.1.1 小节 → Chunk3: {level:3, title:"1.1.1 小节", path:["第一章","1.1 节","1.1.1 小节"]}
层级信息字段:
| 字段 | 类型 | 说明 |
|-----|------|------|
| level | int | 层级深度(0=无标题,1=H1,2=H2...) |
| sectionTitle | string | 当前章节标题 |
| headingPath | array | 完整标题路径 |
| parentId | string | 父块 ID |
文件过滤
$documents = FileLoader::for('/path/to/docs/')
// 只处理指定扩展名
->setIncludeExtensions(['pdf', 'txt', 'md'])
// 排除匹配的文件/目录
->setExcludePatterns(['*.tmp', '.git/*', 'node_modules/*'])
->addReader('pdf', new PdfReader())
->getDocuments();
多格式混合加载
$documents = FileLoader::for('/path/to/docs/')
->addReader('pdf', new PdfReader())
->addReader(['html', 'htm'], new HtmlReader())
->withSplitter(new SentenceSplitter(maxWords: 150, overlapWords: 20))
->getDocuments();
Document 对象
每个文档块包含以下属性:
$document->id; // 唯一 ID(自动生成)
$document->content; // 文本内容
$document->sourceType; // 来源类型:'files', 'string'
$document->sourceName; // 来源名称:文件名或自定义名称
$document->embedding; // 向量嵌入(默认空数组,由外部填充)
$document->score; // 相关性分数(默认 0)
$document->metadata; // 自定义元数据
操作 Document
// 链式设置
$document->setContent('新内容')
->setSourceName('custom-name')
->setMetadata(['author' => '张三', 'date' => '2024-01-01'])
->addMetadata('category', '技术文档');
// 序列化为 JSON
$json = json_encode($document);
自定义扩展
自定义 Reader
实现 ReaderInterface 接口:
use Catch\DocLoader\Reader\ReaderInterface;
class MarkdownReader implements ReaderInterface
{
public static function getText(string $filePath, array $options = []): string
{
$content = file_get_contents($filePath);
// 处理 Markdown 语法...
return $content;
}
public function read(string $filePath): string
{
return static::getText($filePath);
}
}
// 使用
$documents = FileLoader::for('/docs/')
->addReader('md', new MarkdownReader())
->getDocuments();
自定义 Splitter
继承 AbstractSplitter(推荐):
use Catch\DocLoader\Splitter\AbstractSplitter;
use Catch\DocLoader\Document;
class ParagraphSplitter extends AbstractSplitter
{
public function __construct(
private int $minLength = 100
) {}
public function splitDocument(Document $document): array
{
$paragraphs = preg_split('/\n{2,}/', $document->getContent());
$result = [];
foreach ($paragraphs as $para) {
if (mb_strlen($para) >= $this->minLength) {
$newDoc = new Document(trim($para));
$newDoc->sourceType = $document->getSourceType();
$newDoc->sourceName = $document->getSourceName();
$result[] = $newDoc;
}
}
return $result;
}
}
// 使用
$documents = FileLoader::for($path)
->withSplitter(new ParagraphSplitter(minLength: 50))
->getDocuments();
RAG 最佳实践
推荐配置
use Catch\DocLoader\Loader\FileLoader;
use Catch\DocLoader\Reader\PdfReader;
use Catch\DocLoader\Splitter\SentenceSplitter;
$documents = FileLoader::for('/knowledge-base/')
->addReader('pdf', new PdfReader())
->setIncludeExtensions(['pdf', 'txt', 'md'])
->setExcludePatterns(['*.tmp', '.*'])
->withSplitter(new SentenceSplitter(
maxWords: 150, // 适合大多数 embedding 模型
overlapWords: 25 // 约 15% 重叠
))
->getDocuments();
// 准备向量化
foreach ($documents as $doc) {
$text = $doc->getContent();
// 调用 embedding API...
// $embedding = $embeddingService->embed($text);
// $doc->setEmbedding($embedding);
}
分块大小建议
| Embedding 模型 | maxWords | 说明 |
|---|---|---|
| OpenAI text-embedding-3 | 150-200 | 支持 8191 tokens |
| Cohere embed-v3 | 100-150 | 支持 512 tokens |
| BGE-M3 | 200-300 | 支持 8192 tokens |
组件一览
Loader(加载器)
| 类 | 说明 |
|---|---|
StringLoader | 从字符串加载 |
FileLoader | 从文件或目录加载 |
Reader(读取器)
| 类 | 说明 | 依赖 |
|---|---|---|
TextFileReader | 纯文本文件 | 无 |
PdfReader | PDF 文件 | pdftotext |
HtmlReader | HTML 文件 | html2text/html2text |
MarkdownReader | Markdown 文件 | league/commonmark |
Splitter(分块器)
| 类 | 说明 | 推荐场景 |
|---|---|---|
DelimiterSplitter | 按分隔符+最大长度 | 结构化文档 |
SentenceSplitter | 按句子+最大词数 | 自然语言文档(推荐) |
MarkdownSplitter | 按标题层级+保留结构 | Markdown 知识库(推荐) |
License
MIT License