Install any skill in seconds. Free to start, no credit card required.
Get Started Free →把多页 HTML 报告(主页 + N 子页 + PNG 资产)合并为单一独立 HTML / PDF / XLSX 三件交付物。 Use when finalizing a disease market sizing report and need shareable deliverables — single-file HTML for email/IM(零依赖,双击浏览器开),PDF for print/archive, XLSX for data export. 领域无关、跨疾病通用。 下游 of disease-market-sizing-html-template;上游 of email/IM/print 分发。 Use proactively whenever the user says "做交付物" / "生成 PDF" / "导 Excel" / "合并单文件 HTML"。
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-07 | ✗→✓ | ▲ Improved | 41% | 0% |
| case-01 | ✗→✓ | ▲ Improved | -36% | 0% |
| case-02 | ✗→✓ | ▲ Improved | -15% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 116% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 75% | 0% |
把市场调研项目的 main.html + page_*.html + flowchart_*.png 资产组装成 "双击即可阅读 / 邮件可发 / Excel 可分析"三件套。所有图片 base64 内嵌、PDF 自动 TOC、XLSX schema 驱动。
disease-market-sizing-html-templatedisease-market-sizing-orchestration 流水线article-writing 或 docx召回层 (7 retrieval skill: pubmed-eutils / europepmc-search / clinical-trials-v2 /
aact-bulk-trials / bioc-fulltext-fetch / pubtator-entity-search /
medical-evidence-grading)
↓
内容层 (disease-market-sizing-html-template + market-sizing-mece-foundation
+ decision-tree-with-lp-embedding + evidence-appendix-sync)
↓ 输出: main.html, page_*.html, flowchart_*.png
交付层 (本 skill: report-bundle-builder) ← YOU ARE HERE
↓ 输出: report_standalone.html, .pdf, _data.xlsx, delivery_manifest.json
分发层 (邮件 / IM / 网盘 / 打印)pythonfrom pathlib import Path from build_standalone_html import build_standalone_html result = build_standalone_html( main_html=Path("output/report.html"), sub_pages=[ ("AML", Path("output/page_AML.html")), ("MDS", Path("output/page_MDS.html")), ], png_dir=Path("output"), out_path=Path("output/report_standalone.html"), intra_page_anchors=["decision", "calc", "lp", "evidence"], # 血液项目用,通用场景留 None ) # {"ok": True, "size_mb": 9.3, "png_count": 8, # "missing_pngs": [], "validation": {...}}
关键参数:
sub_pages: [(slug, path), ...] · slug 用作 anchor id(#page-{slug})png_dir: PNG 资产目录(<img src="X.png"> 相对此目录解析)intra_page_anchors: 子页内部 href="#decision" 等改写为 href="#page-{slug}-decision" 的 anchor 名列表;None/空时不改写auto_validate: 默认 True · size > MIN_BUNDLE_BYTES(1 MB)+ anchor_count == 子页数pythonfrom build_pdf import build_pdf # 单遍模式(快) result = build_pdf( html_path=Path("output/report_standalone.html"), out_path=Path("output/report.pdf"), ) # 两遍模式(注入 TOC 页码) result = build_pdf( html_path=Path("output/report.html"), out_path=Path("output/report.pdf"), toc_anchors=[ ("#ch1", "一、执行摘要"), ("#ch2", "二、研究方法"), ], ) # {"ok": True, "size_mb": 9.6, "pages": 32, "mode": "two-pass"}
安全保证:
tempfile.mkstemp 避免并发碰撞pages=None,pages_ok=True(不算失败)详见 chrome-headless-setup.md。
pythonfrom build_xlsx import build_xlsx result = build_xlsx( data={ "overview": [["总 PMID", 200, "PubMed"], ["NCT", 30, "CT.gov"]], "lp_ranking": [["LP1", 8.5, "GRADE A"]], }, schema_yaml=Path("data/xlsx_schema.yaml"), out_path=Path("output/report_data.xlsx"), ) # {"ok": True, "sheets": ["T1_overview", "T2_lp_ranking"]}
Schema YAML 格式:
yamlsheets: - name: T1_overview headers: ["指标", "值", "来源"] column_widths: [22, 16, 28] data_key: overview - name: T2_lp_ranking headers: ["LP 名称", "得分", "证据等级"] column_widths: [30, 12, 18] data_key: lp_ranking
data dict 用 data_key 索引,值是 [[row...], [row...]]Sheet #2 missing 'headers')pythonfrom build_all import build_all_deliverables manifest = build_all_deliverables( report_dir=Path("output"), out_dir=Path("output"), sub_pages=None, # None → 自动扫描 page_*.html toc_anchors=[("#ch1","一、执行摘要")], # 给 PDF xlsx_schema=Path("data/xlsx_schema.yaml"), xlsx_data={...}, disease_slug="lung-tb-china", ) # 写入 output/delivery_manifest.json # 任一失败不阻塞其余,manifest 记录详情
自动检测顺序(main.html):
report_dir/main.htmlreport_dir/report.htmlreport_dir/report_v<N>.html 中版本号最大的(report_v25.html > report_v9.html)部分失败处理:
pdf.fallback_input=Trueok=False, error=skipped详见 bundle-manifest-spec.md。
| 函数 | 默认验证 | auto_validate=False 时 | |-----|---------|----------------------| | build_standalone_html | size > 1 MB,anchor_count == 子页数 | 跳过 | | build_pdf | size > 1 MB,pages ≥ 1(或 pypdf 不可用 → 不算失败) | 跳过 | | build_xlsx | size > 1 KB,sheet_count ≥ 1 | 跳过 |
阈值常量集中在 _validate.py:HTML_MIN_SIZE_BYTES / PDF_MIN_SIZE_BYTES / PDF_MIN_PAGES / XLSX_MIN_SIZE_BYTES / XLSX_MIN_SHEET_COUNT。
| 症状 | 原因 | 修复 | |------|------|------| | RuntimeError: No Chrome/Edge/Chromium found | PDF 渲染缺浏览器 | 装 Chrome 或显式 chrome_path= · 见 chrome-headless-setup.md | | ValueError: Main HTML missing <body> | 主页 HTML 不含 <body> | 检查 main_html 是否真页面 | | ValueError: Sheet #N missing 'X' | xlsx schema 字段缺失 | 补全 schema 的 name/headers/data_key | | KeyError: data['xxx'] missing | xlsx schema 引用 data_key 在 data dict 中缺失 | 对齐 schema.sheets].data_key 与 data 的 key | | HTML size < 1 MB,validation.size_ok=False | 内容太少或 PNG 丢失 | 检查 png_dir 是否对、PNG 文件存在 | | PDF pages 远低于预期 | HTML 渲染异常或 Chrome 崩溃 | 用 Chrome 手动开 HTML 看是否正常,看 Chrome stderr | | anchor_count < anchor_target | 子页 HTML 不含 main 标签或 slug 命名冲突 | 子页必须有 <main> 包裹主体内容 | | build_all 报 xlsx skipped | xlsx_schema 或 xlsx_data 任一为 None | 同时传两个,或接受 xlsx ok=False | | Output HTML 在浏览器打不开 | 主页编码非 UTF-8 | 重新生成主页保证 UTF-8 BOM 无 | | result["missing_pngs"] 非空 | 主页/子页引用了 png_dir 不存在的 PNG | 检查 PNG 文件名拼写或是否生成 | | 两遍模式 Pass 2 失败但 HTML 未损坏 | 备份恢复机制工作正常 | 排查 Chrome 错误,原 HTML 已自动从备份恢复 |
X | None 语法)httpx + lxml + pyyaml + openpyxl + 可选 pypdf(无则 PDF 不能测量页数,但仍能渲染)Other measured skills in the registry, with their headline benchmark lift.