首页
/ Docling Service Client 深度解析:将文档转换卸载到 docling-serve 远程服务的完整指南

Docling Service Client 深度解析:将文档转换卸载到 docling-serve 远程服务的完整指南

2026-09-06 20:17:01作者:卓艾滢Kingsley

Docling Service Client(docling.service_client)是 Docling 提供的客户端 SDK,它把文档解析与转换工作交给远程运行的 docling-serve 服务执行,而不是在本地加载任何模型。读完本文,你将掌握 Service Client 的安装与配置、单文档/并发/分块等核心 API、异步与 Job 生命周期管理、docling convert-remote 命令行用法,并能结合仓库源码理解其重试、结果目标(result targets)与错误处理的底层机制。

Service Client 是什么,何时使用

Service Client 的核心价值在于把推理从"调用方机器"剥离出去,典型适用场景有三类:

  • 低延迟与规模:服务端保持模型常驻(warm)状态,并可并发处理大量文档,免去每次冷启动加载模型的开销;
  • 零本地 ML 依赖:调用机器不需要 torch、OCR 引擎或 GPU,只需安装 docling-slim[service-client] 这个轻量 extra;
  • 托管运维:只需一个服务 URL 和 API Key 即可开工。

从接口设计看,Service Client 与本地 DocumentConverter 保持相似的调用形态(同样是"给来源、拿结果、导出 Markdown/JSON"),因此从本地 SDK 迁移的代码基本可以平移。

服务从哪里来:自托管或托管

你永远是针对一个已在运行的 docling-serve 实例进行转换。获取实例有两条路:

  1. 自托管 docling-serve:自行运行开源 API 服务器(容器化部署在自己的基础设施上),获得完全控制权和自有硬件;
  2. 托管服务:由提供方替你运行 docling-serve,你只需要服务 URL 和 API Key。托管方案适合不想运维基础设施、不想配置 GPU 的场景。

无论哪种,客户端代码完全一致——区别仅在 URL 与 API Key。

安装

pip install "docling-slim[service-client]"
# 完整 docling 包也已包含该 extra

仓库中的 docling-slim 包 定义了该 extra 的依赖闭包。命令行侧,cli/main.py 只有在检测到 service-client 依赖可用时才会注册 convert-remote 子命令,否则跳过注册并保留其余子命令可导入——这意味着不装该 extra 时 docling convert-remote 不会出现,但 docling convert 不受影响。

配置:环境变量与 .env

客户端库与 docling convert-remote CLI 读取同一套配置,来源为环境变量或工作目录下的 .env 文件:

DOCLING_SERVICE_URL=https://your-docling-service.example.com
DOCLING_SERVICE_API_KEY=your-api-key    # 服务无鉴权时可省略

CLI 的凭证解析优先级为:命令行 flag > 环境变量 > .env 文件.env 的加载实现在 cli/remote.py_load_dotenv() 中:它用 python-dotenvload_dotenv(find_dotenv(usecwd=True), override=False)用户当前工作目录查找 .env,且 override=False 保证真实环境变量始终优先于 .env 中的值;python-dotenv 未安装时该步骤静默跳过,属于 best-effort 行为。

转换单个文档

最小可用示例:

from docling.service_client import DoclingServiceClient

client = DoclingServiceClient(url=..., api_key=...)   # 或依赖 env / .env
result = client.convert(source="path/to/report.pdf")  # 本地路径或 http(s) URL
print(result.document.export_to_markdown())

默认行为(启用 OCR、表格结构分析、Markdown 输出)与本地 DocumentConverter 一致,仅在需要时用 ConvertDocumentsOptions 覆盖:

from docling.datamodel.service.options import ConvertDocumentsOptions

result = client.convert(
    source="report.pdf",
    options=ConvertDocumentsOptions(...),   # 每个请求的 pipeline 覆盖
)

convert() 的完整参数(来自源码签名)

结合 service_client/client.py 中的定义,convert() 的签名为:

def convert(
    self,
    source: SourceType,                    # Path | str | DocumentStream | HttpSourceRequest
    headers: dict[str, str] | None = None,
    max_num_pages: int | None = None,      # 客户端级页数限制(preflight 校验)
    max_file_size: int | None = None,     # 客户端级文件大小限制(字节)
    page_range: PageRange | None = None,
    options: ConvertDocumentsRequestOptions | None = None,
    raises_on_error: bool = True,
) -> ConversionResult

