VDiff PDF-to-HTML Workflow

foundation  standalone reproduction · mentor workflow reference

为什么它是基础

0-Project/20260617-vdiff-pdf-to-html-data-generation-loop/

这个 subproject 是一个单独的复现项目,保存 mentor 在一些很难的 PDF-to-HTML 例子上手动搭建出来的复杂 workflow。它的意义不是“又一个普通导入项目”,而是提供了一个已经被困难案例逼出来的流程样本:从 PDF 页图出发,经过转写、绑定、校准、bbox/layout 检查和视觉修复,最终形成可观察、可对比、可抽象的验证流程。

我们后面的项目要证明的事情,正是这种过程可以从手工 workflow 中抽象出来,变成 agentic coding loop 里的 multimodal verification 环节。因此这里要保持稳定:能随时打开、学习细节、看少量案例,并和 Minimal Verification Loop / Agent Harness Pilot 的 control-loop 结果互相对照。

入口与案例

当前已按 mentor 的 FastAPI demo 形态部署 live workflow 服务,并通过 Cloudflare Tunnel 暴露公网入口;VDiff 服务自身也有口令保护,所以知道 tunnel 链接但不知道口令时看不到内容。服务运行在 real pipeline 模式,会调用本机 Copilot proxy、Claude CLI、Node calibration 和 Playwright 渲染测量;建议先从单个 gallery case 开始试跑。

Live Workflow Service https://choosing-spirit-observation-autos.trycloudflare.com · password protected · public tunnel → localhost:8002 · real mode
打开服务 →

流程骨架

Transcribe从 PDF 截图生成初始 HTML
Bind把 PDF 文本/图像绑定到生成元素
Calibrate校准字号、行高和布局
Validate用 bbox/layout 证据比较渲染结果
Repair用视觉 rubric 迭代修复 CSS/布局
Validate 子操作

关键子操作描述

在比较 PDF 和 HTML 之前,VDiff 会先统一截图范围与尺度,避免把浏览器留白或原图分辨率差异误认为版面问题。

  1. 1

    优先截取 page root:找到与 PDF 页面宽度匹配的 HTML 页面容器,只截这个容器,并以其左上角作为坐标原点。

  2. 2

    full page 是兜底:如果找不到可靠的页面容器,就截取整个 HTML 文档,保证内容不会遗漏。

  3. 3

    PDF 等比例缩放后再比较:令 s = HTML 截图宽 ÷ PDF 截图宽,PDF 的宽和高同时乘以 s,再做视觉与 bbox/layout 对比;这个过程不会修改最终 HTML。

图中 ①②③ 对应左侧三步:确定 HTML 截图范围、等比例缩放 PDF、在同一尺度下进入 VDiff 比较。

真实服务里,demo/pipeline_real.py 会先启动一段 Claude CLI session 跑 skills/run_pipeline_steps.md,完成 Transcribe 到 Validate;确认产生 1_font_fixed.html 后,再启动第二段 session 跑 skills/calibrate_css_spacing_v6.md 做 CSS 修复。下面每一项都可以展开看具体操作。

1. Transcribe page_snapshot.pngtranscribed.html

输入

  • testcase/{case}/page_snapshot.png,也就是 PDF 单页截图。
  • 可选的 src.pdf 元信息,用来保持 case 目录和后续抽取一致。

具体操作

  • 调用 spatial_binding/reproduce.pyprompt_templates/html_reproduce*.txt
  • 多模态模型先“看图写 HTML”,重点是把页面结构、文字块、图片和大致样式落成一个可运行 candidate。
  • 单步命令线索:python -m spatial_binding.reproduce case1 --output-dir output-test

产物与学习点

  • 主要产物是 output-test/{case}/transcribed.html
  • 这一步还不追求像素级正确,先看内容是否齐全、层级是否合理、是否能被浏览器稳定截图。
  • 学习时可以把它当作 “candidate 生成” 的最小闭环入口。
2. Bind transcribed.html + PDF blocks → bound.html

输入

  • transcribed.html:上一步生成的 HTML candidate。
  • testcase/{case}/src.pdf:用于 PyMuPDF 抽取真实文本、图片和 bbox。

具体操作

  • spatial_binding/preprocess.py 抽取 PDF text/image elements。
  • text_elements.pymatch.pyimage_matching*.py 把 PDF block 对到 HTML node。
  • postprocess.py 写回绑定信息,并生成可视化 dashboard;命令线索:python -m spatial_binding case1 --output-dir output-test

产物与学习点

  • 关键产物包括 bound.htmltext_elements.jsonlimage_elements.jsonl
  • 调试产物包括 semantic_mapping*.jsondashboard-spatial-binding.htmlvisual_binding.png
  • 这一层回答“PDF 上这个区域,对应 HTML 里的哪个元素”。后续 verifier 才能定位问题。
