pdf-craft OCR Backend 配置指南:六种 local/vendor 配置对象的字段、运行约束与源码实现

原创2026-10-03 17:33:58462 阅读
文章标签:AI 应用OCR

pdf-craft OCR Backend 配置指南:六种 local/vendor 配置对象的字段、运行约束与源码实现

pdf-craft 通过 PDFOptions(ocr=...) 接收 OCR 配置对象,在本地 GPU 与远程供应商服务之间选择识别引擎。本指南以 docs/zh-CN/OCR_BACKENDS.md 为主干,逐项讲解三种 local 与三种 vendor 配置对象的字段语义、默认值、代码示例与运行边界,并结合 pdf_craft/ocr_config.py、pdf_craft/pdf/ocr.py、pdf_craft/pdf/vendor_ocr.py 等源码揭示底层实现。读完你将能独立完成本机 GPU 或远程 OCR 的完整配置、模型预下载、离线运行、GPU 指定、并发控制与错误处理。

先决定运行位置:local OCR 还是 vendor OCR

pdf-craft 的 OCR backend 分成两类,二者在数据流、依赖与运行约束上有本质差异:

  • local OCR:模型下载到本地,并直接使用本机 NVIDIA GPU 完成识别。适合希望数据留在本机、已经具备 CUDA 环境,或需要离线运行的场景。依赖支持 CUDA 的 PyTorch、可用的 NVIDIA GPU、足够的显存、模型文件以及 Poppler(用于 PDF 页面渲染)。
  • vendor OCR:页面发送到远程供应商服务,由远端资源完成识别。适合没有 CUDA、不希望安装本地模型运行时,或希望把 GPU 运维交给供应商的场景。它需要网络连接、服务凭据,并会产生供应商侧的请求成本(按 token 或按任务计费)。

一次 OCR 运行只配置一个 backend。 模型归属与运行方式可以分开选择:DeepSeek OCR 和 DeepSeek OCR 2 来自 DeepSeek 系列模型,Unlimited OCR 来自百度。模型的下载位置(本机或远端)与模型本身是正交的两个维度,这正是下面六种配置对象存在的原因。

六种配置对象总览

库 API 通过 PDFOptions(ocr=...) 接收配置对象,不读取环境变量。也就是说,库层只认显式传入的配置对象,环境变量方式仅存在于 pdf_craft_tool 工具层(见下文“CLI 工具的环境变量映射”)。pdf_craft/__init__.py 将这六个类全部导出,可直接 from pdf_craft import ... 导入。

配置对象 模型 运行位置 适用条件
DeepSeekOCRLocalConfig DeepSeek OCR 本机 GPU 有 CUDA,使用 DeepSeek OCR 本地模型
DeepSeekOCR2LocalConfig DeepSeek OCR 2 本机 GPU 有 CUDA,使用 DeepSeek OCR 2 本地模型
UnlimitedOCRLocalConfig 百度 Unlimited OCR 本机 GPU 有 CUDA,使用 Unlimited OCR 本地模型
DeepSeekOCRVendorConfig DeepSeek OCR 远程服务 使用 OpenAI-compatible OCR endpoint
DeepSeekOCR2VendorConfig DeepSeek OCR 2 远程服务 使用 OpenAI-compatible OCR 2 endpoint
UnlimitedOCRVendorConfig 百度 Unlimited OCR 远程服务 使用百度 Unlimited OCR 服务

从源码看,这六个 dataclass 定义在 pdf_craft/ocr_config.py 中,并归纳为三个类型别名(同文件 L137-L143):LocalOCRConfig、VendorOCRConfig 与二者的并集 OCRConfig,同时还定义了 OCRMode 字面量类型,供工具层按字符串模式选择 backend。

下面的配置示例都可以传给 PDFCraft(pdf=PDFOptions(...)),再用于 convert_pdf_to_markdown、convert_pdf_to_epub 或 extract_pdf。以同步入口为例,convert_pdf_to_markdown("input.pdf", "output.md") 会把识别结果渲染为 Markdown;extract_pdf("input.pdf", "book.pcex", options) 则先产出可复用的中间格式 .pcex。

Local OCR 配置

三种 local 配置的公共字段

三种 local 配置对象字段完全相同(models_cache_path、local_only、enable_devices_numbers),定义于 pdf_craft/ocr_config.py:

字段 类型 默认值 作用
models_cache_path 路径或 None None 模型缓存目录;None 使用上游模型库的默认缓存位置
local_only bool False 只使用本地已有模型;设为 True 后禁止运行时下载
enable_devices_numbers 整数可迭代对象或 None None 指定可使用的 GPU 编号;None 使用默认设备映射