几个值得注意的实现细节:

  • 来源归一化:字符串若可解析为合法 http(s) URL,会被包成 HttpSourceRequest 直接发给服务(服务端自行拉取,客户端不下载);否则按本地 Path 走 multipart 上传。DocumentStream(内存流)同样受支持。
  • options 合并语义:从源码 _resolve_options() 看,客户端只覆盖调用方显式设置的字段(基于 model_fields_set),其余保留客户端默认值;且显式设为 None 可以清除客户端默认值。page_range/max_num_pages/max_file_size 会合成 DocumentLimits 用于本地 preflight——例如文件超过 max_file_size 时,客户端在发送前就返回一个 SKIPPED 状态的失败结果,不浪费一次网络往返。
  • 返回与异常:默认 raises_on_error=True 时,状态非 SUCCESS/PARTIAL_SUCCESS 的结果会抛出 ConversionError;置为 False 则直接返回带 statuserrorsConversionResult,方便批量场景自行判定。

客户端构造参数与默认值

DoclingServiceClient 构造函数(service_client/client.py)提供了一组可调的运行参数,源码中的默认值如下:

参数 默认值 说明
url 必填 服务基地址,必须是绝对 http(s) URL,且不能带 /v1 后缀
api_key "" 通过 X-Api-Key 请求头与 WebSocket 查询参数发送
options None 客户端级默认 ConvertDocumentsOptions(深拷贝保存)
status_watcher WEBSOCKET 任务状态跟踪方式:websocketpolling
ws_fallback_to_poll True WebSocket 失败时回退到轮询
poll_server_wait 5.0 轮询时服务端侧等待时长
poll_client_interval 同上 客户端两次轮询的间隔,未设置时等于 poll_server_wait
job_timeout 300.0 等待单个任务完成的客户端超时
max_concurrency 8 并发上限(convert_all 使用);硬上限为 512
http_retries 3 可重试错误的重试次数
http_connect_timeout / http_read_timeout 10.0 / 60.0 HTTP 超时
artifact_download_timeout 60.0 预签名产物下载超时
max_artifact_download_bytes 512 MiB 产物下载体积上限(流式累计校验)

此外,客户端在每次请求中附带 Accept-Docling-Document-Version 头,告知服务端本端 docling-core 可读取的最新 DoclingDocument 版本,服务端需要时会向下投影兼容——这是客户端与服务端版本解耦的关键机制。

并发转换多个文档

for result in client.convert_all(
    source=["a.pdf", "b.pdf", "https://example.com/c.pdf"],
    max_concurrency=4,
):
    print(result.input.file.name, result.status)

convert_all() 接收可迭代来源集合(本地路径、内存流、http(s) URL 混合),逐个产出 ConversionResult。其底层实现(_convert_all_async())值得理解:

  • 同步版本内部构造了一个私有事件循环,把并发扇出委托给原生异步客户端 submit_and_retrieve_each(),因此无额外线程开销;
  • max_concurrency 控制同时在途的任务数,默认取客户端级的 max_concurrency(默认 8),单次调用可用参数覆盖,取值必须在 1..512 之间;
  • 结果按输入顺序产出(ordered=True):慢的任务会"占位",前面的结果先补齐再 yield;
  • 单个来源失败不会中断整个批次——失败项被构造为带错误信息的 FAILURE 状态 ConversionResult,其余文档继续处理。

远程分块(RAG 场景)

from docling.service_client import ChunkerKind

response = client.chunk(source="report.pdf", chunker=ChunkerKind.HYBRID)
# 可选:ChunkerKind.HYBRID 或 ChunkerKind.HIERARCHICAL

chunk() 是"提交任务 + 阻塞取结果"的组合糖:它内部调用 submit_chunk() 获得任务句柄,再以客户端 job_timeout 等待并取回 ChunkDocumentResponse。从 client.py_submit_chunk_task() 看,两种 chunker 分别对应服务端的 /v1/chunk/hybrid/{file,source}/async/v1/chunk/hierarchical/{file,source}/async 端点,分块参数(HybridChunkerOptions / HierarchicalChunkerOptions)随请求一并提交,且默认 include_converted_doc=False、结果走 InBody 目标。需要自定义分块参数(如 max_tokens、tokenizer)时,可参考 CLI 中 --chunks-max-tokens--chunks-tokenizer 的映射思路,通过 options 中的分块选项传递。

异步客户端与 Job 生命周期

两个进阶能力:

  • 异步客户端AsyncDoclingServiceClientservice_client/_async_client.py)以 await 镜像同步 API,适用于 FastAPI 等异步服务;同步客户端的批量接口内部也是复用它实现的。
  • 显式任务句柄:当需要任务生命周期控制、结果目标(result targets,如预签名 URL / S3)、逐项扇出时,使用 submit* 系列方法。它们返回 ConversionJob 句柄,而不是阻塞等结果。

ConversionJob 句柄

