从PDF到RAG知识库

打造可配置、可扩展的自动化预处理流水线:从PDF到RAG知识库

在构建RAG(检索增强生成)系统的过程中,文档预处理是决定检索质量的基础环节。然而,面对多源异构文档(如PDF、图片、扫描件),手动处理不仅效率低下,还难以保证一致性和可维护性。为此,我们需要构建一条自动化预处理流水线 ,将OCR识别、文本清洗、分段、元数据绑定、知识库上传等步骤串联起来,并具备可配置可扩展的能力,从而适应不同业务场景和未来需求的变化。

本文将以一个实际案例为主线,展示如何从PDF(或图片)开始,经过PaddleOCR提取文本、预处理分段、上传至Dify知识库并绑定元数据,最终实现全流程自动化。我们将重点介绍配置驱动插件化扩展的设计思想,并提供核心代码片段,帮助你快速搭建自己的RAG预处理流水线。


一、整体架构概览

整个预处理流水线的流程如下:

text

复制代码
PDF/图片 → [PaddleOCR API] → 提取纯文本 → [预处理/分段] → [Dify API] → 知识库同步 → [元数据绑定]

所有模块的参数均通过一个集中配置文件(config.yaml) 管理,保证修改灵活;同时支持插件化 新增预处理规则,并通过Celery实现异步任务队列,满足大规模文档处理需求。


二、核心模块实现

1. OCR文本提取模块(支持多引擎切换)

我们封装了一个 OCREngine 类,通过配置文件指定使用的OCR引擎(如PaddleOCR)及其API地址。这样可以轻松替换为其他OCR服务。

python

python 复制代码
import requests

class OCREngine:
    def __init__(self, config):
        self.api_url = config["ocr"]["api_url"]          # 从配置加载OCR API地址
        self.high_precision = config["ocr"].get("high_precision", False)

    def extract_text(self, file_path):
        """调用OCR API提取文本,返回JSON格式结果"""
        with open(file_path, "rb") as f:
            files = {"file": f}
            response = requests.post(self.api_url, files=files)
        if response.status_code == 200:
            return response.json()          # 包含识别出的文本及其他信息
        else:
            raise Exception(f"OCR提取失败: {response.text}")

设计要点

  • 通过配置文件指定API地址,支持切换本地部署或云服务。

  • 返回的JSON包含完整信息,便于下游模块进一步筛选(如只取文本字段)。

2. 文本预处理与分段模块

该模块负责清洗OCR结果,并按规则进行分段。我们将其设计为插件式,方便新增预处理规则(如去空格、去URL、去特殊字符等)。

python

python 复制代码
import re

class TextProcessor:
    def __init__(self, config):
        self.config = config
        self.rules = config["dify"]["process_rules"]
        # 插件列表,可动态加载
        self.plugins = [
            RemoveExtraSpaces(enabled=True),
            RemoveSpecialChars(enabled=True),
            # 后续可扩展
        ]

    def preprocess(self, text):
        """应用所有启用的预处理插件"""
        for plugin in self.plugins:
            if plugin.enabled:
                text = plugin.apply(text)
        return text

    def segment(self, text):
        """根据配置的分隔符切分文本"""
        separator = self.rules["segmentation"]["separator"]
        # 简单的分隔符分割,实际可用更复杂的逻辑
        return re.split(separator, text)

# 示例插件:去除多余空格
class RemoveExtraSpaces:
    def __init__(self, enabled=True):
        self.enabled = enabled

    def apply(self, text):
        return re.sub(r"\s+", " ", text).strip()

# 示例插件:去除特殊字符
class RemoveSpecialChars:
    def __init__(self, enabled=True):
        self.enabled = enabled

    def apply(self, text):
        return re.sub(r"[^\w\s]", "", text)

设计要点

  • 预处理规则通过插件类实现,可灵活开关和组合。

  • 分段规则(如分隔符、最大token数)由配置文件定义,便于调整。

3. 上传至Dify知识库模块

封装Dify API,支持通过文本直接创建文档,并可指定分段规则(复用配置中的规则)。

python

python 复制代码
import requests
import json