需要注意两个实现细节:

  • models_cache_path 传入后会经 to_path 解析为绝对 Path 对象(pdf_craft/ocr_config.py),enable_devices_numbers 会被冻结为不可变 tuple。测试 tests/test_ocr_config.py 验证了列表到元组的转换与不可变性。
  • 这三个类均为 frozen=True 的不可变 dataclass,配置对象构造后不可修改,这保证了配置在多线程/多进程复用时的安全性。

安装 local OCR 运行时

local OCR 需要额外安装本地模型运行时:

pip install "pdf-craft[local]"

此外还需要按照设备和操作系统安装支持 CUDA 的 PyTorch,并准备 NVIDIA 驱动、足够的显存和 Poppler。[local] extra 只提供本地 OCR 所需的 Python 运行时(如 Hugging Face、Transformers),不会替你选择或安装适合设备的 CUDA PyTorch wheel。安装后的自检可以运行:

python -c "import torch; print(torch.__version__); print('CUDA available:', torch.cuda.is_available())"

只有输出 CUDA available: True 才满足 local OCR 的运行前提;输出 False 时应改用 vendor OCR,或先修复 PyTorch、驱动与 CUDA 环境。Poppler 的安装与验证细节见 docs/zh-CN/INSTALLATION.md。

基本配置与离线模式

from pdf_craft import DeepSeekOCRLocalConfig, PDFCraft, PDFOptions

ocr = DeepSeekOCRLocalConfig(
    models_cache_path="models-cache",
)
craft = PDFCraft(pdf=PDFOptions(ocr=ocr))
craft.convert_pdf_to_markdown("input.pdf", "output.md")

local OCR 需要支持 CUDA 的 PyTorch、可用的 NVIDIA GPU、足够显存和模型文件。首次运行通常会下载模型;下载完成后,可以切换到离线模式:

ocr = DeepSeekOCRLocalConfig(
    models_cache_path="models-cache",
    local_only=True,
)

离线模式要求对应模型已经完整存在于缓存目录。缓存目录可以在多个运行中复用,但不同模型或不同来源的缓存应分开管理。local_only=True 不会替你下载缺失模型——它只把运行时行为限制为“只读已有缓存”,因此必须先完成下载再开启离线。

指定 GPU

在多 GPU 设备上,可以显式指定可用设备编号:

ocr = DeepSeekOCRLocalConfig(
    models_cache_path="models-cache",
    enable_devices_numbers=[0, 1],
)

这里的编号由上游 OCR 运行时(doc_page_extractor)解释,通常对应当前进程可见的 CUDA 设备。指定不存在或不可用的设备会在模型加载或 OCR 开始时失败。从测试 tests/test_ocr_config.py 可以看到,PageExtractorNode 会把 model_path(解析后的绝对路径)、local_only 与 enable_devices_numbers 原样透传给 create_deepseek_ocr_page_extractor / create_deepseek_ocr2_page_extractor 工厂函数,load_models() 随后加载对应模型。

三种 local 模型的 preset(ocr_size)

ocr_size 是提取选项(ExtractionOptions 的字段,默认 "gundam"),不是 OCR 配置对象字段。类型定义于 pdf_craft/pdf/types.py:DeepSeekOCRSize = Literal["tiny", "small", "base", "large", "gundam"]。可选值包括 tiny、small、base、large 和 gundam,但不同模型支持的 preset 不完全相同:

  • Unlimited OCR local 支持 base 和 gundam。
  • DeepSeek OCR 2 local 的已验证路径使用 base;显式指定 tiny 会在提取前快速失败。
  • 选择其他 backend 时,应以该 backend 的实际支持范围和模型资源为准。

其中“显式指定 tiny 会在提取前快速失败”由 pdf_craft/pdf/page_extractor.py 的 _validate_ocr_size 实现:当配置为 DeepSeekOCR2LocalConfig 且 ocr_size == "tiny" 时直接抛错,提示 deepseek-ocr2-local is not reliable with ocr_size='tiny',应使用 base 走已验证路径。tests/smoke/all_ocr_backends.json 也印证了这一约定:deepseek-ocr2-local 与 unlimited-ocr-local 两条冒烟用例均显式覆盖为 ocr_size: "base"。

例如,下面的完整示例使用 DeepSeek OCR local,并将 preset 设置为 base:

from pdf_craft import (
    DeepSeekOCRLocalConfig,
    ExtractionOptions,
    PDFCraft,
    PDFOptions,
)

