首页
/ Docling 插件系统深度解析:基于 pluggy 扩展 OCR、布局与表格结构引擎

Docling 插件系统深度解析:基于 pluggy 扩展 OCR、布局与表格结构引擎

2026-09-04 13:49:24作者:齐添朝

Docling 通过基于 pluggy 与 setuptools entrypoint 的插件机制,允许第三方在不改动主包的前提下向转换管线注入新的 OCR 引擎、布局引擎与表格结构识别引擎。本文完整讲解插件的注册方式(覆盖 pyproject.toml、poetry、setup.cfg、setup.py 四种打包配置)、三类工厂(factory)约定的接口与继承要求、allow_external_plugins 安全开关的源码实现,以及 docling CLI 中 --allow-external-plugins--show-external-plugins 等参数的用法,带你掌握从打包发布到运行时选型的完整插件开发链路。

插件加载机制:pluggy + setuptools entrypoint

Docling 的插件体系建立在一套两层机制之上:

  1. 插件发现层:Docling 使用 pluggyPluginManager 扫描已安装包在 setuptools 中声明的、组名为 docling 的 entrypoint,每个 entrypoint 指向一个 Python 模块;
  2. 插件注册层:每个被发现的模块内定义一个与工厂同名的可调用函数(如 ocr_engines()layout_engines()table_structure_engines()),返回包含引擎类列表的字典,由对应的 BaseFactory 子类完成登记。

插件发现的核心实现位于 BaseFactory.load_from_plugins

def load_from_plugins(
    self, plugin_name: Optional[str] = None, allow_external_plugins: bool = False
):
    plugin_name = plugin_name or self.plugin_name

    plugin_manager = PluginManager(plugin_name)
    plugin_manager.load_setuptools_entrypoints(plugin_name)

    for plugin_name, plugin_module in plugin_manager.list_name_plugin():
        plugin_module_name = str(plugin_module.__name__)

        if not allow_external_plugins and not plugin_module_name.startswith(
            "docling."
        ):
            logger.warning(
                f"The plugin {plugin_name} will not be loaded because Docling "
                "is being executed with allow_external_plugins=false."
            )
            continue

        attr = getattr(plugin_module, self.plugin_attr_name, None)

        if callable(attr):
            config = attr()
            self.process_plugin(config, plugin_name, plugin_module_name)

从源码结构看,加载流程可以归纳为四步:

  • PluginManager("docling") 创建以 docling 为组名的插件管理器,load_setuptools_entrypoints("docling") 从已安装包的 entrypoint 元数据中加载插件模块;
  • 逐一对每个 (插件名, 模块)外部插件过滤:模块名不以 docling. 开头且未开启 allow_external_plugins 时直接跳过(仅打印 warning),这是第三方插件默认不可见的原因;
  • getattr(plugin_module, self.plugin_attr_name) 在模块上取与工厂匹配的函数(OCR 工厂取 ocr_engines,布局工厂取 layout_engines,表格工厂取 table_structure_engines);
  • 若该属性可调用,则执行它拿到配置字典,交由 process_plugin 把列表中的每个类调用 register 登记。

登记逻辑 BaseFactory.registercls.get_options_type()(即引擎类声明的配置类)为键存入 _classes,并记录 FactoryMeta(kind, plugin_name, module) 元信息;若同一配置类已被注册则抛出 ValueError,外层 process_plugin 捕获后仅告警 %r already registered,因此插件名在生态内必须唯一、同一 kind 只允许存在一个引擎实现。工厂还通过 get_enum 用已注册的 kind 动态生成字符串枚举(如 OcrEngine),create_instance 则按 type(options) 查找引擎类并实例化——这就是"选项类决定用哪个引擎"的绑定方式。

Docling 主包自带的默认插件集中定义在 docling/models/plugins/defaults.py,是编写第三方插件时最好的参考起点:

