name: ocr_kb description: 使用多模态大模型逐页读取长文档图片,精确提取文本、LaTeX公式和独立科研配图。支持环境清理、断点恢复、全局编号管理、失败页标记与部分重跑。逐页生成DOCX并每两页核查质量。本 Skill 专用于 Pipeline A(OCR 管道),处理 PDF 输入。中间产物全部放在 resources/,最终交付物放在 outputs/。Pipeline B / C 定义在主 SKILL.md 中。 license: Proprietary. LICENSE.txt has complete terms
论文排版与整理完全工作流 (Iterative OCR & Typesetting Pipeline)
0. 目录规范 (Directory Convention)
所有中间文件和最终产物必须严格遵守以下目录结构,禁止在项目根目录下放置任何生成文件:
项目根目录/
├── resources/
│ ├── pages/ # 切分出的单页 PNG 图片
│ ├── figures/ # 从页面中裁剪出的独立科研配图(全局编号)
│ ├── md/ # 每页的 Markdown 提取结果 (page_1.md, page_2.md, ...)
│ ├── scripts/ # 所有 Python 辅助脚本(可跨任务复用,清理时不删除)
│ ├── compiled_paper.md # 最终汇总的完整 Markdown
│ ├── config.json # 任务配置(源 PDF、格式规范),整个任务期间不变
│ └── checkpoint.json # 运行时进度(当前页、计数器、失败页),随处理更新
├── outputs/
│ ├── <name>_checkpoint_p<N>.docx # 核查点中间版本
│ └── <name>_final_<date>.docx # 最终交付版本
├── ocr_kb/
│ └── SKILL.md # 本文件
└── <source>.pdf # 用户提供的原始 PDF
0.5 resources/config.json 规范 (Task Configuration)
在每次新任务启动时创建,整个任务期间不修改。续作时直接读取。所有管道共用同一 schema。
{
"source_file": "2103.10360v2.pdf",
"source_type": "pdf",
"pipeline": "A",
"format_style": "IEEE",
"template_file": null,
"total_units": 16,
"unit_type": "page",
"created_at": "2026-04-02T13:00:00+08:00"
}
| 字段 | 说明 |
|---|---|
source_file |
用户指定的源文件名(PDF / DOC / DOCX / MD) |
source_type |
文件类型:"pdf" / "doc" / "docx" / "md" |
pipeline |
管道类型:"A"(OCR)/ "B"(重排版)/ "C"(MD 直转) |
format_style |
排版格式(IEEE / APA / 用户自定义 / ...) |
template_file |
用户提供的 .docx 模板路径,无则 null |
total_units |
总处理单元数(Pipeline A = 页数,Pipeline B/C = 章节数) |
unit_type |
进度单位:"page"(Pipeline A)/ "section"(Pipeline B/C) |
created_at |
任务创建时间 |
[!NOTE] Pipeline A 固定使用
source_type: "pdf",pipeline: "A",unit_type: "page"。
0.6 resources/checkpoint.json 规范 (Runtime Checkpoint)
在每完成一个单元(页或章节)的提取 + DOCX 追加后更新。**这是断点恢复的唯一依据。**所有管道共用同一 schema。
{
"current_unit": 7,
"unit_type": "page",
"last_verified_unit": 6,
"total_units": 16,
"docx_path": "outputs/GLM_checkpoint_p6.docx",
"global_figure_count": 4,
"global_table_count": 2,
"global_equation_count": 15,
"needs_review": [3],
"completed_units": [1, 2, 3, 4, 5, 6],
"status": "suspended"
}
| 字段 | 说明 |
|---|---|
current_unit |
下一个要处理的单元编号 |
unit_type |
单元类型:"page"(Pipeline A)/ "section"(Pipeline B/C) |
last_verified_unit |
最后一个通过核查的单元编号 |
total_units |
总单元数(从 config.json 同步) |
docx_path |
当前最新的 DOCX 中间版本路径 |
global_figure_count |
全局配图计数器(只增不减) |
global_table_count |
全局表格计数器(只增不减) |
global_equation_count |
全局公式计数器(只增不减) |
needs_review |
标记为"需要复查"的单元编号列表 |
completed_units |
已完成提取的单元编号列表 |
status |
"running" / "suspended" / "completed" |
更新时机(以 Pipeline A 为例,B/C 将"页"替换为"章节"):
- 每完成一个单元:更新
current_unit、completed_units、计数器 - 每 2 个单元核查后:更新
last_verified_unit - 核查发现问题:向
needs_review添加单元编号 - 每 4 个单元悬挂前:设
status = "suspended",保存 DOCX 中间版本 - 全部完成后:设
status = "completed"
Pipeline A 中间版本命名:使用 _checkpoint_p<N> 表示页码。Pipeline B/C 使用 _checkpoint_s<N> 表示章节。
0.7 脚本接口契约 (Script Contracts)
[!IMPORTANT] 以下脚本如果
resources/scripts/中已存在,直接复用,不要重新生成。仅在不存在时才创建,且必须严格遵守下列接口。
split_pdf.py
用法: python resources/scripts/split_pdf.py <pdf_path> <output_dir> [--dpi 300]
输入: PDF 文件路径,输出目录路径
输出: output_dir/page_1.png, page_2.png, ..., page_N.png
退出码: 0 成功,非 0 失败
stdout: 最后一行输出 "TOTAL_PAGES=N"
extract_figures.py
用法: python resources/scripts/extract_figures.py <page_image> <page_num> <coords_json> <output_dir> <start_index>
输入:
- page_image: 单页 PNG 路径
- page_num: 页码(用于日志)
- coords_json: JSON 字符串,格式 [{"x0%": 10, "y0%": 30, "x1%": 90, "y1%": 70, "caption": "Figure N: ..."}]
- output_dir: 输出目录
- start_index: 全局起始编号(从 checkpoint.global_figure_count + 1 开始)
输出: output_dir/figure_<start_index>.png, figure_<start_index+1>.png, ...
退出码: 0 成功,非 0 失败
stdout: 最后一行输出 "FIGURES_EXTRACTED=K"
generate_docx.py
用法: python resources/scripts/generate_docx.py <compiled_md> <config_json> <output_path>
输入:
- compiled_md: 汇总后的 Markdown 文件路径
- config_json: 任务配置文件路径(读取 format_style 等信息)
- output_path: 输出 DOCX 文件路径
输出: 指定路径的 .docx 文件
退出码: 0 成功,非 0 失败
latex_to_omml.py
用法: python ocr_kb/scripts/latex_to_omml.py <latex_string> [--inline]
输入:
- latex_string: LaTeX 公式字符串(不含 $ 定界符)
- --inline: 可选标志,输出行内 <m:oMath> 而非块级 <m:oMathPara>
输出: 标准输出打印完整的 OMML XML 片段
- 无 --inline时: <m:oMathPara>...</m:oMathPara>
- 有 --inline时: <m:oMath>...</m:oMath>
退出码: 0 成功,非 0 失败
stderr: 转换失败时输出错误信息(不支持的 LaTeX 命令等)
支持范围: 基础运算、分式(\frac)、上下标、根号(\sqrt)、
希腊字母、求和/积分(\sum/\int)、矩阵(\matrix/\pmatrix)、
常用数学符号(\leq/\geq/\neq/\approx/\infty 等)
[!NOTE] 此脚本位于
ocr_kb/scripts/(不在resources/scripts/),因为它是 Skill 自带的工具而非任务生成的临时脚本。所有管道共用此脚本。
1. 逐页精读与追加写入 (Page-by-Page Extraction)
对于切分好的每一张单页图片(resources/pages/page_N.png),执行以下操作:
1.1 纯文本读取
一页一页地用视觉能力精读图片,将文字原汁原味提取出来。
[!CAUTION] 标题格式规范:切勿在文本中保留多余或错误的 Markdown 标题符号(如
#### 3.2.3)。最终生成 DOCX 时排版脚本会自动映射目标模板(如 IEEE 的罗马数字 I, II, A, B 等)。提取的 Markdown 必须使用极其干净的#层级(如##代表一级标题,###代表二级标题),绝对禁止原文档的旧编号或多余的#号残留在最终正文内。
1.2 公式转译
页面中的独立公式或行内公式(包括表格、图注中的单个数学符号如 $d_{model}$),必须全部读成标准 LaTeX 语法(行内使用 $ $,独立块使用 $$ $$)。每个独立公式递增 checkpoint.json 的 global_equation_count。
[!IMPORTANT] 此举是为了在最终生成 DOCX 时,确保 Python 脚本能通过正则表达式精准捕获这两类边界,并调用
latex_to_omml.py将它们全部挂载为 Word 原生 OMML 公式(<m:oMath>或<m:oMathPara>)。切勿遗漏变量的包裹,否则它们只能渲染成普通文本。
1.3 插图识别与智能裁剪
[!IMPORTANT] 禁止直接把整页截图作为图片插入 Markdown。必须从页面中精确识别并裁剪出独立的科研配图。
具体流程:
- 识别:视觉读取当前页时,判断页面中是否存在独立的科研配图(Figure)或独立的表格图(Table 以图片形式呈现的)。
- 定位:确定配图在页面上的大致位置百分比坐标
(x0%, y0%, x1%, y1%)。 - 全局编号:从
checkpoint.json读取global_figure_count,本页新配图从global_figure_count + 1开始编号。裁剪完毕后更新global_figure_count。表格同理使用global_table_count。 - 裁剪:使用
resources/scripts/extract_figures.py或PyMuPDF的page.get_pixmap(clip=Rect(...))方法,按坐标从高分辨率页面渲染中裁剪出单独的配图,保存到resources/figures/figure_N.png(N 为全局编号)。 - 引用:在 Markdown 中使用裁剪后的独立图片路径:

1.4 写入
将当前页的提取结果写入 resources/md/page_N.md。
1.5 更新 Checkpoint
每写完一个 page_N.md 并追加到 DOCX 后,立即更新 checkpoint.json:
current_unit += 1completed_units添加当前页码- 更新
global_figure_count/global_table_count/global_equation_count
2. 逐页 DOCX 生成与双页核查 (Incremental DOCX Generation)
[!IMPORTANT] 不再等到所有页面提取完毕后一次性生成 DOCX。每完成一页 MD 的提取,就立即将该页内容追加到正在构建的 DOCX 文件中。
2.1 单页流程
读一页图片 → 写 page_N.md → 将 page_N.md 内容追加到 DOCX → 更新 checkpoint.json
2.2 双页核查(每 2 页)
每完成 2 页的 DOCX 追加后,必须执行一次自检核查:
- 回顾刚追加的 2 页内容在 DOCX 中的排版效果(字体、公式、配图位置、标题层级)。
- 对照原始页面图片,确认没有信息丢失或格式错乱。
- 核查通过:在
checkpoint.json中更新last_verified_unit。 - 核查不通过:
- 在
checkpoint.json的needs_review数组中记录问题页码。 - 尝试立即修复。如果修复成功,从
needs_review中移除该页码。 - 如果无法修复,不阻塞后续页面处理,但在最终汇总前
needs_review必须清零。
- 在
2.3 上下文极限保护
- 连续处理 4 页后,必须主动悬挂(suspend):
- 将
checkpoint.json的status设为"suspended"。 - 保存当前 DOCX 中间版本到
outputs/(命名规则见 §2.4)。 - 使用
notify_user通知用户发送"继续"以刷新上下文窗口。
- 将
- 恢复后读取
checkpoint.json,从current_unit继续。
2.4 中间版本命名规则
| 类型 | 命名格式 | 示例 |
|---|---|---|
| 核查点中间版本 | outputs/<原文件名>_checkpoint_p<最后完成页码>.docx |
outputs/GLM_checkpoint_p6.docx |
| 最终交付版本 | outputs/<原文件名>_final_<YYYYMMDD>.docx |
outputs/GLM_final_20260402.docx |
[!NOTE] 每次保存新的核查点版本时,保留上一个版本不要删除,以便出问题时可以回退。
3. 最终排版定稿 (Final Typesetting)
3.1 needs_review 清零检查
在进入定稿之前,检查 checkpoint.json 的 needs_review:
- 不为空:列出所有待复查页码,逐页重新核查并修复。全部修复后才可继续。
- 为空:直接进入汇总。
3.2 汇总
将 resources/md/page_1.md ~ page_N.md 按顺序合并为 resources/compiled_paper.md。
3.3 生成最终 DOCX
使用 resources/scripts/generate_docx.py,按 config.json 中指定的格式规范生成最终排版文件,保存到 outputs/。
3.4 排版格式应用
排版参数从 config.json 的 format_params 读取(由主 SKILL.md §2.2 在启动时解析并写入)。
具体格式预设(IEEE / APA / 中国学位论文等)的完整参数表定义在主 SKILL.md §6 排版格式规范库中,本文件不再重复。
Pipeline A 的 DOCX 生成方式:使用 docx/SKILL.md 中的 docx-js 创建新文档,或使用 unpack-edit-pack 方式基于模板编辑。
关于 图片、表格、页眉页脚、分栏、公式 的排版细则,参见主 SKILL.md §5 通用排版细则。
3.5 更新 Checkpoint
设 checkpoint.json 的 status = "completed",记录最终文件路径。
4. 执行口令指引 (Execution Triggers)
4.1 全新任务
用户发送 "按照某某格式排版这个 PDF" 时(主 SKILL.md 路由到 Pipeline A):
- 清理确认:按主 SKILL.md §1.1 执行环境清理确认。
- 选定源文件:按主 SKILL.md §1.2 确认源 PDF 并选择 Pipeline A。
- 创建配置:写
resources/config.json(pipeline: "A",unit_type: "page")。 - 切图:运行
resources/scripts/split_pdf.py。 - 建立进度:创建
resources/checkpoint.json(current_unit = 1,unit_type: "page",所有计数器归零)。 - 逐页循环:按 §1 和 §2 执行。
- 汇总定稿:按 §3 执行。
- 输出报告:按主 SKILL.md §5 输出完成报告。
4.2 断点续作
用户发送 "继续" 时:
- 读取
resources/checkpoint.json。 - 从
current_unit指定的页码继续逐页循环。 - 所有全局计数器从 checkpoint 恢复,不重置。
4.3 部分重跑
用户发送 "重跑第 N 页" 或 "rerun page N" 时:
- 读取
resources/checkpoint.json,确认第 N 页在completed_units中。 - 重新读取
resources/pages/page_N.png,重新执行 §1 的提取流程。 - 覆盖写入
resources/md/page_N.md。 - 注意:如果该页有配图,需要更新对应的
figure_*.png,但不改变全局编号——复用原编号。 - 从
needs_review中移除 N(如果存在)。 - 重新生成最终 DOCX(重新执行 §3.2 ~ §3.3)。
4.4 批量重跑
用户发送 "重跑第 X-Y 页" 时:
按 §4.3 对范围内每一页逐一执行重跑。
5. 完成后结构化总结 (Completion Report)
[!IMPORTANT] 全部页面处理完毕并生成最终 DOCX 后,必须输出以下总结,不可省略。
【完成报告】
源文件:<config.source_file>
输入类型:<config.source_type>
处理管道:Pipeline <config.pipeline>
格式规范:<config.format_style>
总单元数:<checkpoint.total_units>(<config.unit_type>)
提取公式数:<checkpoint.global_equation_count>
裁剪配图数:<checkpoint.global_figure_count>
提取表格数:<checkpoint.global_table_count>
核查点版本数:<outputs/ 中 checkpoint 文件的数量>
最终文件:outputs/<最终文件名>.docx
遗留问题:<needs_review 中的单元编号 / 无>