craft = PDFCraft(pdf=PDFOptions(ocr=DeepSeekOCRLocalConfig(
    models_cache_path="models-cache",
)))
options = ExtractionOptions(ocr_size="base")
craft.extract_pdf("input.pdf", "book.pcex", options)

ExtractionOptions 的其余常用字段(见 pdf_craft/craft.py)还包括 page_indexes(只处理指定页码)、dpi(页面渲染 DPI,默认 300)、max_page_image_file_size、max_ocr_tokens / max_ocr_output_tokens(token 预算)、includes_footnotes(脚注二次识别)、ignore_pdf_errors / ignore_ocr_errors、aborted 与 on_ocr_event 回调等。

预下载模型

如果希望把模型下载和实际转换分开(例如在 CI 中先准备模型,运行时全程离线),可以使用 predownload_models:

from pdf_craft import DeepSeekOCRLocalConfig, predownload_models

predownload_models(
    ocr=DeepSeekOCRLocalConfig(models_cache_path="models-cache"),
    revision=None,
)

转换时使用同一个 models_cache_path,并在确认模型完整后设置 local_only=True。

从源码看,pdf_craft/functions.py 中的 predownload_models 会通过 ensure_ocr_config(ocr, models_cache_path, False) 规范化配置,构造 OCR 对象后调用 recognizer.predownload(revision);OCR.predownload(pdf_craft/pdf/ocr.py)最终委托给 PageExtractorNode.download_models。revision 参数可指定上游模型库的具体版本;None 表示使用默认版本。

Vendor OCR 配置

Vendor OCR 不要求本地 CUDA,但会把页面发送到远程服务。请根据供应商的 endpoint、凭据和模型文档填写配置;pdf-craft 只负责把配置传给对应的 OCR backend。库层不对供应商服务本身做代理或缓存,base_url、模型名及凭据错误会在请求阶段直接失败。

DeepSeek OCR 与 DeepSeek OCR 2

这两个 vendor 配置字段相同,定义于 pdf_craft/ocr_config.py:

字段 类型 默认值 作用
base_url str 必填 OpenAI-compatible 服务的基础 URL
api_key str 必填 服务访问密钥
model str 必填 服务端模型名
temperature float 或 None None 传给服务的采样温度
top_p float 或 None None 传给服务的 nucleus sampling 参数
max_tokens int 8000 单次请求的最大输出 token 数
timeout_seconds int 180 单次请求超时时间
retry_times int 2 可重试网络错误的重试次数
retry_interval_seconds float 1.0 供应商未给出等待时间时的重试间隔

实现层面的两个细节:

  • api_key 使用 field(repr=False),repr 不会泄露密钥;测试 tests/test_ocr_config.py 明确断言 repr 中不包含密钥字符串。
  • 所有 vendor 配置在 __post_init__ 中调用 _validate_vendor_retry(pdf_craft/ocr_config.py):retry_times < 0 或 retry_interval_seconds < 0 会直接抛 ValueError,把参数错误提前到构造阶段。
from pdf_craft import DeepSeekOCRVendorConfig, PDFCraft, PDFOptions

ocr = DeepSeekOCRVendorConfig(
    base_url="https://example.com/v1",
    api_key="your-api-key",
    model="deepseek-ocr",
    timeout_seconds=180,
)
craft = PDFCraft(pdf=PDFOptions(ocr=ocr))

使用 DeepSeek OCR 2 时,将配置对象和服务端模型名替换为 OCR 2 对应值:

from pdf_craft import DeepSeekOCR2VendorConfig

ocr = DeepSeekOCR2VendorConfig(
    base_url="https://example.com/v1",
    api_key="your-api-key",
    model="deepseek-ocr2",
)

请求构造位于 pdf_craft/pdf/vendor_ocr.py 的 _deepseek_payload:页面以 PNG base64 的 image_url 内容块形式发送,携带 max_tokens、stream: False,并仅在显式给出时才加入 temperature / top_p。请求 URL 通过 _chat_completions_url 规范化(同文件 L675-L679):base_url 以 /v1 结尾时拼接 /chat/completions,否则补 /v1/chat/completions。

百度 Unlimited OCR

Unlimited OCR vendor 配置使用百度服务凭据,定义于 pdf_craft/ocr_config.py:

字段 类型 默认值 作用
ak str 必填 百度 Access Key
sk str 必填 百度 Secret Key
base_url str https://aip.baidubce.com 百度服务基础 URL
poll_interval_seconds float 2.0 轮询异步任务的间隔
timeout_seconds int 180 OCR 任务超时时间
retry_times int 2 可重试网络错误的重试次数
retry_interval_seconds float 1.0 供应商未给出等待时间时的重试间隔