def ocr_engines():
    from docling.models.stages.ocr.auto_ocr_model import OcrAutoModel
    # ... Tesseract、EasyOCR、RapidOCR、Nemotron、KserveV2、macOS Vision 等
    return {
        "ocr_engines": [
            OcrAutoModel,
            EasyOcrModel,
            # ...
        ]
    }

声明插件:entrypoint 的四种打包写法

entrypoint 组名固定为 docling,值为你包内负责插件注册的模块(dotted path)。your_plugin_name 是插件名,必须在全 Docling 生态中唯一;your_package.module 指向定义注册函数的模块。不同打包系统的声明方式如下。

pyproject.toml(PEP 621 / setuptools):

[project.entry-points."docling"]
your_plugin_name = "your_package.module"

poetry v1 的 pyproject.toml:

[tool.poetry.plugins."docling"]
your_plugin_name = "your_package.module"

setup.cfg:

[options.entry_points]
docling =
    your_plugin_name = your_package.module

setup.py:

from setuptools import setup

setup(
    # ...,
    entry_points = {
        'docling': [
            'your_plugin_name = "your_package.module"'
        ]
    }
)

无论使用哪种写法,最终效果一致:包安装后,pkg_resources/importlib.metadata 都能在 docling 组下找到 your_plugin_name -> your_package.module 的映射,供 PluginManager.load_setuptools_entrypoints 发现。

插件工厂与接口约定

Docling 当前提供三类可插拔工厂,各自封装在 docling/models/factories/ 目录中,均继承自 BaseFactory 并在构造时传入 plugin_attr_name(即插件模块中注册函数的名字):

  • OcrFactorysuper().__init__("ocr_engines"),面向 BaseOcrModel
  • LayoutFactorysuper().__init__("layout_engines"),面向 BaseLayoutModel
  • TableStructureFactorysuper().__init__("table_structure_engines"),面向 BaseTableStructureModel

工厂实例通过 docling/models/factories/init.py 中的 get_ocr_factory()get_layout_factory()get_table_structure_factory() 等函数创建,并用 @lru_cacheallow_external_plugins 取值缓存——这意味着同一进程内开关状态变化时,需以不同参数调用才会触发重新加载。

OCR 工厂

OCR 工厂允许向 Docling 用户提供更多 OCR 引擎。插件模块 your_package.module 中的注册代码形如:

# Factory registration
def ocr_engines():
    return {
        "ocr_engines": [
            YourOcrModel,
        ]
    }

其中 YourOcrModel 必须:

  1. 实现 BaseOcrModel(抽象页面处理模型,其 __call__(conv_res, page_batch) 接收转换结果与页面批次并返回处理后的页面);
  2. 提供从 OcrOptions 派生的选项类,并通过类方法 get_options_type() 暴露该配置类型(该协议定义在 BaseModelWithOptionsget_options_type() 返回类型 + 以 options 关键字参数构造)。

OcrOptions 携带 kind 字段(如 tesseract、easyocr 等),工厂即以 kind 生成引擎枚举成员,用户在管线中通过选项类选择具体引擎。

Layout 引擎工厂

布局引擎工厂用于提供新的版面分析引擎:

# Factory registration
def layout_engines():
    return {
        "layout_engines": [
            YourLayoutModel,
        ]
    }

YourLayoutModel 必须实现 BaseLayoutModel,并提供从 BaseLayoutOptions 派生的选项类。默认插件(defaults.pylayout_engines())注册了 LayoutModelLayoutObjectDetectionModel 以及实验性的 TableCropsLayoutModel

Table structure 引擎工厂

表格结构工厂用于提供新的表格结构识别引擎:

# Factory registration
def table_structure_engines():
    return {
        "table_structure_engines": [
            YourTableStructureModel,
        ]
    }

YourTableStructureModel 必须实现 BaseTableStructureModel,并提供从 BaseTableStructureOptions 派生的选项类。默认实现包括 TableStructureModelTableStructureModelV2GraniteVisionTableStructureModel

此外,从 defaults.py 的源码结构看,插件注册函数并非只有文档所述三类——同一机制还驱动着 picture_description() 工厂(PictureDescriptionFactory,注册函数名为 picture_description),说明该插件框架是通用可扩展的,新增一类引擎只需新增一个 BaseFactory 子类与对应的注册函数约定。

