ocr-kb

star 243

使用多模态大模型逐页读取长文档图片,精确提取文本、LaTeX公式和独立科研配图。支持环境清理、断点恢复、全局编号管理、失败页标记与部分重跑。逐页生成DOCX并每两页核查质量。本 Skill 专用于 Pipeline A(OCR 管道),处理 PDF 输入。中间产物全部放在 resources/,最终交付物放在 outputs/。Pipeline B / C 定义在主 SKILL.md 中。

TFboy1 By TFboy1 schedule Updated 4/8/2026

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_unitcompleted_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.jsonglobal_equation_count

[!IMPORTANT] 此举是为了在最终生成 DOCX 时,确保 Python 脚本能通过正则表达式精准捕获这两类边界,并调用 latex_to_omml.py 将它们全部挂载为 Word 原生 OMML 公式(<m:oMath><m:oMathPara>)。切勿遗漏变量的包裹,否则它们只能渲染成普通文本。

1.3 插图识别与智能裁剪

[!IMPORTANT] 禁止直接把整页截图作为图片插入 Markdown。必须从页面中精确识别并裁剪出独立的科研配图

具体流程:

  1. 识别:视觉读取当前页时,判断页面中是否存在独立的科研配图(Figure)或独立的表格图(Table 以图片形式呈现的)。
  2. 定位:确定配图在页面上的大致位置百分比坐标 (x0%, y0%, x1%, y1%)
  3. 全局编号:从 checkpoint.json 读取 global_figure_count,本页新配图从 global_figure_count + 1 开始编号。裁剪完毕后更新 global_figure_count。表格同理使用 global_table_count
  4. 裁剪:使用 resources/scripts/extract_figures.pyPyMuPDFpage.get_pixmap(clip=Rect(...)) 方法,按坐标从高分辨率页面渲染中裁剪出单独的配图,保存到 resources/figures/figure_N.png(N 为全局编号)。
  5. 引用:在 Markdown 中使用裁剪后的独立图片路径:
    ![Figure 4: 图注说明](resources/figures/figure_4.png)
    

1.4 写入

将当前页的提取结果写入 resources/md/page_N.md

1.5 更新 Checkpoint

每写完一个 page_N.md 并追加到 DOCX 后,立即更新 checkpoint.json

  • current_unit += 1
  • completed_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 追加后,必须执行一次自检核查

  1. 回顾刚追加的 2 页内容在 DOCX 中的排版效果(字体、公式、配图位置、标题层级)。
  2. 对照原始页面图片,确认没有信息丢失或格式错乱。
  3. 核查通过:在 checkpoint.json 中更新 last_verified_unit
  4. 核查不通过
    • checkpoint.jsonneeds_review 数组中记录问题页码。
    • 尝试立即修复。如果修复成功,从 needs_review 中移除该页码。
    • 如果无法修复,不阻塞后续页面处理,但在最终汇总前 needs_review 必须清零。

2.3 上下文极限保护

  • 连续处理 4 页后,必须主动悬挂(suspend):
    1. checkpoint.jsonstatus 设为 "suspended"
    2. 保存当前 DOCX 中间版本到 outputs/(命名规则见 §2.4)。
    3. 使用 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.jsonneeds_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.jsonformat_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.jsonstatus = "completed",记录最终文件路径。


4. 执行口令指引 (Execution Triggers)

4.1 全新任务

用户发送 "按照某某格式排版这个 PDF" 时(主 SKILL.md 路由到 Pipeline A):

  1. 清理确认:按主 SKILL.md §1.1 执行环境清理确认。
  2. 选定源文件:按主 SKILL.md §1.2 确认源 PDF 并选择 Pipeline A。
  3. 创建配置:写 resources/config.jsonpipeline: "A", unit_type: "page")。
  4. 切图:运行 resources/scripts/split_pdf.py
  5. 建立进度:创建 resources/checkpoint.jsoncurrent_unit = 1, unit_type: "page",所有计数器归零)。
  6. 逐页循环:按 §1 和 §2 执行。
  7. 汇总定稿:按 §3 执行。
  8. 输出报告:按主 SKILL.md §5 输出完成报告。

4.2 断点续作

用户发送 "继续" 时:

  1. 读取 resources/checkpoint.json
  2. current_unit 指定的页码继续逐页循环。
  3. 所有全局计数器从 checkpoint 恢复,不重置。

4.3 部分重跑

用户发送 "重跑第 N 页" 或 "rerun page N" 时:

  1. 读取 resources/checkpoint.json,确认第 N 页在 completed_units 中。
  2. 重新读取 resources/pages/page_N.png,重新执行 §1 的提取流程。
  3. 覆盖写入 resources/md/page_N.md
  4. 注意:如果该页有配图,需要更新对应的 figure_*.png,但不改变全局编号——复用原编号。
  5. needs_review 中移除 N(如果存在)。
  6. 重新生成最终 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 中的单元编号 / 无>

Install via CLI
npx skills add https://github.com/TFboy1/academic-paper-writer --skill ocr-kb
Repository Details
star Stars 243
call_split Forks 14
navigation Branch main
article Path SKILL.md
More from Creator