与 DeepSeek vendor 不同,ak / sk 均为必填且同样使用 field(repr=False) 隐藏凭据。Unlimited OCR 是异步任务模型:先提交任务拿 task_id,再轮询 query 直到 success,最后下载解析结果——这四个阶段在 pdf_craft/pdf/vendor_ocr.py 中依次实现,OAuth token 通过 base_url + "/oauth/2.0/token" 换取并缓存复用(同文件 L503-L545)。

from pdf_craft import (
    ConcurrentExecutor, FixedCapacity, UnlimitedOCRVendorConfig,
    PDFCraft, PDFOptions,
)

ocr = UnlimitedOCRVendorConfig(
    ak="your-access-key",
    sk="your-secret-key",
)
craft = PDFCraft(pdf=PDFOptions(
    ocr=ocr,
    ocr_executor=ConcurrentExecutor(FixedCapacity(8)),
))

ocr_executor 是 PDFOptions 的独立字段(pdf_craft/craft.py),用于控制 vendor 请求的并发容量。ConcurrentExecutor(FixedCapacity(8)) 表示最多 8 个远程 operation 并行。注意:只有 vendor 远程请求会占用该容量,PDF 渲染与后续处理仍保持串行(见 pdf_craft/pdf/ocr.py 的注释 “Keep PDF rendering serial while vendor page calls share async capacity”)。

CLI 工具的环境变量映射