class DifyUploader:
    def __init__(self, config):
        self.api_key = config["dify"]["api_key"]
        self.dataset_id = config["dify"]["dataset_id"]
        self.base_url = f"https://api.dify.ai/v1/datasets/{self.dataset_id}"
        self.rules = config["dify"]["process_rules"]

    def upload_by_text(self, segments):
        """将分段后的文本列表上传为Dify文档"""
        headers = {
            "Authorization": f"Bearer {self.api_key}",
            "Content-Type": "application/json"
        }
        document_ids = []
        for i, content in enumerate(segments):
            payload = {
                "name": f"segment_{i}.txt",
                "text": content,
                "indexing_technique": "high_quality",
                "process_rule": self.rules   # 复用配置中的分段规则
            }
            response = requests.post(
                f"{self.base_url}/document/create_by_text",
                headers=headers,
                data=json.dumps(payload)
            )
            if response.status_code == 200:
                doc_id = response.json()["document"]["id"]
                document_ids.append(doc_id)
                print(f"Segment {i} uploaded, ID: {doc_id}")
            else:
                print(f"Upload failed: {response.text}")
        return document_ids

设计要点

  • 使用Dify官方API,支持通过文本创建文档。

  • 分段规则直接取自配置,保证与预处理阶段一致。

4. 动态元数据绑定模块

元数据可以帮助后续检索时进行过滤或溯源。我们通过配置定义元数据字段及其值来源(如固定值、文件名等)。

python

python 复制代码
class MetadataManager:
    def __init__(self, config):
        self.api_key = config["dify"]["api_key"]
        self.dataset_id = config["dify"]["dataset_id"]
        self.base_url = f"https://api.dify.ai/v1/datasets/{self.dataset_id}"
        self.metadata_fields = config["metadata"]["fields"]

    def bind_metadata(self, document_id, filename):
        """为指定文档绑定元数据"""
        url = f"{self.base_url}/documents/{document_id}/metadata"
        headers = {"Authorization": f"Bearer {self.api_key}", "Content-Type": "application/json"}

        metadata_list = []
        for field in self.metadata_fields:
            value = field["value"]
            if field.get("value_from") == "filename":
                value = filename   # 从文件名动态取值
            metadata_list.append({
                "name": field["name"],
                "value": value
            })

        payload = {"metadata": metadata_list}
        response = requests.post(url, headers=headers, data=json.dumps(payload))
        if response.status_code == 200:
            print(f"Metadata bound for document {document_id}")
        else:
            print(f"Metadata binding failed: {response.text}")

设计要点

  • 支持从文件名、固定值等来源动态生成元数据。

  • 调用Dify的元数据绑定API,将字段与文档关联。


三、可配置设计:集中管理参数

所有模块的配置统一存放在 config.yaml 文件中,示例如下:

yaml

复制代码
ocr:
  api_url: "http://localhost:8080/ocr"   # PaddleOCR API地址
  high_precision: true

dify:
  api_key: "your_api_key"
  dataset_id: "your_dataset_id"
  process_rules:
    pre_processing:
      - id: "remove_extra_spaces"
        enabled: true
    segmentation:
      separator: "\n\n"                  # 按段落分隔
      max_tokens: 500

metadata:
  fields:
    - name: "author"
      value: "default_author"
    - name: "source"
      value_from: "filename"             # 从文件名提取

在Python中加载配置:

python

python 复制代码
import yaml

def load_config():
    with open("config.yaml", "r", encoding="utf-8") as f:
        return yaml.safe_load(f)

通过这种方式,修改OCR地址、切换预处理规则、调整分段参数等均无需改动代码,只需更新配置文件。


四、可扩展设计:插件与异步任务

1. 插件化预处理规则

如前所述,我们将每个预处理操作封装为独立的插件类,在 TextProcessor 中统一调用。新增规则时,只需创建新的插件类并添加到插件列表即可,无需修改核心逻辑。

2. 异步任务支持(Celery)

当需要批量处理大量文档时,同步处理会阻塞主流程。我们可以引入Celery将任务异步化,提高吞吐量。

python

python 复制代码
from celery import Celery

app = Celery('tasks', broker='redis://localhost:6379/0')

