Docling HTML 表单解析实战:从餐厅小票 kvp_data_example 到结构化 DoclingDocument
本篇以 Docling 测试数据集中的餐厅小票样本 kvp_data_example.html 及其 Markdown 基准输出为主线,完整解析 Docling HTML 后端如何将带有编号标记(marker)、字段名(key)和字段值(value)的 HTML 表单解析为 field_region / field_item 结构化节点,并说明这些节点在 Markdown 导出、JSON(DoclingDocument)和层级树(itxt)中的具体呈现方式,读完后可掌握 Docling 键值对(key-value pair)表单解析的完整数据流与可验证依据。
1. 样本来源:一份典型餐厅小票 HTML
该 Markdown 基准文件由源文档 kvp_data_example.html 经 Docling 的 HTML 后端转换而来。源文档是一份“Restaurant Receipt Form”(餐厅小票表单页),页面标题(<h1>)为 HEADER,副标题为 Order Receipt Details,并包含一段说明文本 “Receipt documentation for table service:”。
从源码结构看,页面主体是一个 .form_region 容器,内部由多行 .field-row 组成,每一行是一个 .field,由三个语义明确的子元素构成:
.field-marker:编号标记,如<div id="key1_marker" class="field-marker">1</div>,在转换结果中对应marker标签文本;.field-label:字段名,如<span class="field-label" id="key1">Restaurant</span>,对应field_key标签;.field-value:字段值,如<span class="field-value" id="key1_value1">Docling</span>,对应field_value标签,且由于是只读展示值,kind被标记为read_only。
表单区之后还有一段提示标题 “Order items:”、说明文本 “Itemized list of products ordered with quantities.”、一个 4 行 3 列的 <table>(Nr. / Item Description / Quantity),以及页脚粗体文本 “Docling Restaurant 2026”。这种“编号 + 字段名 + 下划线值”的排版是表单/票据类文档的典型形态,也是 Docling 键值对提取能力要覆盖的场景。
2. Markdown 基准输出的完整内容与解读
基准文件 kvp_data_example.html.md 是该样本转换结果的 Markdown 呈现,全文如下(逐行保留):
# HEADER
Order Receipt Details
Receipt documentation for table service:
<!-- missing-text -->
<!-- missing-text -->
1
Restaurant
Docling
<!-- missing-text -->
2
Telephone
(123) 456-7890
<!-- missing-text -->
3
Server
Quack Quackling
<!-- missing-text -->
4
Order Number
12345
<!-- missing-text -->
5
Seating
13
<!-- missing-text -->
6
Dining Type
Celebration
<!-- missing-text -->
7
Number of guests
5
<!-- missing-text -->
8
Receipt date
16/03/2026
Order items:
Itemized list of products ordered with quantities.
| Nr. | Item Description | Quantity |
| - | - | - |
| I | Coffee | 1 |
| II | Lunch | 2 |
| III | Cake | 1 |
Docling Restaurant 2026
可以从中读出几个关键事实:
- 标题层级完整继承:
<h1>HEADER</h1>导出为 Markdown 一级标题# HEADER;页面上的<title>Restaurant Receipt Form</title>并未混入正文,而是作为文档家具(furniture)单独存放(见下文 JSON 分析)。 - 8 个字段全部保真:从 Restaurant = Docling、Telephone = (123) 456-7890、Server = Quack Quackling、Order Number = 12345、Seating = 13、Dining Type = Celebration、Number of guests = 5,到 Receipt date = 16/03/2026,编号 marker、字段名、字段值按原始阅读顺序成组出现,值与键之间没有混淆。
- 表格被完整转换为 Markdown 管道表格:表头三列(Nr.、Item Description、Quantity)与三行数据(I/Coffee/1、II/Lunch/2、III/Cake/1)一一映射,与源 HTML 的
<thead>/<tbody>结构一致。 <!-- missing-text -->占位注释:全文共出现 9 处。结合同目录的 JSON 基准(1 个field_region+ 8 个field_item)与 itxt 层级文件可以推断,这些注释是 Markdown 导出器为“无文本内容的容器节点”(field_region与每个field_item)保留的结构性占位,使纯文本读者仍能感知到表单区的分组边界;而真正承载信息量的 marker/key/value 文本则正常导出。
3. DoclingDocument JSON 结构:表单区如何被结构化
同目录的 kvp_data_example.html.json 是该样本的 DoclingDocument 序列化结果(schema_name: DoclingDocument,version: 1.10.0),它揭示了 Markdown 之下的完整结构:
- 家具层(furniture):
texts/0是label: title、content_layer: furniture的 “Restaurant Receipt Form”,即页面<title>,与正文分离存储,用于文档元信息(如摘要、RAG 元数据)。 - 正文层(body):
texts/1是HEADER标题节点,其子节点依次为两段说明文本、field_regions/0、表格标题/说明文本、tables/0和页脚文本,完整保留了页面阅读顺序。 field_regions/0:唯一的表单区域节点,label: field_region,聚合了全部 8 个field_item。field_items/0~field_items/7:每个字段一个容器,各自挂 3 个子文本节点:label: marker(编号 1~8);label: field_key(字段名,如 Restaurant);label: field_value(字段值,如 Docling),并带"kind": "read_only"属性,表明该值来自只读展示(而非可填写输入控件),这正是 HTML 后端依据.field-value这类只读值元素打上的标记。
tables/0:data中同时包含table_cells(扁平单元格数组)与grid(4×3 二维网格),表头单元格带column_header: true,单元格还携带row_span/col_span及起止行列偏移,可直接用于行列级定位;num_rows: 4、num_cols: 3与源表格一致。
这套 field_region → field_item → marker/field_key/field_value 的四级结构,使得下游任务(RAG 抽取、表单字段问答、数据录入校验)能够按“区域—字段—角色”精确取值,而不必对 Markdown 文本再做正则解析。
4. itxt 层级视图:容器节点在树中的位置
同目录的 kvp_data_example.html.itxt 以缩进树展示了同一文档的节点层级,其前几行为:
item-1 at level 1: title: HEADER
item-2 at level 2: text: Order Receipt Details
item-3 at level 2: text: Receipt documentation for table service:
item-4 at level 2: field_region: ignored
item-5 at level 3: field_item: ignored
item-6 at level 4: marker: 1
item-7 at level 4: field_key: Restaurant
item-8 at level 4: field_value: Docling
...(7~8 号字段同构)
item-37 at level 2: text: Order items:
item-38 at level 2: text: Itemized list of products ordered with quantities.
item-39 at level 2: table with [4x3]
item-40 at level 2: text: Docling Restaurant 2026
可以观察到两点:其一,field_region/field_item 是结构容器,本身无文本,故在 itxt 中标记为 ignored(不产生文本内容);其二,marker、field_key、field_value 是同一 field_item 下的并列子节点(level 4),与 JSON 中的 children 引用完全一致。表格在树中压缩为一行 table with [4x3] 摘要,与 JSON 中 num_rows/num_cols 呼应。三个基准文件(md / json / itxt)互为印证,构成 Docling 对该 HTML 样本的完整地面真值(ground truth)。
5. 源码级原理:HTML 后端如何产出 field_region / field_item
上述结构由 html_backend.py 的表单容器处理链生成,核心调用路径如下:
_handle_form_container(html_backend.py#L4307):当遍历到表单容器标签(如示例中的.form_region区域)时,先探测目标文档是否支持add_field_region/add_field_item/add_field_key/add_field_value四个方法。支持时走新式字段化路径;不支持时回退到旧的add_form+add_key_values(KVP 图)路径,即生成FormItem/KeyValuesItem的单元格与GraphLink(TO_VALUE)链接结构。_extract_form_region(html_backend.py#L3831):在容器标签内收集全部子标签,按“key 标签 → marker → values”的映射关系抽取为_ExtractedFormRegion,并记录consumed_tag_ids(已被表单逻辑消费的标签),避免后续普通遍历重复处理。_add_field_item_from_extracted(html_backend.py#L3750):为每个_ExtractedFormField依次创建field_item,并把key、marker、value、extra_text四类片段按 DOM 中的order排序后逐一落库——key 调用add_field_key,marker 以DocItemLabel.MARKER落为普通文本,value 调用add_field_value(kind透传,即 JSON 中出现的read_only),带checkbox_label的值则降级为普通文本。这正是 itxt/JSON 中 marker、field_key、field_value 保持原始阅读顺序的原因。- 落库与回退:确认字段解析成功后,
add_field_region先挂载到当前父节点(html_backend.py#L4367),再以_use_form_container/_use_form_fields_by_key_id上下文重走标签遍历;若无有效字段(fields_by_key_id为空),表格容器会退化为普通_handle_block表格解析,普通容器则按常规块级内容处理——这解释了示例中订单明细<table>为何以标准表格(而非表单字段)呈现。
6. 如何在仓库中查看与验证该样本
- 源 HTML 与基准三元组分别位于 tests/data/html/sources/kvp_data_example.html 与 tests/data/html/groundtruth/ 下的
kvp_data_example.html.md/.json/.itxt;HTML 后端的测试入口为 test_backend_html.py。 - 验证方式:使用 Docling 的 HTML 后端转换该源文件后,依次比对 Markdown 导出、
DoclingDocument序列化 JSON 与 itxt 树,三者应与上述基准一致——重点核对 8 组 marker/key/value、field_value的read_only标记、4×3 表格网格及<!-- missing-text -->占位数量(9 处,对应 1 个 field_region + 8 个 field_item)。 - 适用前提:
field_region/field_item结构要求文档版本支持相应的add_field_*系列方法(基准 JSON 中version为 1.10.0);旧版文档模型下同一 HTML 会走 KVP 图回退路径,输出形态不同,跨版本比对时需留意。
7. 小结
kvp_data_example 系列基准文件用一份餐厅小票完整示范了 Docling 处理键值对表单的能力边界:编号标记、字段名与字段值按 DOM 顺序保真落到 field_item 子节点,只读值通过 kind: read_only 显式标注,表格以网格化 grid 数据承载,页面标题下沉为家具层文本,Markdown 导出则以 <!-- missing-text --> 注释保留容器结构痕迹。源码层面,这套结果由 html_backend.py 中 _handle_form_container → _extract_form_region → _add_field_item_from_extracted 的表单解析链生成,并带有面向旧文档模型的 KVP 图回退机制。对从事表单/票据数据抽取的开发者而言,这组“源 HTML + md + json + itxt”四件套既是现成的对照基准,也是理解 DoclingDocument 字段化结构的最佳教学样本。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00