库 API 本身不读环境变量,但配套的 pdf_craft_tool 工具层提供了环境变量到配置对象的映射(pdf_craft_tool/runtime.py)。主要变量如下:

  • PDF_CRAFT_OCR_MODE:backend 模式,取值与六种配置对象一一对应,如 deepseek-ocr-local、deepseek-ocr2-local、unlimited-ocr-local、deepseek-ocr-vendor、deepseek-ocr2-vendor、unlimited-ocr-vendor,默认 deepseek-ocr-local。
  • local 系列:PDF_CRAFT_*_MODELS_CACHE_PATH(默认 models-cache)、PDF_CRAFT_*_LOCAL_ONLY(工具层默认 True,与库层默认 False 不同)、PDF_CRAFT_*_ENABLE_DEVICES_NUMBERS。
  • DeepSeek vendor 系列:PDF_CRAFT_DEEPSEEK_OCR_BASE_URL、PDF_CRAFT_DEEPSEEK_OCR_API_KEY、PDF_CRAFT_DEEPSEEK_OCR_MODEL、PDF_CRAFT_DEEPSEEK_OCR_MAX_TOKENS(默认 8000)、PDF_CRAFT_DEEPSEEK_OCR_TIMEOUT_SECONDS(默认 180)等,OCR 2 使用 PDF_CRAFT_DEEPSEEK_OCR2_* 前缀。
  • Unlimited vendor 系列:PDF_CRAFT_UNLIMITED_OCR_ACCESS_KEY、PDF_CRAFT_UNLIMITED_OCR_SECRET_KEY、PDF_CRAFT_UNLIMITED_OCR_BASE_URL(默认 https://aip.baidubce.com)、PDF_CRAFT_UNLIMITED_OCR_POLL_INTERVAL_SECONDS(默认 2.0)。

工具层的冒烟配置(tests/smoke/all_ocr_backends.json)用同一份清单覆盖了全部六种 backend,可作为选择与验证配置的参考。

PDFOptions 的配置边界

PDFOptions 还提供 models_cache_path 和 local_only 两个便捷字段,用于在不显式传入 ocr 时创建默认的 DeepSeekOCRLocalConfig。如果已经传入了 ocr 配置对象,不能同时再传 models_cache_path 或 local_only,否则会抛出 ValueError。

该约束由 pdf_craft/ocr_config.py 的 ensure_ocr_config 实现:当 ocr 非空且 models_cache_path 或 local_only 任一非默认值时,抛出 ValueError("ocr cannot be combined with models_cache_path or local_only.")。测试 tests/test_ocr_config.py 通过 assertRaisesRegex(ValueError, "ocr cannot be combined") 验证了这一行为。

推荐明确选择一种写法,不要混用:

# 推荐:显式使用一个完整 OCR 配置对象。
PDFCraft(pdf=PDFOptions(ocr=DeepSeekOCRVendorConfig(
    base_url="https://example.com/v1",
    api_key="your-api-key",
    model="deepseek-ocr",
)))

# 便捷写法:不传 ocr 时,使用默认的 DeepSeek local OCR 配置。
PDFCraft(pdf=PDFOptions(
    models_cache_path="models-cache",
    local_only=True,
))

常见运行边界

以下是实际运行中需要重点注意的边界条件,每条都有对应的实现佐证:

  • 运行环境边界:local OCR 依赖本地 CUDA、GPU 显存和模型缓存;没有这些条件时选择 vendor OCR。vendor OCR 依赖网络和有效凭据,base_url、模型名及凭据错误会在请求阶段失败(见 _checked_response 对 4xx/5xx 的分类处理,pdf_craft/pdf/vendor_ocr.py)。
  • 离线模式的边界:local_only=True 不会替你下载缺失模型;请先完成模型下载。
  • 并发边界:vendor OCR 只把远程供应商请求交给 ocr_executor 并发;PDF 渲染与后续处理仍保持串行。
  • 重试与容量释放:retry 会先释放 ocr_executor 容量,等待结束后每次 HTTP 尝试重新申请容量。在 pdf_craft/pdf/vendor_ocr.py 中,_retry_request_after_error 在重试前先 asyncio.sleep(delay) 再重新走 _request_once,而 _request_once 内部的网络操作才通过 executor.run 占槽。延迟优先取 RateLimitedError.retry_after(即 HTTP Retry-After 头),否则用 retry_interval_seconds。
  • Unlimited OCR 的 operation 拆分:Unlimited OCR 的 token 获取、任务提交、每次轮询和结果下载分别算一个远程 operation;轮询间隔不占槽,查询或下载重试也不会重新提交页面任务。access token 失效时会刷新 token 后重试当前 operation(_UnlimitedTokenExpiredError 触发 token 失效与刷新,pdf_craft/pdf/vendor_ocr.py 与 L431-L435);供应商明确报告任务处理失败时,才会重新提交当前页面(_UnlimitedResubmitError,错误码 282000 触发,同文件 L882-L885)。
  • 限流与配额:HTTP 429 默认按限流重试(RateLimitedError);如果响应体明确表示余额或配额耗尽(_is_quota_response 识别 insufficient_quota、quota_exceeded、billing_not_active 等),则立即抛 OCRBillingError 并关闭共享执行器。余额、配额、认证(401/403)和权限错误不可忽略,其他页面错误仍可交给 ignore_ocr_errors。回调收到的 OCRError 会在异常 cause 链中保留供应商原始异常及可用的 HTTP response。Unlimited OCR query 以 task_error 报告“额度不够”时同样属于不可忽略的配额终止错误(_raise_unlimited_task_error 对 额度不够/额度不足/配额/余额不足/quota 标记的处理,同文件 L889-L911)。
  • token 预算:设置任一累计 OCR token 预算(max_ocr_tokens / max_ocr_output_tokens)后,vendor 请求会逐页结算并将精确的剩余预算传给下一页;预算耗尽即抛 TokenLimitError(pdf_craft/pdf/ocr.py)。local OCR 路径同样逐页扣减剩余预算(同文件 L632-L633、L764-L769)。
  • preset 与 backend 的关系:ocr_size 由提取选项控制,不能用来改变 vendor/local backend;backend 由 OCR 配置对象决定。
  • 页面级断点续跑:识别结果按页写入 page_N.xml,完成页在 done 文件标记;存在 .failed 文件或缺失页时会在下次运行继续补齐,page_pixel_sizes.json 保存每页几何信息以支持缓存命中(pdf_craft/pdf/ocr.py)。
  • 翻译与 OCR 的独立性:翻译所需的文本 LLM 与 OCR backend 是两套独立配置。OCR 只负责把页面图像变成结构化文本,翻译由章节级 transformer 调用独立的 LLM 完成,本指南不讨论翻译配置。

小结

pdf-craft 的 OCR backend 设计把“模型选择”与“运行位置”解耦为六种显式配置对象:local 三件套共享缓存目录、离线开关与 GPU 编号字段,vendor 三件套分别面向 OpenAI-compatible endpoint 与百度异步任务接口。库层 API 通过 PDFOptions(ocr=...) 显式传参、不读环境变量,且用 ensure_ocr_config 强制“显式配置与便捷字段二选一”;工具层才提供 PDF_CRAFT_OCR_MODE 等环境变量映射。掌握每个字段的默认值、ocr_size preset 约束、并发/重试/配额边界,就能根据本机硬件与供应商资源做出正确的 backend 选择,并写出可复现、可离线、可断点续跑的 OCR 流程。

登录后查看全文
pdf-craft