3. Calibrate bound.html1_font_fixed.html

输入

  • bound.htmltext_elements.jsonl
  • page_snapshot.png,以及 PDF-derived 文本 bbox、行数和宽高信息。

具体操作

  • calibration/pre_calibrate.py 先找出换行、字号和文本框容易漂移的区域。
  • calibration/calibrate_v2.js 在浏览器/Node 环境里测量 HTML 实际渲染尺寸。
  • calibration/patch_font_v2.py 把字体、行高、宽度等 patch 回 HTML。

产物与学习点

  • 主要产物是 1_font_fixed.html,辅助产物有 calibration_result.jsoncalibrated_fonts_html.json
  • 可以打开 dashboard-calibration.html 看哪些文本块被校准。
  • 这一层说明 PDF-to-HTML 不能只靠语义相似,还要把浏览器渲染几何纳入 loop。
4. Validate 1_font_fixed.html → layout evidence + dashboard

输入

  • 1_font_fixed.html:已经做过字体/文本框校准的候选页面。
  • PDF 侧的 page_snapshot.png、文本元素、重要区域 bbox。

具体操作

  • eval/ground_truth_v2.py 从 PDF 侧生成 layout skeleton 和 salient elements。
  • eval/measure.py 用 Playwright 渲染 HTML,测量 DOM 元素位置和截图。
  • python -m eval.verify_v2 ... 把目标和渲染结果对齐比较,生成证据 dashboard。

产物与学习点

  • 核心产物:ground_truth.jsonlayout_measurement.jsonverify_result_v2.json
  • 可视化产物:debug_dashboards/ground_truth.htmldashboard-verify.htmlverify_screenshot.png
  • 这一层把“哪里不像”变成可以复查的 bbox/layout evidence,而不是只靠主观截图对比。
5. Repair verify evidence + rubrics → 1_css_fixed.html

输入

  • 1_font_fixed.htmlground_truth.jsonlayout_measurement.json
  • verify_result_v2.json、目标截图,以及 rubric 生成/验证结果。

具体操作

  • 第二段 Claude CLI 执行 skills/calibrate_css_spacing_v6.md,根据证据修改 CSS/布局。
  • eval/rubrics_gen.pyeval/rubrics_verify.py 把视觉要求写成可检查项,再逐项验证。
  • 每轮生成截图、metrics 和 snapshot,最后由 demo/best_pick.py 选择最优 iteration。

产物与学习点

  • 每轮产物在 css_fixing/iter{N}/snapshot.htmlmetrics.json
  • 最终页面是 1_css_fixed.html,rubric 结果在 rubrics.jsonrubric_verify_result.json
  • 重点看 iteration 为什么改、改了什么、指标是否真的改善;这正是 coding loop 可以抽象的部分。

Workflow 设计细节与案例

复现项目自己的设计记录;不是主看板概念说明

这个 workflow 的设计重点

它不是单次 image-to-code 生成,而是把一个 PDF 页面拆成可被验证的中间状态:先得到可运行 HTML,再把文本、图像和容器绑定到可测元素,随后用真实渲染位置校准字体与盒模型,最后把视觉差异写成 rubric 和修复任务。对我们的主线来说,它提供了一个 candidate → evidence → verifier → repair 的手工参照物。

18 PDF/page 输入 13 mentor 历史输出 5 pipeline stages
1. Transcribe

从 PDF page snapshot 生成初始 HTML,把视觉页面先变成可执行、可截图、可追踪的 candidate。

2. Bind

把 PDF 文本块、图片资产和 HTML 元素建立对应关系,后续 verifier 才知道“错的是哪个元素”。

3. Calibrate

用渲染测量校准字体、行高、宽高和空间位置,处理纯 prompt 生成最容易漂移的几何误差。

4. Validate

生成 bbox/layout dashboard、rubrics 和 pass/fail 结果,把肉眼差异变成可复查证据。

5. Repair

把失败项和 layout delta 交给 coding agent 修 CSS,再重新截图和比较,形成可迭代闭环。

案例 A · ticket / receipt 页面 case1 · 105/107 rubrics passed

这个 case 包含淡灰斜向水印、多色 logo、粗标题、编号栏和表格化信息。它展示了 VDiff 为什么要同时保留视觉 rubric 和几何证据:最终结果大部分通过,但 verifier 仍抓到两个细节问题:水印没有形成足够完整的重复斜向带,以及 No. 表头与编号值的水平对齐关系不对。

案例 B · report cover / branded illustration case7 · 52/52 rubrics passed