service_client/job.py 定义了同步 ConversionJob 与异步 AsyncConversionJob,二者共享同一身份属性与操作:

job = client.submit(source="report.pdf", target=S3Target(bucket=..., key_prefix=...))
print(job.task_id, job.submitted_at)   # 任务身份
print(job.status, job.queue_position) # "pending" 或任务状态;队列位置
print(job.done)                        # 是否到达终态

job.poll(wait=0.0)        # 主动拉一次状态(wait 为服务端侧等待秒数)
job.watch(timeout=...)    # 迭代状态更新(WebSocket 或轮询)
result = job.result(timeout=300)  # 等待终态并取回结果

result(timeout) 语义:若任务未达终态则阻塞等待至 timeout(默认取客户端 job_timeout),超时抛出 TaskTimeoutError;到达终态后从 /v1/result/{task_id} 取回按 target 类型解析的结果对象。

提交方法族与结果目标

submit / submit_batch / submit_and_retrieve_eachservice_client/init.py 统一导出,配套的批处理来源/目标类型为 BatchSourceRequestInput / BatchSourceRequestItem / BatchTargetRequestInput(定义在 datamodel/service/requests.py),存储目标类型 S3TargetPresignedUrlTarget 等定义在 datamodel/service/targets.py。支持的提交目标(SubmitTarget 类型别名)包括:

目标类型 结果形态 说明
PresignedUrlTarget PresignedUrlConvertResponse 服务端返回预签名下载 URL,客户端按需下载
InBodyTarget ConvertDocumentResponse 结果内联在 HTTP 响应体中(JSON 文档随响应返回)
ZipTarget RawServiceResult 结果是 zip 原始字节
S3Target / AzureBlobTarget / GoogleCloudStorageTarget / GoogleDriveTarget 预签名文档响应 服务端直接把产物写入你的对象存储
GenericTargetRequest 插件目标 服务端插件提供的自定义存储目标

两个重要的自动行为(源码注释与 _submit_conversion_job_with_auto_target() 可印证):

  1. submit() 不带 target 时默认走"预签名优先":先以 PresignedUrlTarget 提交;若服务端因未配置 artifact 存储而拒绝(400/422 且 detail 命中特征),自动回退到 InBodyTarget 重新提交;
  2. 高层 convert()/convert_all() 强制追加 JSON 输出格式:因为客户端要重建 DoclingDocument,无论服务端返回预签名产物还是内联结果,都需要 JSON 文档载荷。

submit_and_retrieve_each(items, max_in_flight, ordered, target) 则提供"提交 N 个 ConversionItem、逐项产出 (item, 结果或异常) 元组"的扇出能力——每个 item 可携带独立的 source、options、headers 与 metadata。注意:同步客户端的这类批量方法不能在活跃的 asyncio 事件循环内调用_ensure_sync_bridge_allowed() 会显式抛 RuntimeError),异步场景应直接使用 AsyncDoclingServiceClient

产物下载的安全性

高层 convert() 拿到预签名 URL 后会下载产物并在本地重建文档。从 client.py 看,这条"物化"(materialization)路径内置了多重防护:

  • SSRF 防护:每个下载 hop 都校验目标必须是公网可达地址(_is_safe_artifact_url()),拒绝指向内网/回环/链路本地地址的预签名 URL;重定向手动跟随且逐跳重校验(上限 5 次);
  • 体积上限:流式下载累计超过 max_artifact_download_bytes(默认 512 MiB)即中止;
  • API Key 隔离:产物下载使用独立 httpx 客户端,不会把服务的 X-Api-Key 头泄露给外部存储端点;
  • zip-slip 与路径逃逸防护:解压 resource_bundle 时校验成员路径,且文档 JSON 中引用的相对图片 URI 被限制在解压目录内;
  • 优雅降级:下载/重建失败不抛异常,而是转为 FAILURE 状态的 ConversionResult,保证 convert_all() 能继续处理后续文档。

错误处理:类型化异常族

客户端抛出全部继承自 DoclingServiceClientError 的类型化异常(service_client/exceptions.py),捕获基类即可兜住所有:

from docling.service_client import (
    DoclingServiceClientError,
    ConversionError,
    ServiceUnavailableError,
    TaskTimeoutError,
    UsageLimitExceededError,
    ResultExpiredError,
)
异常 触发场景(源码可查的映射)
ConversionError 单次转换以失败状态结束(raises_on_error=True 时)
ServiceUnavailableError 服务 5xx 重试耗尽、传输层错误(如 GET/HEAD/OPTIONS 的 TransportError)重试耗尽
ServiceError 其他 4xx 不可重试错误,携带 status_code 与服务端 detail
ResponseSchemaMismatchError 2xx 响应无法反序列化为预期模型(客户端/服务端版本漂移的信号)
TaskTimeoutError 等待任务终态超过 job_timeout
TaskNotFoundError 轮询/取结果时任务 ID 不存在(404 + "Task not found.")
ResultNotReadyError / ResultExpiredError 结果 404 但任务未到终态 / 已终态但结果已被清理
TaskExecutionError 服务端任务级编排失败(TaskFailureResult),携带 PublicFailureInfo
UsageLimitExceededError HTTP 402:用量配额耗尽,附 current_usagelimit 字段
ArtifactDownloadError 预签名产物下载失败/超限(通常内部消化为 FAILURE 结果,不直接透出)

