输入拆解:先把 PDF 页变成可观察目标
流程不是直接让模型“照着图写 HTML”就结束,而是保存目标截图、PDF 单页和后续中间产物。这样每一步都能回看:生成结果到底偏离了哪块区域。
- 输入文件:
testcase/cbe23568/src.pdf - 目标截图:
testcase/cbe23568/page_snapshot.png - 页面特点:多元素、长文本、二维码、水印和表格结构同时存在。
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 开始试跑。
在比较 PDF 和 HTML 之前,VDiff 会先统一截图范围与尺度,避免把浏览器留白或原图分辨率差异误认为版面问题。
优先截取 page root:找到与 PDF 页面宽度匹配的 HTML 页面容器,只截这个容器,并以其左上角作为坐标原点。
full page 是兜底:如果找不到可靠的页面容器,就截取整个 HTML 文档,保证内容不会遗漏。
PDF 等比例缩放后再比较:令 s = HTML 截图宽 ÷ PDF 截图宽,PDF 的宽和高同时乘以 s,再做视觉与 bbox/layout 对比;这个过程不会修改最终 HTML。
page root 优先full page 兜底
宽、高 × s
bbox / layout / visual
真实服务里,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 修复。下面每一项都可以展开看具体操作。
page_snapshot.png → transcribed.html
testcase/{case}/page_snapshot.png,也就是 PDF 单页截图。src.pdf 元信息,用来保持 case 目录和后续抽取一致。spatial_binding/reproduce.py 和 prompt_templates/html_reproduce*.txt。python -m spatial_binding.reproduce case1 --output-dir output-test。output-test/{case}/transcribed.html。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.py、match.py 和 image_matching*.py 把 PDF block 对到 HTML node。postprocess.py 写回绑定信息,并生成可视化 dashboard;命令线索:python -m spatial_binding case1 --output-dir output-test。bound.html、text_elements.jsonl、image_elements.jsonl。semantic_mapping*.json、dashboard-spatial-binding.html、visual_binding.png。bound.html → 1_font_fixed.html
bound.html 和 text_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.json 和 calibrated_fonts_html.json。dashboard-calibration.html 看哪些文本块被校准。1_font_fixed.html → layout evidence + dashboard
1_font_fixed.html:已经做过字体/文本框校准的候选页面。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.json、layout_measurement.json、verify_result_v2.json。debug_dashboards/ground_truth.html、dashboard-verify.html、verify_screenshot.png。1_css_fixed.html
1_font_fixed.html、ground_truth.json、layout_measurement.json。verify_result_v2.json、目标截图,以及 rubric 生成/验证结果。skills/calibrate_css_spacing_v6.md,根据证据修改 CSS/布局。eval/rubrics_gen.py 和 eval/rubrics_verify.py 把视觉要求写成可检查项,再逐项验证。demo/best_pick.py 选择最优 iteration。css_fixing/iter{N}/snapshot.html 和 metrics.json。1_css_fixed.html,rubric 结果在 rubrics.json 和 rubric_verify_result.json。它不是单次 image-to-code 生成,而是把一个 PDF 页面拆成可被验证的中间状态:先得到可运行 HTML,再把文本、图像和容器绑定到可测元素,随后用真实渲染位置校准字体与盒模型,最后把视觉差异写成 rubric 和修复任务。对我们的主线来说,它提供了一个 candidate → evidence → verifier → repair 的手工参照物。
从 PDF page snapshot 生成初始 HTML,把视觉页面先变成可执行、可截图、可追踪的 candidate。
把 PDF 文本块、图片资产和 HTML 元素建立对应关系,后续 verifier 才知道“错的是哪个元素”。
用渲染测量校准字体、行高、宽高和空间位置,处理纯 prompt 生成最容易漂移的几何误差。
生成 bbox/layout dashboard、rubrics 和 pass/fail 结果,把肉眼差异变成可复查证据。
把失败项和 layout delta 交给 coding agent 修 CSS,再重新截图和比较,形成可迭代闭环。
这个 case 包含淡灰斜向水印、多色 logo、粗标题、编号栏和表格化信息。它展示了 VDiff 为什么要同时保留视觉 rubric 和几何证据:最终结果大部分通过,但 verifier 仍抓到两个细节问题:水印没有形成足够完整的重复斜向带,以及 No. 表头与编号值的水平对齐关系不对。
这个 case 的难点不是长文本,而是强品牌视觉:深蓝绿色上半区、珊瑚橙下半区、白色单色 logo、细分割线、底部白线插画。它说明 workflow 里的 verifier 不能只看文字 OCR,也要检查调色、区块关系、logo 单色化和插画层级。
这个 case 暴露的是更“论文相关”的问题:大号空心数字要压在标题后面,多栏正文要保持行数和基线关系,右侧插画还要保留浅色半透明细节。历史结果的失败项包括插画浅色形状不明显、路由线条数量少一条、正文行数和左右栏基线不一致。这类失败很适合抽象成 multimodal verifier 的 evidence schema。
这个 demo run 更适合看“闭环”本身:同一个上传 PDF 页面留下了 transcribed、bound、font-fixed、verify dashboard、rubric verify 和 iter0 到 iter4 的 CSS 修复快照。rubric verify 是 90/90 通过,但几何指标在不同迭代间仍然波动,说明 rubric pass 和 layout metric 不是同一种信号,后续 verifier 设计需要把两者一起纳入。
这个例子相对难,是因为它同时有品牌 logo、水印、二维码、表格化订单信息、长段正文和底部蓝色链接文本;它还保留了完整的 5 轮 CSS fixing 过程。下面按“开始是什么样 → 做了什么操作 → 变成什么样”的顺序展开。
红框标的是这个 case 里最容易漂移、也最值得 verifier 盯住的区域。它不等于最终一定失败,而是告诉我们 repair loop 主要围绕哪些区域反复校准。
流程不是直接让模型“照着图写 HTML”就结束,而是保存目标截图、PDF 单页和后续中间产物。这样每一步都能回看:生成结果到底偏离了哪块区域。
testcase/cbe23568/src.pdftestcase/cbe23568/page_snapshot.png
第一步把视觉页面转成 HTML/CSS candidate。这个阶段的价值是“先让页面活起来”:可以渲染、截图、对齐、继续被工具测量。
transcribed.html。
只看截图很难知道“哪个 HTML 节点错了”。绑定阶段把 OCR/PDF 文本块、图片资产、容器和 HTML 元素建立对应关系,后面的 verifier 和 repair 才能指向具体对象。
bound.html、layout skeleton 和元素级调试 dashboard。
PDF-to-HTML 最容易漂移的是几何量:字号、行高、段落宽度、表格列宽、二维码位置。校准阶段用渲染测量结果反推 CSS,使页面从“内容像”走向“位置像”。
1_font_fixed.html 和 calibration dashboard。
验证阶段不是简单说“像/不像”,而是生成可复查证据:layout measurement、截图对比、visual rubrics 和 pass/fail 结果。这个 case 的 rubric verify 最终是 90/90 passed。
dashboard-verify.html、verify_screenshot.png 和 rubric_verify_result.json。
这个 case 最值得看的地方是 repair loop。每轮都会产生一个 snapshot 和 metrics:agent 调整 CSS,系统重新渲染并量化最大 top/left/width/height 偏差。几何指标并不总是单调变好,这正说明 verifier 需要同时看 layout metric 和 semantic/visual rubric。
最终输出保存为 1_css_fixed.html。这个结果不是“完美复制 PDF”,而是一个带有可解释证据链的 reproduction:每一步都有中间文件、截图、指标和 rubric 结果可以回查。
output-demo/cbe23568/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 单页输入目录,其中 case1–case16 是 gallery 主用例;每个目录都有 src.pdf 和 page_snapshot.png。
data_generation_loop/output-test/ 里 case1–case13 已有转写、绑定、字体校准、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 主要读取 case1–case16 |
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 PDF | rubric 生成/验证子实验的数据源 |