Office → Markdown Skill (v5.2.0)
将办公文档抽取为干净 Markdown,面向知识库文档提取。所有转换逻辑位于 scripts/,按需调用对应脚本。
> 🔴 优先级铁律(v4.4 新增)
> 需要处理任何 Office 文件(PDF / PPTX / DOCX / XLSX / CSV / .doc)内容时,必须优先使用本技能。
> 严禁绕过本技能自行编写一次性提取脚本(zip+XML、临时解析器等)—— 临时脚本不可复用、
> 不统一、无 Lint 与 frontmatter 环节,是知识库提取质量事故的主要来源。
> 若技能输出不满足需求(漏表格、漏备注、编码异常等),正确做法是增强本技能脚本后复用,
> 而非另起炉灶。详见 §7.3。
> 安全与依赖说明
> - 依赖固定版本、隔离安装至 %LOCALAPPDATA%/office_md_deps(Windows 下解析为
> C:\Users\<user>\AppData\Local\office_md_deps),不污染系统 Python/Node,也不会在 C 盘根目录创建文件。
> - 技能不调用任何云端视觉 API、不发网络请求、不写死云端模型名。 图片型/扫描页导出 PNG 素材并插入 OCR-REQUIRED 占位标记后,识别有两条路径:
> ① 本地 OCR 引擎(scripts/ocr-local.py:RapidOCR + ONNX Runtime,纯 CPU、离线、模型随包内置)—— 默认优先,快、不占模型上下文、错误形态是「看得见的字符错」而非「看不见的幻觉」;
> ② 模型视觉 OCR —— 本地引擎不可用时的降级路径,由任务执行时指定的大模型读图回填。
> 两条路径都不把图片外发。本地引擎是确定性识别器(不做生成式改写),与云端 VLM 性质不同,判定口径见「反例与黑名单」第 1、2 条。
> - 已彻底移除旧版 Anthropic / 可配置视觉 API 的全部硬编码与配置。
Script Reference
| Format | Extensions | Script |
|---|---|---|
| PDF(文本 + 表格,可选 fitz 通道) | .pdf |
scripts/pdf-to-md.py |
| EPUB(目录 + 正文,零三方依赖) | .epub |
scripts/epub-to-md.py |
| Word(高保真) | .docx |
scripts/docx-to-md.py |
| 旧版 Word | .doc |
scripts/doc-to-md.js(Node / word-extractor) |
| PowerPoint(文本 + 表格 + 演讲者备注) | .pptx |
scripts/pptx-to-md.py |
| Excel | .xlsx, .xls |
scripts/xlsx-to-md.py |
| CSV | .csv |
scripts/csv-to-md.py |
| 视觉素材准备(渲染/导出 PNG + OCR-REQUIRED 标记) | — | scripts/_ocr_assets.py |
| 扫描页本地 OCR 回填(RapidOCR,批量 + 覆盖度校验) | — | scripts/ocr-local.py |
| 后续步骤 注入 frontmatter + 源指纹 | — | scripts/inject-frontmatter.py |
| 后续步骤 Lint(OCR 待核实 / 待回填校验) | — | scripts/lint-md.py |
| 工程校验 资产一致性(版本号 / 口径 / 目录清单,见 §7.4) | — | scripts/check_asset_sync.py |
Workflow
1. Confirm conversion intent
触发判定(v4.4 放宽):只要任务需要读取或解析 Office 文件的内容即触发,不限于用户说"转成 Markdown"。
典型触发:
- 用户要求转换 / 提取 / 抽取 / 解析 / 读取某办公文档;
- 知识库 ingest / 摄取 / 入库流程中需要落
_extract缓存; - 需要把文档内容整理进报告、wiki、表格。
不触发:仅上传文件但无任何内容处理要求;或任务与 Office 文件无关。
2. Run the matching script (text-only pass first)
python scripts/pdf-to-md.py in.pdf out.md [--text-engine pdfplumber|fitz] [--table-strategy lines|text|auto]
python scripts/epub-to-md.py in.epub out.md [--no-toc] [--ocr-images]
python scripts/docx-to-md.py in.docx out.md [--no-headers]
python scripts/pptx-to-md.py in.pptx out.md [--table-mode md|tsv] [--no-notes]
python scripts/xlsx-to-md.py in.xlsx out.md [--formulas] [--skip-hidden] [--no-merge-fill]
python scripts/csv-to-md.py in.csv out.md
node scripts/doc-to-md.js in.doc out.md
- PDF 文本引擎默认
pdfplumber(带表格);个别中文 PDF 出现乱码时改用--text-engine fitz
(PyMuPDF 文本通道,图片页渲染本就用 fitz)。
--table-strategy:lines默认(依赖表格边框线);无边框 / 空白对齐表格用text;
auto为「lines 优先,页面完全无表时回退 text」。 - 每个脚本先检查依赖是否可导入,可导入则跳过 pip(仅缺失时联网装固定版本到
%LOCALAPPDATA%/office_md_deps);该守卫使重复抽取不再受网络策略拖累。
2.1 格式专项抽取要点与易漏点
各格式"结构性内容"的归属与易失点不同。此表是格式易漏点的唯一定义处
(标题不挂版本号,避免随迭代陈旧;每行自带修复版本标记):
抽取后按「必须覆盖」列逐项核对;§7.3 定位缺口时引用本表,不再另处复述。
Edge Cases 表只列处置动作(换参 / 绕行 / 兜底),不重复此处的清单。
| 格式 | 必须覆盖的结构性内容 | 已知易漏点 / 失真(✅ 已修 · ⚠️ 未修或需换参) |
|---|---|---|
| XLSX | 每个 sheet 的真表头行、多段不连续表格、纵向合并单元格的类别值、横向合并区清单、隐藏/空 sheet 标记 | ✅ 把 sheet 顶部「文件名称/文件编号/版本号」横幅或公司名当作表头 → 真表头沦为数据行(v5.0 修);✅ nan / Unnamed: 噪音(v5.0 修);⚠️ 横向合并区按设计不填充(避免整行重复值),仅输出 <!-- merged-cells: ... --> 清单,核对时勿误判为漏值 |
| DOCX | 正文段落与表格(mammoth)、页眉 / 页脚(含其中表格)、脚注/尾注/批注、表格内图片 | ✅ mammoth 不输出页眉页脚 → 交付件编号 / 版本 / 页码 / 修订日期整块丢失(v5.0 修) |
| 正文文本、表格(结构化)、扫描页 OCR 素材 | ✅ 表格区域被「纯文本 + 表格」双重输出(v5.0 修);⚠️ 无边框 / 空白对齐表格被 lines 策略漏检 → 换 --table-strategy text;⚠️ 跨页表格被拆成多表,按表头重复出现识别 |
|
| EPUB | 目录(TOC)、按 spine 顺序的正文、章节标题层级、表格 | ⚠️ toc.ncx 缺失时目录段为空(正文内的目录列表仍会被抽出,可据此编译);⚠️ 图片默认不导出(reflowable epub 正文为文本,图片多为插图而非扫描页),需素材时加 --ocr-images |
| PPTX | 文本框、表格、演讲者备注 | ✅ 只处理 has_text_frame 会漏掉全部表格与备注(v4.4 修);⚠️ 图表 / 组合形状(groupShape)内的文本 |
- 若文档含图片型/扫描页,脚本会按页面判级自动渲染/导出素材到
out.md_assets/*.png并插入
<!-- OCR-REQUIRED ... -->占位标记(不产生任何图像外发,也不调用模型): - 常规页(A4/B5/Letter 等):整页单图,dpi 自适应保证长边 ≈ 1800px,不切块;
- 骑缝拼版(A3 两页展开,宽高比 ≈ √2 且左右/上下内容对称):沿中线拆为两幅半页(L/R 或 T/B),
半页 dpi 自适应 ≈ A4 级单图,既保真又避免无意义切块(v5.1.0 新增); - 高密度页(物理面积 ≥ A3 的 85% 且非骑缝):300dpi 网格切块,逐块独立
OCR-REQUIRED。
3. 🔴 CHECKPOINT · OCR 回填(本地引擎优先,模型视觉兜底)
图片型/扫描页的素材已就绪。按顺序执行两条路径:
路径 A(默认)· 本地 OCR 引擎
python scripts/ocr-local.py out.md --report ocr_report.json
- 一次加载模型、批量识别全部
OCR-REQUIRED块并回填为OCR-EXTRACTED(块内标注engine=rapidocr:<模型名>,可复现)。 - 纯 CPU、离线、图片不出本机。本机实测 约 1.3–2.5 s/页(首图含约 1.5 s 模型加载,之后复用)。
- 内建双层校验(判据见 §5.2):
- 硬校验(计入未回填,退出码
2):页到齐度 / 空产出 / 素材缺失; - 软提示(不阻断,退出码仍
0):覆盖度偏离 / 低置信行 —— 写入块内提示并在报告列出。
路径 B(降级)· 模型视觉 OCR
仅当 ocr-local.py 退出码为 1(依赖或引擎不可用)或 2(部分块未回填)时使用:
- 读取步骤 2 产出的
out.md,定位剩余的<!-- OCR-REQUIRED page=N asset="..." -->块。 - 用多模态能力读取
asset指向的 PNG(位于out.md_assets/)。 - 将识别出的 Markdown 回填,替换占位块为:
```
<!-- OCR-EXTRACTED page=N 待核实 engine=<实际执行方> -->
> ⚠️ 本页由视觉 OCR 提取,内容待核实
<识别出的 Markdown>
<!-- /OCR-EXTRACTED -->
``
4. 若某页确实无法识别,保留OCR-REQUIRED` 占位并在汇报中说明,不得静默丢弃。
> 🛑 两条路径共同红线:不调用任何云端视觉 API、不把图片外发、不让抽取脚本自调模型。
> 由脚本(ocr-local.py)批量回填仍是本地确定性识别,不属于「脚本自调模型」。
🔴 转录纪律(知识库提取硬约束,缺一不可):
- 逐字、完整:转录图片页(含切分图块)的全部可识别文本——正文、表格(含每一行每一列数值)、脚注、小字警告、参数范围上下限、页眉页脚——不得概括、省略、意译或改写。
- 仅当某处确实无法辨识时,用 ▢ 或 [?] 占位并保留其原位置,不得用「略」「省略」「详见原表」「建议以原版手册为准」等省略性表述带过。
- A3+ 高密度页会被脚本自动切分为多个 OCR-REQUIRED 图块(Tile r-c 或 Half L/R);常规页为整页单图。若为图块/半页,逐块独立转录,再按原顺序拼接;禁止跨块丢内容。
- 这是知识库溯源数据,宁可多录不可漏录;准确性由「(待核实)」标记 + Lint + 人工复核兜底,不应以"省略"替代。
> 路径 A 的本地引擎与路径 B 的视觉模型都不在抽取脚本里配置 —— 换引擎 / 换模型均不影响
> pdf-to-md.py 等抽取脚本。无论走哪条路径回填,都必须遵守上述逐字完整转录纪律。
🔴 CHECKPOINT · 回填前自检(不满足则不得视为完成):
- [ ] 已读完全部 OCR-REQUIRED 图块(含 Tile r-c / Half L/R 分块),逐块独立转录后按序拼接,无跨块丢内容?
- [ ] 无法辨识处已用 [?] 占位,而非「省略 / 详见原表 / 以原版手册为准」等省略性表述?
- [ ] 表格每一行每一列数值均已录入,无整列 / 整行留空?
4. Inject frontmatter + source fingerprint(后续步骤)
抽取脚本只输出纯 Markdown。由本步骤注入知识库 frontmatter 与源文件指纹:
python scripts/inject-frontmatter.py out.md --source in.pdf \
--title "血气分析仪手册" --type manual --tags "血气,pH,iCa" --sources "https://..."
字段:title / type / created / updated / tags / sources / source_fingerprint
(source_fingerprint = sha256:.. size:.. mtime:..,用于溯源与去重)。
5. Lint(每批编译后执行)
python scripts/lint-md.py ./output
扫描全部 .md:
- 对 OCR-EXTRACTED 块确保带可见「(待核实)」标记,缺失自动补入。
- 统计 OCR-REQUIRED 占位数量(即尚未回填的页数),供流水线核对。
- 对缺 frontmatter 的文件给 INFO 提示。
- 结构欠提取检测(OCR-EXTRACTED 块内):见 §5.1,是「以厂商原版为准」「详见原表」等省略性表述无法识别的兜底。
5.1 结构欠提取检测(针对"占位过多"型漏读)
[?] 是逐字纪律下唯一合法的占位符,但模型可能把"可读"误标为"不可读"骗过规则。本检查通过结构化门控抓出此类伪装欠提取:
| 信号 | 触发条件 | 默认阈值 |
|---|---|---|
[?] 绝对数 |
OCR-EXTRACTED 块内 [?] 数量 |
≥ 5 即 [WARN] |
[?] 行占比 |
含 [?] 的数据行 ÷ 全部数据行 |
≥ 30% 且总单元格 ≥ 20 即 [WARN] |
| 薄回填 | OCR-EXTRACTED 正文长度 + 存在 OCR-REQUIRED 引用 |
< 200 字符 且 REQUIRED ≥ 1 即 [WARN] |
| 省略性表述 | 命中"省略 / 从略 / 略去 / 详见原表 / 未能逐一 / 以厂商原版 / 以原版手册为准" 等 | 命中即 [WARN] |
四类信号任一触发即在 OCR-EXTRACTED 块所在行打 [WARN]结构欠提取=...,并在文末汇总触发根因。命中即应回到原 out.md_assets/page_*.png 图块,按 §3 转录纪律重做视觉识别——而非改写为省略性表述绕过。
5.2 OCR 覆盖度校验(双层 · 由 ocr-local.py 实施)
本地 OCR 回填时同步做两层校验。判据必须依赖独立信号:若只用 OCR 自身产出(检出框)判断,
就是自证;故硬校验 H2 用图像墨迹量(引擎看不到的量)与检出框交叉核对。
| 层 | 信号 | 判据 | 处置 |
|---|---|---|---|
| 硬 H1 | 页到齐度 | 成功回填数 < OCR-REQUIRED 块数 |
计入未回填,退出码 2 |
| 硬 H2 | 空产出 | 图有墨迹(ink > 0.5%)却 0 行检出 |
计入未回填,退出码 2 |
| 硬 H3 | 素材缺失 | 占位块引用的 PNG 不存在 | 计入未回填,退出码 2 |
| 软 S1 | 覆盖度偏离 | 检出框并集 / 墨迹比 cov/ink < 2.0 |
仅块内提示 + 报告,不阻断 |
| 软 S2 | 低置信偏多 | 置信度 < 0.80 的行占比 > 10% |
仅块内提示 + 报告,不阻断 |
阈值标定来源(2026-09-30,5 种合成页型实测):纯文本满页 8.17 / 短页 8.16 / 稀疏页 8.08 /
浅灰字 8.73 / 含大图 0.24 → 提示线取 2.0(远离文本页常态,容忍图文混排页)。
> ⚠️ 已知局限(显式化,不得当作已解决):cov/ink 无法区分「页面含大量图形」与「OCR 漏检」 ——
> 含大图页实测 0.24,与真实漏检在该指标上不可分辨。故 S1 仅作人工复核提示,不作自动判定。
> 要真正区分须引入版面检测(分割文字区 / 图区),属另一量级改动,当前未实现。
Lint 会把块内「覆盖度提示 / 低置信提示」扫为 [WARN],作为统一质检出口。
🔴 CHECKPOINT · Lint 命中 [WARN] 时 STOP:
- 命中省略表述或结构欠提取 → 停下,回到 out.md_assets/page_*.png 图块按 §3 重做视觉识别。
- 不得改写为省略性表述绕过、不得静默跳过、不得直接交付带 [WARN] 的产物。
6. Present the file
用 present_files 打开产出 .md,并简要说明:文件类型与页数/行数、是否含待 OCR 页及素材路径、frontmatter 是否已注入、Lint 结果。
How vision works (PDF / PPTX / DOCX)
脚本采用两通道策略,但视觉通道由 Agent 在脚本之外完成:
1. 文本通道(脚本内,始终运行,无外部调用):PDF 用 pdfplumber/fitz 抽文本与表格;DOCX 用 mammoth→markdownify;PPTX 用 python-pptx。
2. 素材导出通道(脚本内):图片型页按判级渲染或导出为 PNG,存入 <md>_assets/,并插入 OCR-REQUIRED 标记(常规页整页单图;A3+ 高密度页网格切块)。
3. OCR 回填通道:① 默认由 scripts/ocr-local.py 用本地引擎(RapidOCR)批量识别回填;
② 引擎不可用或部分未回填时,降级为 Agent 视觉 OCR(读 PNG → 任务指定的大模型识别 → 回填 OCR-EXTRACTED)。
Edge Cases & Failure Handling
| 触发条件 | 一线修复 | 仍失败兜底 |
|---|---|---|
| 纯扫描 PDF(无文本层) | 全部页导出 PNG + OCR-REQUIRED → 进入步骤 3 🔴 CHECKPOINT,先跑 ocr-local.py 本地回填 |
引擎不可用或未回填 → 降级 Agent 视觉 OCR;Lint 校验「(待核实)」;覆盖度/结构欠提取告警 → 回图块重做 |
| 混合 PDF(部分文本/部分图片) | 仅图片页导出素材 + 标记;文本页正常抽取 | 图片页走 OCR 通道,文本页走文本通道,互不阻塞 |
旧版 .doc(文本型) |
用 doc-to-md.js(word-extractor,含中文编码),输出正文文本 |
若页眉页脚 / 表格结构 / 内嵌图必需 → 按下行图片型路径转换格式后重跑 |
旧版 .doc(图片型:正文即扫描图) |
v5.0 仍未支持素材导出——word-extractor 只取正文文本,无图像 / 页眉 API,因此不会产出 OCR-REQUIRED。绕行:① 另存 PDF → pdf-to-md.py(走判级渲染,自动导出 PNG + OCR-REQUIRED);② 另存 .docx → docx-to-md.py(内嵌图片通道同样导出 PNG + OCR-REQUIRED)。无 GUI 时用 soffice --headless --convert-to pdf --outdir <dir> <file.doc> |
两条路径均不可用(无法转换 / 加密)→ 显式标注「待补」并人工处理,不得静默丢弃图片页 |
| 密码保护文件 | 脚本报错退出并给出明确提示 | 停止并询问用户:提供密码或另存无密版后重跑 |
| 依赖安装失败 | pip/npm 报错退出 → 检查网络 / LOCALAPPDATA 权限后重试 | 仍失败 → 报告缺依赖清单,交用户离线安装;不静默跳过 |
| 超大 PDF(50+ 图片页) | 全部导出 PNG + 标记;Agent 分页 OCR | 分批回填并拼接,避免一次性长上下文溢出 |
| CSV 编码错误 | 自动以 latin-1 重试 | 仍乱码 → 标注「编码存疑」并报告,不静默丢弃 |
| OCR 结果不可靠 | OCR-EXTRACTED 块强制带「(待核实)」→ Lint 校验 |
人工复核兜底;命中覆盖度 / 低置信 / 省略 / 结构欠提取 → 回图块重做 |
| DOCX 页眉/脚注疑漏 | v5.0 起脚本自动补抽页眉页脚(含表格)与脚注/尾注/批注;检查产出是否含 ## 页眉 / 页脚 章节 |
若为域代码/艺术字页眉,--no-headers 关闭后人工核对原件 |
| XLSX 表头疑似错位 | v5.0 起脚本识别"文件信息区"并剥离单值横幅行,表头取真表头行;核对产出中是否出现 文件名称 之类被当作表头 |
仍错位 → 该 sheet 用 --no-merge-fill 重跑对比;必要时人工指定表头行后入库 |
| PDF 表格重复输出 | v5.0 起表格区域内字符已从文本通道剔除;核对产出中同一内容是否出现两次 | 仍重复 → 换 --table-strategy text 或 --text-engine fitz 复跑比对 |
| XLSX 隐藏/空 sheet | 隐藏 sheet 标 <!-- SHEET hidden -->,空 sheet 标 <!-- EMPTY SHEET -->;入库前可按标记过滤 |
需完全跳过时用 --skip-hidden |
🚫 反例与黑名单(不要做什么)
执行本技能时,严禁以下行为:
- ❌ 调用云端视觉 API / 发网络请求 —— 抽取脚本与
_ocr_assets.py不 import 任何 HTTP 客户端或云端 OCR SDK,不读VISION_*类环境变量,图片不外发。
✅ 明确允许的例外:scripts/ocr-local.py使用本地 OCR 引擎(RapidOCR + ONNX Runtime)—— 纯 CPU、离线、模型随包内置、确定性识别(不做生成式改写,不会产生「看似合理的错句」)。它只依赖隔离目录内安装的 Python 包,除首次pip install外不访问网络。 - ❌ 写死任何云端模型名 / API 地址 / Key —— 技能内不得出现
api.anthropic.com、api.openai.com、具体gpt-4o/claude-*等字符串或默认云端模型配置。换模型由任务调度决定。
✅ 例外与区别:本地引擎的包名与版本(rapidocr==3.9.2、onnxruntime==1.30.0)按「依赖固定版本」纪律写死 —— 这是可复现的确定性依赖,与「把技能绑死到某个云服务」性质不同。 - ❌ 把图片页外发给第三方后再拼回 —— 图片素材只落本地
<md>_assets/,OCR 在本地由任务模型完成;不触发任何外部图像上传。 - ❌ 跳过 Lint —— 每批编译后必须跑
lint-md.py,确保OCR-EXTRACTED带「(待核实)」、OCR-REQUIRED被清点。 - ❌ 在 C 盘根目录创建依赖 —— 依赖固定装至
%LOCALAPPDATA%/office_md_deps,绝不写/tmp/office_md_deps或C:\tmp\...。 - ❌ 让抽取脚本输出 frontmatter —— 抽取脚本只输出纯 Markdown;frontmatter 由
inject-frontmatter.py后续注入。 - ❌ 把 OCR 结果当权威数据 —— 无论本地引擎还是视觉模型,OCR 均非 100% 可靠;必须保留「(待核实)」标记并提示人工复核。
注意两类引擎的失败形态不同:本地引擎错在形近字混淆(可见、可校对),视觉模型还可能有看似合理的改写(不可见)。
7. 复用纪律(Skill Reuse Discipline)
7.1 优先使用,不重复造轮子
涉及 Office 文件内容的任何任务,先加载本技能并选对应脚本;不得用一次性脚本替代。
7.2 依赖缺失不是绕行理由
脚本会在首次运行时把固定版本依赖装到 %LOCALAPPDATA%/office_md_deps。
若 import xxx 失败,正确处理是:让脚本自行安装,或手动
pip install --target %LOCALAPPDATA%/office_md_deps <pkg==ver> 后重跑;
不能因为"本机没装 python-pptx"就放弃技能改自写脚本。
7.3 输出不满足 → 增强技能,而非另起炉灶
正确流程:
- 复现:用技能脚本跑一遍,保存输出。
- 定位缺口:与源文件实际内容量对比(页数、字符数、是否含表格/备注/图表)。
- 各格式已知易漏点清单见 §2.1 表(该项为本技能唯一定义处);表中标「✅ 已修」的条目
均为真实发生过的漏抽事故,优先按其复现。
- 核对方法:抽取后统计产出中的表格数 / 字符数,与源文件结构计数(openpyxl 非空单元格数、
python-docx 表格数、pdfplumberfind_tables()数)比对;覆盖率 < 95% 继续查漏。 - 改脚本:在
scripts/对应转换器内补全,保持"抽取脚本只输出纯 Markdown"的约定。 - 回归:重跑并与源文件体量对比,覆盖率 < 95% 视为仍未达标,继续查漏。
- 记录:更新
CHANGELOG.md与本文件版本号;并执行 §7.4 的资产同步校验(多副本防漂移),
让下次复用直接受益。
> 教训实例(2026-09-11):Charter DCP 汇报(120 页 PPT)用 v4.3.3 抽取仅得 17,010 字符,
> 因只处理 has_text_frame 而漏掉 89 个表格 + 44 条备注;Agent 未走技能而自写 zip+XML
> 脚本(75,033 字符)。正确做法应是发现漏抽后增强 pptx-to-md.py,v4.4 补全后达 74,738 字符
> (覆盖率 99.6%),全项目复用。
7.4 🔴 改动实现后必扫「资产清单」(多副本防漂移)
为什么:本技能的事实(版本号、行为口径、脚本清单)存在多份副本 ——
SKILL.md / CHANGELOG.md / skill-card.md / README.md / examples/basic-usage.sh。
改实现时若只更新其中一份,其余副本会静默漂移;而读者没有理由去怀疑一份看起来正常的文档。
> 实例(v5.2.0,2026-09-30):SKILL.md 已改为「本地引擎优先」,而 README.md / skill-card.md /
> examples/basic-usage.sh 三份副本仍写「脚本不做 OCR、由大模型回填」。一个照着 README 执行的 Agent
> 会跳过本地 OCR、直奔视觉模型——与本技能的既定架构相反。错误的文档比没有文档更危险。
怎么防:改完 scripts/ 或本文件正文后,必须跑机器校验(不靠记忆逐份核对):
python scripts/check_asset_sync.py # 退出码 0=一致 / 1=存在漂移
校验三面:
| 面 | 内容 |
|---|---|
| A 版本号 | SKILL.md 标题 / CHANGELOG.md 顶部条目 / skill-card.md version / README.md 标题,四处必须一致 |
| B 口径 | README.md / skill-card.md / examples 须已反映「本地引擎」路径;面向读者的副本不得残留淘汰口径(如「脚本不做 OCR」) |
| C 目录清单 | README.md 须列出 scripts/ 全部脚本与 tests/ 全部测试 |
纪律:校验未过 ⇒ 本次改动未完成,不得标记版本发布。
(与「没有校验器在读的阈值表必然漂移」同族:多副本事实若无校验器在读,必然漂移。)
校验器自身也有护栏:check_asset_sync.py 的有效性由 tests/test_asset_sync.py(8 项)守护 ——
它在临时副本上注入 A/B/C 三面「漂移」变异,断言坏时会红、且失败面紧致、且真源零污染。
⚠️ 若改动本校验器的判据(三面定义 / 标志词 / 淘汰口径 / 参数处理),必须跑该测试:
> 实测证据(v5.2.0):把 --root 处理删掉后,基线用例 t1 依然全绿 —— 因为校验器退回扫真源,
> 而真源恰好是全绿的。只有 t2–t7 会红。⇒ 单看「基线全绿」根本发现不了校验器已经失效;
> 变异用例是唯一能察觉的探针。「全绿」只有在能证明坏时会红之后才算证据。
> 注:check_asset_sync.py 的淘汰口径扫描不含本文件 —— §7.4 自身需引用这些措辞作反例,
> 纳入扫描会自伤。故面 B 的负向词的适用范围严格限定为「面向读者的副本」。
Engineering
参考 office-to-md-v2 的工程成熟度补充:
- README.md — 总览与快速开始
- CHANGELOG.md — 版本记录(当前版本以本文件顶部条目与 skill-card.md 为准,此处不再复述版本号)
- tests/test_converters.py — 格式转换冒烟测试(python tests/test_converters.py,缺依赖自动跳过)
- tests/test_ocr_local.py — 本地 OCR 通道回归护栏(python tests/test_ocr_local.py):7 项 =
正常路径(素材导出→回填→零 OCR-REQUIRED 残留)+ 退化退出码契约 d1–d4(1/2/3/2)
+ 软校验 s1/s2(覆盖度提示、低置信提示按占比)。引擎缺失自动跳过,d1 除外(它测的就是引擎不可用)
- tests/test_asset_sync.py — 资产校验器自身有效性护栏(python tests/test_asset_sync.py):8 项 =
基线全绿 + A/B/C 三面各自变异(断言仅对应面失败,紧致性)+ 三面组合(恰好 3 项漂移)
+ --root 真被采用(空目录须报 FATAL)+ 真源零污染。零三方依赖,任何环境可跑
- examples/basic-usage.sh — 抽取→回填→注入→Lint 流水线示例
- scripts/check_asset_sync.py — 资产一致性校验(python scripts/check_asset_sync.py,退出码 0/1):
防「实现已改、副本未跟上」的多副本漂移,检查版本号 / 口径 / 目录清单三面。改动实现后必跑(见 §7.4)
- --root <目录> 可切换检查范围(默认由 __file__ 推导)——供 tests/test_asset_sync.py 在临时副本上做变异自证
- skill-card.md — 技能卡片
> 依赖版本(固定):pdfplumber==0.11.4、pymupdf==1.24.14、mammoth==1.6.0、markdownify==0.12.1、
> python-docx==1.1.2、python-pptx==1.0.2、openpyxl==3.1.5、pandas==2.2.3、tabulate==0.9.0;
> .doc 用 word-extractor(npm,隔离安装);本地 OCR 用 rapidocr==3.9.2 + onnxruntime==1.30.0
> (含 opencv-python / numpy / shapely 等传递依赖;实测依赖目录增量约 250 MB,其中 opencv-python 占 113 MB。
> 模型随包内置于 office_md_deps/rapidocr/models/,运行时零联网)。