重试策略同样在源码中可验证:500/502 按指数退避重试(基数 1 秒,1.0 * 2**attempt),429/503 优先遵循服务端 Retry-After 头(支持秒数与 HTTP-date 两种格式),均受 http_retries(默认 3)约束。

CLI:docling convert-remote

不写代码的一次性远程转换:

docling convert-remote report.pdf \
  --service-url https://docling.example.com --to md --output /tmp/

该命令内部同样驱动同步 DoclingServiceClient,并复用与本地 docling convert 完全相同的导出器,因此落盘产物一致。完整实现见 cli/remote.py,可用 flag 一览(仅暴露服务端真正生效的选项;--device--threads 等本地执行类 flag 被有意省略):

Flag 默认 说明
--service-url env DOCLING_SERVICE_URL 服务基地址(必填,三者取其一)
--api-key env DOCLING_SERVICE_API_KEY 鉴权密钥,无鉴权服务可省略
--from 全部受支持格式 输入格式白名单,同时用于过滤目录遍历
--to(可重复) md 输出格式:md/json/chunks/html/dclx/doctags
--chunks-type hybrid --to chunks 的分块器:hybridhierarchical
--chunks-max-tokens 每个 chunk 的最大 token 数
--chunks-tokenizer sentence-transformers/all-MiniLM-L6-v2 HF tokenizer 名称/路径,仅 hybrid 生效
--ocr / --no-ocr 是否对位图内容执行 OCR
--force-ocr 用 OCR 文本替换已有文本层
--ocr-lang 逗号分隔的 OCR 语言列表(引擎特定名称)
--tables / --no-tables 是否做表格结构分析
--pipeline standard 服务处理 PDF/图片所用 pipeline
--enrich-code / --enrich-formula / --enrich-picture-classes / --enrich-picture-description / --enrich-chart-extraction 各类富化模型开关
--image-export-mode 服务默认 图片导出模式:embedded/placeholder/referenced
--page-range 仅转换指定页范围,如 1-4(从 1 计)
--document-timeout 服务端处理每个文档的超时(秒)
--abort-on-error / --no-abort-on-error 出错即中止整批
--max-concurrency 8 客户端并发文档数
--timeout 300 客户端等待每个 Job 完成的超时
--watcher websocket 状态跟踪方式:websocketpolling
--output . 结果输出目录(不存在会自动创建)
-v / -vv 日志级别 info / debug

退出码约定:0 成功;1 运行时/连接失败(服务不可达、转换错误);2 用法/配置错误(无法解析出服务 URL)。命令执行前会先调用 client.health()(对应 GET /health)做快速失败检查,连不通直接以退出码 1 报错,避免逐文档重试的漫长等待。

几个实用例子(摘自该命令的 epilog):

# 单文件,凭证来自 env / .env
docling convert-remote report.pdf

# 显式端点与密钥,Markdown + JSON 双格式输出
docling convert-remote --service-url https://docling.example.com \
  --api-key "$KEY" --to md --to json --output ./out report.pdf

# 整个目录,只取 PDF 和 DOCX,关闭 OCR
docling convert-remote --from pdf --from docx --no-ocr ./inbox

来源可以是本地文件、本地目录(按 --from 过滤遍历)或 http(s) URL;URL 不会被客户端预下载,而是作为 HTTP source 请求交给服务端拉取。

小结

Service Client 把 Docling 的转换能力封装成一组"提交任务—跟踪状态—取回结果"的远程原语:高层 convert()/convert_all() 屏蔽了任务细节并自动处理预签名产物下载与 InBody 回退;submit* 家族 + ConversionJob 则暴露完整任务生命周期,配合 S3TargetPresignedUrlTargetBatchTargetRequestInput 等目标类型覆盖大规模、跨存储的转换与分块流水线;类型化异常与指数退避重试让生产环境下的故障可诊断、可恢复。仓库内 tests/test_service_client_* 系列测试与 examples/service_client/ 下的 convert.pychunk.pybatch.py 示例,是进一步验证上述行为与编写自己流水线的最佳起点。

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