这个 case 的难点不是长文本,而是强品牌视觉:深蓝绿色上半区、珊瑚橙下半区、白色单色 logo、细分割线、底部白线插画。它说明 workflow 里的 verifier 不能只看文字 OCR,也要检查调色、区块关系、logo 单色化和插画层级。

案例 C · magazine / multi-column layout case10 · 61/65 rubrics passed

这个 case 暴露的是更“论文相关”的问题:大号空心数字要压在标题后面,多栏正文要保持行数和基线关系,右侧插画还要保留浅色半透明细节。历史结果的失败项包括插画浅色形状不明显、路由线条数量少一条、正文行数和左右栏基线不一致。这类失败很适合抽象成 multimodal verifier 的 evidence schema。

案例 D · uploaded demo run / repair iterations cbe23568 · 5 CSS fixing iterations

这个 demo run 更适合看“闭环”本身:同一个上传 PDF 页面留下了 transcribed、bound、font-fixed、verify dashboard、rubric verify 和 iter0iter4 的 CSS 修复快照。rubric verify 是 90/90 通过,但几何指标在不同迭代间仍然波动,说明 rubric pass 和 layout metric 不是同一种信号,后续 verifier 设计需要把两者一起纳入。

Deep Dive Case

重点复盘:cbe23568 · uploaded Mandai E-Ticket

这个例子相对难,是因为它同时有品牌 logo、水印、二维码、表格化订单信息、长段正文和底部蓝色链接文本;它还保留了完整的 5 轮 CSS fixing 过程。下面按“开始是什么样 → 做了什么操作 → 变成什么样”的顺序展开。

5 CSS fixing iterations 90/90 visual rubrics passed 0 final breakages in iteration metrics
关于水印:这里的斜向 watermark 是原始 PDF target 自带的,不是生成流程额外加进去的装饰。因此它不能删除,反而是 verifier 和 repair 需要保持的视觉元素之一。
cbe23568 target PDF page snapshot
输入:PDF page snapshot。目标页面是白底 e-ticket,要求 logo、斜向水印、订单信息、二维码和正文区域都保持空间关系。
cbe23568 final CSS fixed HTML
输出:最终 HTML。经过绑定、校准、验证和多轮 CSS 修复后,页面结构、主要视觉层级和 rubric 检查都稳定下来。

快速问题定位:先看红框,不用逐像素慢慢找

红框标的是这个 case 里最容易漂移、也最值得 verifier 盯住的区域。它不等于最终一定失败,而是告诉我们 repair loop 主要围绕哪些区域反复校准。

  1. 1 订单/二维码带:表格列宽、二维码位置、订单信息对齐。
  2. 2 正文块:长段文字的宽度、行高、换行和正文起点。
  3. 3 底部链接/法律文本:蓝色链接层级、底部密集小字和水印穿插区域。
  4. 0 整体页面框:初始转写阶段尤其要看整体缩放和页面边距。
problem callouts on transcribed HTML
第一版 transcribed:整体缩放、订单/二维码、正文和底部区域都需要进入后续校准。
problem callouts on CSS fixing iteration 0
iter0:主要结构已经出现,但 red-box 区域仍是 layout metric 的主要来源。
problem callouts on CSS fixing iteration 4
iter4:rubric 通过,但这些区域仍适合继续观察几何误差和空间关系。
problem callouts on verification screenshot
verify screenshot:验证阶段把这些区域固化成可复查证据,而不是只靠肉眼印象。
0

输入拆解:先把 PDF 页变成可观察目标

流程不是直接让模型“照着图写 HTML”就结束,而是保存目标截图、PDF 单页和后续中间产物。这样每一步都能回看:生成结果到底偏离了哪块区域。

  • 输入文件:testcase/cbe23568/src.pdf
  • 目标截图:testcase/cbe23568/page_snapshot.png
  • 页面特点:多元素、长文本、二维码、水印和表格结构同时存在。
target PDF page for cbe23568
Target PDF snapshot
1

Transcribe:生成第一版可运行 HTML

第一步把视觉页面转成 HTML/CSS candidate。这个阶段的价值是“先让页面活起来”:可以渲染、截图、对齐、继续被工具测量。

  • 操作:从 page snapshot 生成 transcribed.html
  • 结果:主要内容和区域结构已出现,但空间、尺寸和水印密度仍只是粗略接近。
transcribed HTML screenshot for cbe23568
transcribed.html
2

Bind:把文本、图片、容器绑定到可验证元素

只看截图很难知道“哪个 HTML 节点错了”。绑定阶段把 OCR/PDF 文本块、图片资产、容器和 HTML 元素建立对应关系,后面的 verifier 和 repair 才能指向具体对象。

  • 操作:生成 bound.html、layout skeleton 和元素级调试 dashboard。
  • 结果:页面从“一个整体截图”变成一组可命名、可测量、可追责的 visual/text elements。
