背景
当前 PDF 转换链路主要依赖 Docling:上传和 CN/HK 下载会生成 *_docling.json,后续由 DoclingProcessor / FinsDoclingProcessor 供 LLM 读取。部分用户反馈 Docling convert 经常出现 std::bad_alloc / bad_alloc 类错误,导致财报转换失败。
动机判断
问题真实存在,且严重性不应低估:
- 代码本身已经把 Docling Windows
std::bad_alloc 作为已知故障写进 dayu/docling_runtime.py 的策略说明,并做了 pypdfium2 / CPU 回退链;这说明当前 Docling runtime 已经在处理同类稳定性问题,但仍未覆盖用户遇到的全部失败面。
- 对 CN/HK 财报下载来说,Docling 转换失败会直接让单份 filing 失败,无法进入
ingest_complete=True 的完成态。
- 只换 convert 命令不够,因为当前 LLM 读取链路依赖 Docling JSON schema;MinerU 输出 schema 与 Docling 不兼容,必须同时落 convert 与 processor。
当前 Docling 使用链路
1. 依赖与 runtime 真源
pyproject.toml 当前把 docling>=2.90.0,<3.0.0 与 docling-core>=2.74.0,<3.0.0 放在生产依赖。
dayu/docling_runtime.py 是 Docling SDK 调用真源:
convert_pdf_bytes_with_docling(raw_bytes, stream_name=...) 把 PDF bytes 包成 DocumentStream,避免路径编码问题。
run_docling_pdf_conversion(...) 负责 backend × device 尝试链。
build_docling_pdf_converter(...) 固定 Docling DocumentConverter / PdfFormatOption / backend 策略。
dayu/fins/docling_export.py 是 fins 侧唯一出口:
convert_pdf_bytes_to_docling_payload(...) -> dict[str, Any]。
convert_pdf_bytes_to_docling_json_bytes(raw_data, stream_name) -> bytes。
PdfToDoclingJsonBytes = Callable[[bytes, str], bytes] 是 CN/HK 下载注入点。
2. 上传链路
dayu/fins/pipelines/docling_upload_service.py 的 DoclingUploadService 读取原文件后,对每个文件调用 _convert_with_docling。
- 转换产物固定命名为
<stem>_docling.json,source="docling",content_type="application/json"。
primary_document 由 _pick_primary_docling_file(...) 选择,要求存在 _docling.json。
- SEC/CN pipeline 上传 filing/material 都复用这个 service。
3. CN/HK 下载链路
dayu/fins/pipelines/cn_pipeline.py 构造时默认注入 convert_pdf_bytes_to_docling_json_bytes。
dayu/fins/pipelines/cn_download_workflow.py 把 host 的 convert_pdf_to_docling_json 传给单 filing workflow。
dayu/fins/pipelines/cn_download_filing_workflow.py:
- PDF 下载/复用后,若无可复用 Docling JSON,就在线程里调用 converter。
- 产物固定为
${document_id}_docling.json。
- commit source meta 时把
primary_document 切到该 _docling.json。
- 多处硬编码需要处理:
cn_download_source_upsert.py 校验完成态 primary_document.endswith("_docling.json")。
cn_download_rebuild.py / cn_download_staging.py 使用 _docling.json 判断可复用与重建。
cn_download_models.py / README 文案把完成态定义成 Docling JSON。
4. Processor / LLM 读取链路
- Engine 注册表
dayu/engine/processors/registry.py 默认注册 DoclingProcessor、MarkdownProcessor、BSProcessor。
- Fins 注册表
dayu/fins/processors/registry.py 用 FinsDoclingProcessor 覆盖 engine 的 docling_processor。
DoclingProcessor.supports(...) 通过 _docling.json 后缀或 sniff Docling JSON 判断支持。
DoclingProcessor 依赖 docling_core.types.doc.document.DoclingDocument.load_from_json(...),然后构建 sections/tables/page_content/search。
FinsDoclingProcessor 继承 DoclingProcessor,再对 _tables 做金融语义 relabel。
MinerU 使用方式调研
基于 2026-05-04 查阅的官方资料:
架构建议
不建议的做法
不要把 MinerU 输出伪装成 _docling.json 或让 DoclingProcessor 读取 MinerU JSON。两者 schema 不同,伪装会把根因隐藏到 processor 内部,后续表格/章节/page_content 都会变成脆弱分支。
建议方向
把当前 Docling 专有链路抽象成“文档转换产物”链路,但转换器与处理器保持成对实现:
- 新增 provider 概念:
docling / mineru。
- Converter 层输出强类型
ConvertedDocumentAsset,至少包含:
name: str
data: bytes
content_type: str
source_label: str,如 docling / mineru
parser_format: str,如 docling_json / mineru_content_list
- 可选
converter_version: str | None
- 上传 service 不应继续叫
DoclingUploadService 如果它开始支持多个 provider;建议改名为 provider-neutral 的 DocumentConversionUploadService 或同类名字,并更新调用点。
- CN/HK download host 不应继续暴露
convert_pdf_to_docling_json;建议替换为 convert_pdf_to_primary_document 或 document_converter,让 workflow 不关心 provider 具体 suffix。
- Processor 层新增:
dayu/engine/processors/mineru_processor.py
dayu/fins/processors/fins_mineru_processor.py
- 在 engine/fins registry 注册,优先级与 Docling/Markdown 同级;靠
supports() 通过后缀与 JSON sniff 判定。
- 元数据层建议显式记录转换 provider:
- source meta 增加
conversion_provider、conversion_parser_format、converter_version。
- file entry 的
source 从当前 docling 扩展为 mineru。
- 如果这被视为 schema 变更,需要按项目约束补
workspace_migrations 插件进入 dayu-cli init,为既有 _docling.json 元数据填充 conversion_provider="docling"。
MinerU 产物选择建议
第一阶段建议把 *_content_list.json 作为 primary document:
- 它是官方定位的“简化、阅读序、适合后续处理”的内容列表。
- section/list/search 可以直接基于阅读序构建。
- table 有 HTML
table_body,可复用现有 HTML/table 工具或 pandas.read_html 解析。
page_idx 可映射到 Dayu 的 1-based page_no。
*_middle.json 可以作为附加文件存储,不建议第一阶段作为 primary:它更适合二次开发和调试,结构更重,processor 实现复杂度高。content_list_v2 虽然更规整,但官方标注 development version,第一阶段不宜把它作为稳定读取契约。
代码实施建议
-
新增 MinerU converter runtime:
- 新模块建议
dayu/mineru_runtime.py 或 dayu/fins/mineru_export.py。
- 第一阶段优先走 CLI/subprocess 或本地
mineru-api,不要直接绑定不稳定的内部 Python API;CLI/API 是官方文档主路径。
- 输入 bytes 时需要写入
workspace/tmp/ 或临时目录,并确保清理;项目约束要求临时脚本/临时文件在 workspace/tmp/。
- 输出目录解析应定位
<output>/<stem>/<method_or_backend>/..._content_list.json;需要单测覆盖不同 backend 目录名。
- 支持配置项:backend 默认建议
pipeline(稳定、CPU 可用),lang 默认可按市场设 ch/en,method 默认 auto,table/formula 默认开启。
-
重构转换注入边界:
- 用 provider-neutral Protocol 替代
PdfToDoclingJsonBytes。
- CN/HK download workflow 只消费
ConvertedDocumentAsset,不拼 _docling.json。
- 上传 service 对每个原始文件追加 provider 产物,并用统一
_pick_primary_converted_file(...) 选择 primary。
-
清理 Docling 硬编码:
DOCLING_FILE_SUFFIX 改成 provider suffix 配置,Docling 可继续是 _docling.json,MinerU 建议 _mineru_content_list.json 或 _mineru.json。
cn_download_source_upsert.py 的 _docling.json 校验改为“primary 指向受支持的转换产物”。
cn_download_rebuild.py / cn_download_staging.py 改成按 provider/suffix 查找可复用产物。
- 文案中的“Docling JSON 完成态”改成“转换 JSON 完成态”。
-
实现 MinerUProcessor:
supports():后缀命中 _mineru_content_list.json / _mineru.json,或 JSON sniff 到 list 且元素包含 MinerU content block 字段(type + page_idx + text/table_body/...)。
list_sections():把 text_level >= 1 的 text/title 作为 section header;无标题时构造单一全文 section。
read_section():按 section range 拼正文;遇到 table 插入 [TABLE: table_1] 风格占位,保持与现有工具语义一致。
list_tables():扫描 type == "table",caption 来自 table_caption,context_before 从前序文本窗口取,page_no=page_idx + 1。
read_table():优先把 table_body HTML 解析为 records;失败则返回 HTML/markdown 字符串。
get_page_content():按 page_idx 收集 sections/tables/text_preview。
search() / get_full_text() 可复用 run_titled_section_search 与现有 text_utils。
-
实现 FinsMinerUProcessor:
- 继承
MinerUProcessor。
- 复用
relabel_tables(self._tables) 的金融表格标注思路;如果内部 table block 字段不同,先定义与 financial_enhancer 兼容的 table block 或抽出 provider-neutral table semantic 输入。
-
依赖策略:
- 不建议直接把
mineru[all] 放入默认生产依赖,体量和硬件依赖都较重。
- 建议新增 optional extra,例如
mineru = ["mineru[pipeline]>=3.1,<4"] 或文档要求用户自行安装;具体 extras 名称以验证后的 pip 解析结果为准。
- 如果默认配置允许
DAYU_PDF_CONVERTER=mineru,启动时必须给出明确缺依赖错误,不要在深层 subprocess 抛不可读异常。
测试建议
- Converter 单测:fake subprocess / fake output directory,验证能从 MinerU 输出目录读取
content_list.json 并返回 ConvertedDocumentAsset。
- Processor 单测:用最小 MinerU
content_list.json fixture 覆盖 section/table/search/page_content。
- Fins processor 单测:验证金融表格 relabel 不回退。
- Upload service 单测:同一原始文件在 provider 不变且 fingerprint 相同时 convert 前跳过;provider 变化时应重新转换并更新 primary。
- CN/HK download workflow 单测:转换失败 reason 不再写死
docling_convert_failed,建议改为 document_convert_failed 或带 provider 的 mineru_convert_failed。
- Rebuild/staging 单测:能复用 MinerU primary,也能识别 Docling primary(如果迁移策略保留历史数据)。
- Pyright:新增 Protocol/dataclass/TypedDict 后必须全量跑。
README 触发范围
如果落地实现修改以下目录,应按项目规则同步:
dayu/fins/:更新 dayu/fins/README.md,说明转换 provider、两条执行路径、产物选择。
dayu/engine/:更新 dayu/engine/README.md,说明 MinerUProcessor 与 Processor registry。
- CLI/config 入口变更:更新根目录
README.md 与 dayu/config/README.md。
- 测试结构变更:更新
tests/README.md。
验收标准
- 可通过配置选择 Docling 或 MinerU 作为 PDF convert provider。
- MinerU convert 成功后,source meta 的
primary_document 指向 MinerU JSON 产物。
FinsToolService 对 MinerU primary document 能正常执行:list_sections、read_section、list_tables、get_table、get_page_content、search_document。
- Docling bad_alloc 不再阻断选择 MinerU 的用户。
- 受影响测试和 pyright 通过;相关 README 已同步。
背景
当前 PDF 转换链路主要依赖 Docling:上传和 CN/HK 下载会生成
*_docling.json,后续由DoclingProcessor/FinsDoclingProcessor供 LLM 读取。部分用户反馈 Docling convert 经常出现std::bad_alloc/bad_alloc类错误,导致财报转换失败。动机判断
问题真实存在,且严重性不应低估:
std::bad_alloc作为已知故障写进dayu/docling_runtime.py的策略说明,并做了pypdfium2/ CPU 回退链;这说明当前 Docling runtime 已经在处理同类稳定性问题,但仍未覆盖用户遇到的全部失败面。ingest_complete=True的完成态。当前 Docling 使用链路
1. 依赖与 runtime 真源
pyproject.toml当前把docling>=2.90.0,<3.0.0与docling-core>=2.74.0,<3.0.0放在生产依赖。dayu/docling_runtime.py是 Docling SDK 调用真源:convert_pdf_bytes_with_docling(raw_bytes, stream_name=...)把 PDF bytes 包成DocumentStream,避免路径编码问题。run_docling_pdf_conversion(...)负责 backend × device 尝试链。build_docling_pdf_converter(...)固定 DoclingDocumentConverter/PdfFormatOption/ backend 策略。dayu/fins/docling_export.py是 fins 侧唯一出口:convert_pdf_bytes_to_docling_payload(...) -> dict[str, Any]。convert_pdf_bytes_to_docling_json_bytes(raw_data, stream_name) -> bytes。PdfToDoclingJsonBytes = Callable[[bytes, str], bytes]是 CN/HK 下载注入点。2. 上传链路
dayu/fins/pipelines/docling_upload_service.py的DoclingUploadService读取原文件后,对每个文件调用_convert_with_docling。<stem>_docling.json,source="docling",content_type="application/json"。primary_document由_pick_primary_docling_file(...)选择,要求存在_docling.json。3. CN/HK 下载链路
dayu/fins/pipelines/cn_pipeline.py构造时默认注入convert_pdf_bytes_to_docling_json_bytes。dayu/fins/pipelines/cn_download_workflow.py把 host 的convert_pdf_to_docling_json传给单 filing workflow。dayu/fins/pipelines/cn_download_filing_workflow.py:${document_id}_docling.json。primary_document切到该_docling.json。cn_download_source_upsert.py校验完成态primary_document.endswith("_docling.json")。cn_download_rebuild.py/cn_download_staging.py使用_docling.json判断可复用与重建。cn_download_models.py/ README 文案把完成态定义成 Docling JSON。4. Processor / LLM 读取链路
dayu/engine/processors/registry.py默认注册DoclingProcessor、MarkdownProcessor、BSProcessor。dayu/fins/processors/registry.py用FinsDoclingProcessor覆盖 engine 的docling_processor。DoclingProcessor.supports(...)通过_docling.json后缀或 sniff Docling JSON 判断支持。DoclingProcessor依赖docling_core.types.doc.document.DoclingDocument.load_from_json(...),然后构建 sections/tables/page_content/search。FinsDoclingProcessor继承DoclingProcessor,再对_tables做金融语义 relabel。MinerU 使用方式调研
基于 2026-05-04 查阅的官方资料:
mineru-3.1.6(2026-04-28)。来源:https://github.com/opendatalab/MinerUuv pip install -U "mineru[all]";mineru[all]覆盖 Windows / Linux / macOS 的核心能力。来源:https://github.com/opendatalab/MinerU#install-minerumineru -p <input_path> -o <output_path>;无 GPU 或希望纯 CPU 时可指定-b pipeline。来源:https://opendatalab.github.io/MinerU/usage/quick_usage/mineru-api之上的 orchestration client:不传--api-url会启动临时本地mineru-api;传--api-url会连接已有 FastAPI 服务。来源:https://opendatalab.github.io/MinerU/usage/cli_tools/-p/--path输入文件或目录。-o/--output输出目录。-m/--method [auto|txt|ocr]。-b/--backend [pipeline|hybrid-auto-engine|hybrid-http-client|vlm-auto-engine|vlm-http-client],默认hybrid-auto-engine。-l/--lang指定 OCR 语言。-f/--formula、-t/--table控制公式/表格。-s/--start、-e/--end控制页码。mineru-api --host 0.0.0.0 --port 8000,提供/health、POST /tasks、POST /file_parse、GET /tasks/{task_id}、GET /tasks/{task_id}/result。来源:https://opendatalab.github.io/MinerU/usage/quick_usage/{original_filename}_content_list.json:扁平阅读序内容,适合后续处理。{original_filename}_middle.json:包含pdf_info,适合二次开发与 page/block 级信息。{original_filename}_content_list_v2.json:3.0 起新增,按页分组、统一type + content,但官方标注为 development version,需谨慎作为稳定契约。来源:https://opendatalab.github.io/MinerU/reference/output_files/
content_list.json里 table block 包含table_bodyHTML,caption/footnote/page_idx/bbox 等字段;这适合在 Processor 中用结构化 HTML 表格解析成 records/markdown。MINERU_MODEL_SOURCE可切换模型源;MINERU_PDF_RENDER_TIMEOUT、MINERU_PDF_RENDER_THREADS、MINERU_PROCESSING_WINDOW_SIZE、MINERU_API_MAX_CONCURRENT_REQUESTS等可控制渲染和服务并发。来源:https://opendatalab.github.io/MinerU/usage/cli_tools/架构建议
不建议的做法
不要把 MinerU 输出伪装成
_docling.json或让DoclingProcessor读取 MinerU JSON。两者 schema 不同,伪装会把根因隐藏到 processor 内部,后续表格/章节/page_content 都会变成脆弱分支。建议方向
把当前 Docling 专有链路抽象成“文档转换产物”链路,但转换器与处理器保持成对实现:
docling/mineru。ConvertedDocumentAsset,至少包含:name: strdata: bytescontent_type: strsource_label: str,如docling/mineruparser_format: str,如docling_json/mineru_content_listconverter_version: str | NoneDoclingUploadService如果它开始支持多个 provider;建议改名为 provider-neutral 的DocumentConversionUploadService或同类名字,并更新调用点。convert_pdf_to_docling_json;建议替换为convert_pdf_to_primary_document或document_converter,让 workflow 不关心 provider 具体 suffix。dayu/engine/processors/mineru_processor.pydayu/fins/processors/fins_mineru_processor.pysupports()通过后缀与 JSON sniff 判定。conversion_provider、conversion_parser_format、converter_version。source从当前docling扩展为mineru。workspace_migrations插件进入dayu-cli init,为既有_docling.json元数据填充conversion_provider="docling"。MinerU 产物选择建议
第一阶段建议把
*_content_list.json作为 primary document:table_body,可复用现有 HTML/table 工具或pandas.read_html解析。page_idx可映射到 Dayu 的 1-basedpage_no。*_middle.json可以作为附加文件存储,不建议第一阶段作为 primary:它更适合二次开发和调试,结构更重,processor 实现复杂度高。content_list_v2虽然更规整,但官方标注 development version,第一阶段不宜把它作为稳定读取契约。代码实施建议
新增 MinerU converter runtime:
dayu/mineru_runtime.py或dayu/fins/mineru_export.py。mineru-api,不要直接绑定不稳定的内部 Python API;CLI/API 是官方文档主路径。workspace/tmp/或临时目录,并确保清理;项目约束要求临时脚本/临时文件在workspace/tmp/。<output>/<stem>/<method_or_backend>/..._content_list.json;需要单测覆盖不同 backend 目录名。pipeline(稳定、CPU 可用),lang 默认可按市场设ch/en,method 默认auto,table/formula 默认开启。重构转换注入边界:
PdfToDoclingJsonBytes。ConvertedDocumentAsset,不拼_docling.json。_pick_primary_converted_file(...)选择 primary。清理 Docling 硬编码:
DOCLING_FILE_SUFFIX改成 provider suffix 配置,Docling 可继续是_docling.json,MinerU 建议_mineru_content_list.json或_mineru.json。cn_download_source_upsert.py的_docling.json校验改为“primary 指向受支持的转换产物”。cn_download_rebuild.py/cn_download_staging.py改成按 provider/suffix 查找可复用产物。实现
MinerUProcessor:supports():后缀命中_mineru_content_list.json/_mineru.json,或 JSON sniff 到 list 且元素包含 MinerU content block 字段(type+page_idx+text/table_body/...)。list_sections():把text_level >= 1的 text/title 作为 section header;无标题时构造单一全文 section。read_section():按 section range 拼正文;遇到 table 插入[TABLE: table_1]风格占位,保持与现有工具语义一致。list_tables():扫描type == "table",caption 来自table_caption,context_before 从前序文本窗口取,page_no=page_idx + 1。read_table():优先把table_bodyHTML 解析为 records;失败则返回 HTML/markdown 字符串。get_page_content():按page_idx收集 sections/tables/text_preview。search()/get_full_text()可复用run_titled_section_search与现有 text_utils。实现
FinsMinerUProcessor:MinerUProcessor。relabel_tables(self._tables)的金融表格标注思路;如果内部 table block 字段不同,先定义与financial_enhancer兼容的 table block 或抽出 provider-neutral table semantic 输入。依赖策略:
mineru[all]放入默认生产依赖,体量和硬件依赖都较重。mineru = ["mineru[pipeline]>=3.1,<4"]或文档要求用户自行安装;具体 extras 名称以验证后的 pip 解析结果为准。DAYU_PDF_CONVERTER=mineru,启动时必须给出明确缺依赖错误,不要在深层 subprocess 抛不可读异常。测试建议
content_list.json并返回ConvertedDocumentAsset。content_list.jsonfixture 覆盖 section/table/search/page_content。docling_convert_failed,建议改为document_convert_failed或带 provider 的mineru_convert_failed。README 触发范围
如果落地实现修改以下目录,应按项目规则同步:
dayu/fins/:更新dayu/fins/README.md,说明转换 provider、两条执行路径、产物选择。dayu/engine/:更新dayu/engine/README.md,说明MinerUProcessor与 Processor registry。README.md与dayu/config/README.md。tests/README.md。验收标准
primary_document指向 MinerU JSON 产物。FinsToolService对 MinerU primary document 能正常执行:list_sections、read_section、list_tables、get_table、get_page_content、search_document。