目录递归处理
概述¶
目录递归处理功能(ingest-directory.ts
)是 RAG Worker 工具集中的一个核心组件,专门用于批量处理指定目录下的 Hugo 内容文件。该功能支持递归遍历目录结构,自动识别和处理 index.md
和 _index.md
文件,并智能过滤 draft 状态的文件。
核心特性¶
🎯 文件类型支持¶
index.md
: 页面内容文件- 博客文章内容
- 书籍章节内容
- 文档页面内容
-
自定义页面内容
-
_index.md
: 章节/分类索引文件 - 书籍目录和章节介绍
- 博客分类页面
- 文档分区概述
- 导航结构文件
🛡️ 智能 Draft 过滤¶
系统会按照 Hugo 的 draft 规则自动过滤不应该被索引的内容:
- 直接 Draft 检查
---
title: "示例文章"
draft: true # 此文件将被过滤
---
- Section 级别 Draft
# content/zh/book/example/_index.md
---
title: "示例书籍"
draft: true # 整个 example 目录下的文件都将被过滤
---
- Cascade Draft 继承
# content/zh/book/example/_index.md
---
title: "示例书籍"
cascade:
draft: true # 所有子页面都继承 draft 状态
layout: book-content
---
📊 详细进度跟踪¶
处理过程中会显示:
- 发现的文件总数
- Draft 过滤结果
- 逐文件处理进度
- 向量化块数统计
- 上传成功/失败状态
- 总体处理时间和平均速度
使用方法¶
基础用法¶
# 进入 rag-worker 目录
cd tools/rag-worker
# 处理单个目录
npm run ingest-directory -- ../../content/zh/book
# 处理多个目录
npm run ingest-directory -- ../../content/zh/book ../../content/en/book
高级用法¶
# 使用绝对路径
npm run ingest-directory -- /path/to/hugo/content/zh/docs
# 混合处理不同类型的目录
npm run ingest-directory -- \
../../content/zh/book \
../../content/zh/docs \
../../content/en/blog
# 处理特定的子目录
npm run ingest-directory -- ../../content/zh/book/kubernetes-handbook
输出示例¶
📁 RAG Directory Ingestion Tool
📄 Processes all index.md and _index.md files recursively
🎯 Target directories: ../../content/zh/book/example
🚀 Starting directory ingestion...
🔍 Searching for index.md and _index.md files in: /path/to/content/zh/book/example
📁 Found 15 index/section files in ../../content/zh/book/example
📊 Total files found: 15
🔍 Checking draft status for files...
⏭️ Skipping draft file: ../../content/zh/book/example/draft-chapter/_index.md
⏭️ Skipping draft file: ../../content/zh/book/example/draft-chapter/intro/index.md
📝 Filtered out 2 draft files
📊 Files to process after draft filtering: 13
📋 Files to be processed:
1. ../../content/zh/book/example/_index.md
2. ../../content/zh/book/example/chapter1/_index.md
3. ../../content/zh/book/example/chapter1/intro/index.md
...
📄 Processing (1/13): ../../content/zh/book/example/_index.md
✅ Processed: 3 chunks
⬆️ Uploaded: 3 chunks
📄 Processing (2/13): ../../content/zh/book/example/chapter1/_index.md
✅ Processed: 2 chunks
⬆️ Uploaded: 2 chunks
...
🎉 Directory ingestion completed!
📈 Stats:
- Found: 15 total files
- Processed: 13/13 non-draft files
- Skipped: 2 draft files
- Failed: 0 files
- Total chunks: 45
⏱️ Time: 12.3s
🚀 Average speed: 3.7 chunks/sec
技术实现¶
Draft 检查算法¶
async function isDraftFile(filePath: string): Promise<boolean> {
// 1. 检查文件本身的 draft 状态
const content = await fs.readFile(filePath, 'utf-8');
const fm = matter(content);
if (fm.data.draft === true) {
return true;
}
// 2. 检查父级目录的 cascade draft 设置
let currentDir = path.dirname(filePath);
while (currentDir && currentDir !== '.' && currentDir !== '/') {
const indexPath = path.join(currentDir, '_index.md');
try {
const indexContent = await fs.readFile(indexPath, 'utf-8');
const indexFm = matter(indexContent);
// 检查直接的 draft 设置
if (indexFm.data.draft === true) {
return true;
}
// 检查 cascade 中的 draft 设置
if (indexFm.data.cascade && indexFm.data.cascade.draft === true) {
return true;
}
} catch (error) {
// _index.md 文件不存在,继续检查上级目录
}
currentDir = path.dirname(currentDir);
}
return false;
}
文件发现机制¶
使用 globby
库进行高效的文件匹配:
const patterns = [
'**/index.md', // 所有子目录下的 index.md
'**/_index.md' // 所有子目录下的 _index.md
];
const files = await globby(patterns, {
cwd: targetDirectory,
absolute: true,
onlyFiles: true
});
最佳实践¶
1. 目录组织建议¶
content/
├── zh/
│ ├── book/
│ │ ├── handbook1/
│ │ │ ├── _index.md # 书籍介绍
│ │ │ ├── chapter1/
│ │ │ │ ├── _index.md # 章节介绍
│ │ │ │ └── intro/
│ │ │ │ └── index.md # 具体内容
│ │ │ └── draft-content/
│ │ │ └── _index.md # draft: true
│ │ └── handbook2/
│ └── docs/
└── en/
└── book/
2. Draft 管理策略¶
- 开发阶段: 使用
draft: true
标记未完成的内容 - 章节级别: 在
_index.md
中使用cascade: {draft: true}
标记整个章节 - 发布准备: 移除 draft 标记前先本地测试
3. 批量处理建议¶
# 开发环境:只处理非 draft 内容
npm run ingest-directory -- ../../content/zh/book/ready-book
# 测试环境:可以临时移除 draft 标记进行测试
# (记得测试后恢复 draft 状态)
# 生产环境:确保所有内容都已移除 draft 标记
npm run ingest-directory -- ../../content/zh ../../content/en
错误处理和故障排除¶
常见问题¶
-
权限错误 - 文件访问被拒绝:
Error: EACCES: permission denied, open '/path/to/file'
解决方案: 检查文件权限,确保有读取权限
-
路径不存在 - 目录未找到:
❌ Failed to access directory /path: ENOENT: no such file or directory
解决方案: 验证路径是否正确,使用绝对路径或正确的相对路径
-
内存溢出(处理大量文件时) 解决方案: 分批处理目录,或增加 Node.js 内存限制:
NODE_OPTIONS="--max-old-space-size=4096" npm run ingest-directory -- /path
调试模式¶
设置环境变量以获得更详细的输出:
DEBUG=true npm run ingest-directory -- ../../content/zh/book
性能优化¶
处理大型目录的建议¶
- 分批处理: 将大型目录分解为较小的子目录
- 并发控制: 脚本内置了合理的并发限制
- 网络优化: 确保稳定的网络连接以避免上传失败
监控指标¶
- 处理速度: 正常情况下应该在 2-5 chunks/sec
- 失败率: 应该保持在 5% 以下
- 内存使用: 监控 Node.js 进程的内存占用
集成工作流¶
与 CI/CD 集成¶
# .github/workflows/content-update.yml
name: Update RAG Index
on:
push:
paths:
- 'content/**/*.md'
jobs:
update-index:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: '20'
- name: Update specific changed directories
run: |
cd tools/rag-worker
npm install
npm run ingest-directory -- ../../content/zh/book
env:
ADMIN_TOKEN: ${{ secrets.ADMIN_TOKEN }}
WORKER_URL: ${{ secrets.WORKER_URL }}
QWEN_API_KEY: ${{ secrets.QWEN_API_KEY }}
与 Hugo 部署集成¶
#!/bin/bash
# deploy.sh - 部署脚本示例
# 1. 构建 Hugo 站点
hugo --minify
# 2. 更新 RAG 索引
cd tools/rag-worker
npm run ingest-directory -- ../../content/zh ../../content/en
# 3. 部署到服务器
# rsync -av public/ user@server:/var/www/