启用第三方插件:allow_external_plugins

出于安全与可预测性考虑,非 docling 包自身的插件默认不会加载(见上文 load_from_pluginsstartswith("docling.") 的过滤逻辑)。第三方插件必须通过 allow_external_plugins 显式开启。该选项声明在 PdfPipelineOptions 中:

from docling.datamodel.base_models import InputFormat
from docling.datamodel.pipeline_options import PdfPipelineOptions
from docling.document_converter import DocumentConverter, PdfFormatOption

pipeline_options = PdfPipelineOptions()
pipeline_options.allow_external_plugins = True  # <-- enable external plugins
pipeline_options.ocr_options = YourOptions  # <-- your OCR options here
pipeline_options.layout_options = YourLayoutOptions  # <-- your layout options here
pipeline_options.table_structure_options = YourTableStructureOptions  # <-- your table structure options here

doc_converter = DocumentConverter(
    format_options={
        InputFormat.PDF: PdfFormatOption(
            pipeline_options=pipeline_options
        )
    }
)

使用要点:

  • allow_external_plugins = True 只是解除过滤开关,具体选用哪个引擎仍由 ocr_options / layout_options / table_structure_options 传入的选项实例决定,选项类的 kind 必须与插件注册时的配置类一致,否则 BaseFactory.create_instance 会抛出 RuntimeError 并列出全部已知 class 供排查(见 _err_msg_on_class_not_found);
  • 三个选项位对应三个工厂,可只启用其一,例如仅替换 OCR 引擎而沿用默认布局与表格结构实现。

使用 docling CLI

CLI 侧同样需要先开启外部插件才能选择新引擎,相关参数定义在 docling/cli/main.pyallow_external_pluginsshow_external_plugins 两个选项):

# Show the external plugins
docling --show-external-plugins

# Run docling with a custom OCR engine
docling --allow-external-plugins --ocr-engine=NAME

# Run docling with a custom layout engine
docling --allow-external-plugins --layout-engine=NAME

# Run docling with a custom table structure engine
docling --allow-external-plugins --table-structure-engine=NAME

其中 --show-external-plugins 的实现 show_external_plugins_callback 会分别以 allow_external_plugins=True 获取 OCR、布局、表格结构三个工厂并打印各自 registered_kind(即所有可见引擎的 kind 列表),而 --ocr-engine--layout-engine--table-structure-engine 传入的 NAME 正是各工厂枚举成员名(也就是选项类的 kind)。CLI 内部在默认路径下仍以 allow_external_plugins=False 构建工厂(见 main.py),仅在用户显式传参后才切换,行为与 API 路径完全对称。

小结与开发检查清单

结合文档约定与 base_factory.py 的源码实现,开发并验证一个 Docling 引擎插件可对照以下清单:

  1. 接口:引擎类继承对应基类(BaseOcrModel / BaseLayoutModel / BaseTableStructureModel),选项类继承对应 *Options 并定义唯一 kind
  2. 注册:模块内提供与工厂同名的注册函数,返回 {"<工厂键>": [引擎类]} 字典,不要注册已被占用的 kind(会被静默跳过并告警);
  3. 打包:在 entrypoint 的 docling 组声明插件,插件名保持生态内唯一;
  4. 验证docling --show-external-plugins 确认 kind 出现在列表中,再用 --allow-external-plugins --<engine>=NAME 跑通转换;
  5. 参考实现:以 docling/models/plugins/defaults.py 中 Tesseract、EasyOCR 等内置引擎的注册方式作为模板。

需要说明的适用前提:以上机制与参数均以当前仓库版本为准,插件发现依赖包已通过 pip 等标准方式安装(entrypoint 元数据可用);外部插件加载始终需要用户显式授权(allow_external_plugins/--allow-external-plugins),未开启时任何第三方引擎都不会进入工厂注册表。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.83 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
987
506
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384