3

Calibrate:校准字体、间距和盒模型

PDF-to-HTML 最容易漂移的是几何量:字号、行高、段落宽度、表格列宽、二维码位置。校准阶段用渲染测量结果反推 CSS,使页面从“内容像”走向“位置像”。

  • 操作:生成 1_font_fixed.html 和 calibration dashboard。
  • 结果:主要文本块、二维码和订单信息的位置更接近目标页。
font fixed HTML screenshot for cbe23568
1_font_fixed.html
4

Validate:把视觉差异变成证据

验证阶段不是简单说“像/不像”,而是生成可复查证据:layout measurement、截图对比、visual rubrics 和 pass/fail 结果。这个 case 的 rubric verify 最终是 90/90 passed

  • 操作:生成 dashboard-verify.htmlverify_screenshot.pngrubric_verify_result.json
  • 信号:rubric 检查关注 logo、文本层级、水印、二维码、订单表格、正文和链接样式。
5

Repair loop:5 轮 CSS fixing,不是一次修完

这个 case 最值得看的地方是 repair loop。每轮都会产生一个 snapshot 和 metrics:agent 调整 CSS,系统重新渲染并量化最大 top/left/width/height 偏差。几何指标并不总是单调变好,这正说明 verifier 需要同时看 layout metric 和 semantic/visual rubric。

CSS fixing iteration 0 screenshot
iter0dT 24.7 · dL 111.8 · dW 311.5 · dH 638.1
CSS fixing iteration 1 screenshot
iter1dT 365.5 · dL 542.7 · dW 105.1 · dH 431.7
CSS fixing iteration 2 screenshot
iter2dT 141.1 · dL 542.7 · dW 105.1 · dH 431.7
CSS fixing iteration 3 screenshot
iter3dT 86.4 · dL 712.4 · dW 105.1 · dH 431.7
CSS fixing iteration 4 screenshot
iter4dT 83.5 · dL 821.7 · dW 206.1 · dH 532.7
6

Final pick:把可接受版本固化成最终 HTML

最终输出保存为 1_css_fixed.html。这个结果不是“完美复制 PDF”,而是一个带有可解释证据链的 reproduction:每一步都有中间文件、截图、指标和 rubric 结果可以回查。

  • 最终 artifact:output-demo/cbe23568/1_css_fixed.html
  • 验证结论:rubric 层面 90/90 passed;layout metric 仍可作为后续 verifier 改进信号。
final CSS fixed HTML screenshot for cbe23568
final 1_css_fixed.html

我们要从这里学什么

困难例子如何暴露流程

复杂文档会逼出转写、绑定、校准、验证和修复的分层步骤,这些步骤比单次 image-to-code 更接近真实验证闭环。

哪些证据可以抽象

bbox、layout、文本绑定、截图和 rendered HTML 都可以成为 verifier 的 evidence,而不是只依赖肉眼或 pixel diff。

如何保持可对比

这个 workflow 需要作为稳定参照物存在,方便之后拿 Minimal Loop 的 Agent Harness Pilot / verification 结果做横向观察。

数据与已有结果

这不是一个空流程。上传的 mentor 快照里同时包含流程代码、初始 PDF/page-snapshot 输入,以及一批已经跑过的历史输出。当前建议把它分成三层理解:

初始输入数据

data_generation_loop/testcase/ 有 18 个 PDF 单页输入目录,其中 case1case16 是 gallery 主用例;每个目录都有 src.pdfpage_snapshot.png

mentor 历史结果

data_generation_loop/output-test/case1case13 已有转写、绑定、字体校准、layout 验证和最终 HTML;多数 case 还有 rubrics 与 pass/fail 结果。

当前服务输出

live service 的新 gallery run 会写到 output-gallery/;上传 PDF 会写到 output-demo/。这两处用于区分“当前复跑”与“mentor 快照里已有结果”。

位置内容怎么用
data_generation_loop/testcase/18 组 PDF + snapshot 输入真实 pipeline 的初始数据;UI gallery 主要读取 case1case16
data_generation_loop/output-test/13 组历史 pipeline 输出mentor 结果快照;可作为 baseline/reference,不是当前服务刚跑出来的
data_generation_loop/output-demo/2 组 demo/upload 结果,其中 cbe23568 有 5 轮 CSS fixing查看历史上传/演示输出,理解 Step 5 迭代形态
data_generation_loop/output-gallery/当前为空从 live service 对 gallery case 重新跑 real pipeline 后会写到这里
rubrics-diff/pdfs/7 个 standalone rubric-diff PDFrubric 生成/验证子实验的数据源