@app.task
def process_pdf_async(pdf_path):
    config = load_config()

    # 1. OCR提取
    ocr_engine = OCREngine(config)
    raw_result = ocr_engine.extract_text(pdf_path)
    raw_text = raw_result.get("text", "")   # 假设返回JSON中有text字段

    # 2. 预处理与分段
    processor = TextProcessor(config)
    cleaned_text = processor.preprocess(raw_text)
    segments = processor.segment(cleaned_text)

    # 3. 上传至Dify
    uploader = DifyUploader(config)
    doc_ids = uploader.upload_by_text(segments)

    # 4. 绑定元数据
    metadata_mgr = MetadataManager(config)
    for doc_id in doc_ids:
        metadata_mgr.bind_metadata(doc_id, pdf_path)

通过Celery,我们可以将处理任务放入队列,由worker异步执行,支持定时调度(如每天凌晨处理新增文件)。


五、完整流程演示

将上述模块串联起来,main.py 如下:

python

python 复制代码
def main(pdf_path):
    config = load_config()

    # 1. OCR提取
    ocr_engine = OCREngine(config)
    raw_result = ocr_engine.extract_text(pdf_path)
    raw_text = raw_result["text"]   # 假设返回JSON中包含text字段

    # 2. 预处理与分段
    processor = TextProcessor(config)
    cleaned_text = processor.preprocess(raw_text)
    segments = processor.segment(cleaned_text)

    # 3. 上传至Dify
    uploader = DifyUploader(config)
    doc_ids = uploader.upload_by_text(segments)

    # 4. 绑定元数据
    metadata_mgr = MetadataManager(config)
    for doc_id in doc_ids:
        metadata_mgr.bind_metadata(doc_id, pdf_path)

if __name__ == "__main__":
    main("example.png")   # 支持PDF或图片

执行后,你将看到类似如下的输出:

  • OCR提取完成,得到文本。

  • 文本被预处理并分为若干段。

  • 各段依次上传至Dify知识库,返回文档ID。

  • 元数据成功绑定。

在Dify后台刷新,即可看到新创建的文档及其元数据。


六、总结与最佳实践

通过本文的实践,我们构建了一条可配置、可扩展的自动化预处理流水线,其核心设计思想值得借鉴:

  1. 配置驱动:将所有可变参数外置到YAML文件,避免硬编码,便于调整和维护。

  2. 模块化封装:将OCR、预处理、上传、元数据等独立为类,职责清晰,便于替换和升级。

  3. 插件化扩展:预处理规则以插件形式组织,新增功能只需添加新插件,无需修改原有代码。

  4. 异步任务:结合Celery实现高并发处理,支持大规模文档批量导入。

  5. 元数据动态绑定:通过配置定义元数据来源,增强文档的可管理性和检索能力。

这套流水线不仅适用于Dify,稍作修改即可对接其他RAG平台(如RAGFlow、LlamaIndex等)。它解放了人工操作,让RAG系统的数据接入变得高效、规范、可持续。

未来,你还可以在此基础上增加更多功能,如多模态OCR、自动语言检测、自定义分块算法等,使其成为企业级RAG预处理的标准组件。

相关推荐
Lw中2 小时前
RAG如何统一多源文档格式?
rag·多源文档处理
想摆烂的不会研究的研究生2 小时前
RAG入门指南:检索增强生成全解析
人工智能·agent·rag
Lw中3 小时前
RAG切片语义割裂怎么办?
python·rag文本分割·大模型应用基础
winfredzhang4 小时前
Python 从零打造桌面文件管理工具开发全过程实录与源码深度解析
python·pdf·视频·图片·docx·xlsx
醇氧5 小时前
PowerPoint 批量转换为 PDF
java·spring boot·spring·pdf·powerpoint
2501_930707785 小时前
使用 C# 将 PDF 转换为 PCL
pdf
spencer_tseng6 小时前
com.sun.pdfview.PDFParseException: Unknown pattern type 2
pdf·type 2
诸葛大钢铁6 小时前
如何将NH转为PDF?在线将NH/CAJ/KDH等格式文件转PDF
经验分享·pdf·caj·nh转pdf
飞乐鸟14 小时前
Github 3.3k Star!一款开源的PDF工具包!
pdf·github