Metadata-Version: 2.4
Name: pptx-wzq
Version: 3.4.4
Summary: PPT 多模态教学知识库自动化构建：文本/公式/图片提取（含 vsdx 与矢量规范化、文本坐标）→ 可视逻辑块解析（空间聚类+Semantic Captioning+拓扑+跨模态关系）→ 相关性过滤 → 教材文案（300字直出）→ 交付物体系对齐 word-wzq → 教材HTML/Deck；断点续传+日志
Author: Wu Zheqian
License: MIT
Keywords: pptx,education,knowledge-base,multimodal,latex,ocr,visio,vsdx,visual-block
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Education
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Education
Classifier: Topic :: Text Processing :: Markup :: LaTeX
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: Pillow>=9.0
Requires-Dist: openai>=1.0
Requires-Dist: olefile>=0.46
Requires-Dist: ultralytics>=8.0
Requires-Dist: omml2latex>=0.1.1
Requires-Dist: pywin32>=306; platform_system == "Windows"
Requires-Dist: pymupdf>=1.24.0
Provides-Extra: ocr
Requires-Dist: pix2tex>=0.1.2; extra == "ocr"
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Dynamic: license-file

# pptx-wzq · PPT 多模态教学知识库自动化构建

> **作者：吴振谦 · wuzhenqian@nbu.edu.cn · QQ：38328063**
> 本项目帮助高校教师把教学 PPT 自动转化为**图文并茂、可检索、可复用、可再加工**的多模态教学知识库。
> 当前版本：**3.4.4**（PyPI：https://pypi.org/project/pptx-wzq/ · GitHub：https://github.com/wuzhenqian6611/pptx-wzq）

**开发原因**：高校教师在课程建设与教材建设中，长期面临「课件里的大量图片、公式、文本散落各处，难以整理为规范、可复用、图文并茂的教学资源」的痛点——手工整理一份课程的图文知识库往往要耗费数周。本工具把 PPT 自动转化为图文并茂的多模态教学知识库，让教师从机械整理中解放出来。

---

## 目录

- [一、它能做什么](#一它能做什么)
- [二、安装与环境要求](#二安装与环境要求)
- [三、快速开始](#三快速开始)
- [四、右键菜单（Windows 一键操作）](#四右键菜单windows-一键操作)
- [五、命令一览](#五命令一览)
- [六、输入 PPTX 预处理要求](#六输入-pptx-预处理要求)
- [七、工作流程（8 环节）](#七工作流程8-环节)
- [八、输出文档体系与内部格式](#八输出文档体系与内部格式)
- [九、技术架构](#九技术架构)
- [十、图块识别规则（16 条）](#十图块识别规则16-条)
- [十一、生命周期与可靠性](#十一生命周期与可靠性)
- [十二、常见问题](#十二常见问题)
- [十三、版本演进](#十三版本演进)
- [十四、文档与许可](#十四文档与许可)

---

## 一、它能做什么

输入一份教学 PPT（.pptx），自动完成 **8 环节流水线**：图块提取 → 文本提取 → 公式提取 → 图块 AI 解读 → 相关性过滤 → 教材文案（整篇自主分章分节）→ JSON 组装 → 输出。

**核心能力：**

- **图块（可视逻辑块）优先**：以「组合即图块」为第一原则，作者用 PPT 组合工具绘制的逻辑图/示意图整体保留（原生 XML 段），而非拆散成碎片。
- **源语言级解构**：grpSp 组合保留原生 XML 段（p:/a:/r: 前缀）；Visio/vsdx 剥离原生文件；公式转 LaTeX——信息不降维。
- **模型按块路由**：有 XML 的块 → DeepSeek 读 XML（组内公式转 LaTeX）；纯像素图块 → qwen VLM 兜底；DeepSeek 空响应自动降级；**blocks_json 步骤 qwen 视觉兜底按需触发**（仅 caption 未解读的块读渲染图，避免重复解读）。
- **公式双通道**：组合内公式既保留为组合块内容（XML 段），又进 formulas.md（语义存档）；OLE 预览图（公式/Visio 快照）判定后**不污染图块体系**。
- **运行过程全透明**：所有模型调用（DeepSeek/qwen）的输入输出摘要实时打印（`[DeepSeek]`/`[qwen]` 前缀），用户可随时看到每一步在想什么、输出了什么。
- **教材级文案**：整个 PPT 视为一部完整教材，DeepSeek 自主划分若干章（`# 第X章 章名`）、每章若干节（`## 第X节 节名`，一节可含多页），每页内容首行标注所属章节；**500 字为限**（原文 ≤500 字扩写到不少于 500 字；>500 字直出整理，不改原意、不增字数）。
- **生命周期自管**：全流程成功即清理过程文件；中断保留断点、二次运行自动接续。

## 二、安装与环境要求

### 系统要求

| 项 | 要求 |
|---|---|
| 操作系统 | **Windows 10/11**（强烈建议；渲染通道支持 PowerPoint/WPS COM） |
| Python | 3.9 及以上 |
| 演示应用 | **Microsoft Office PowerPoint 或 WPS 演示**（渲染自动探测：有 Office 用 Office，只有 WPS 用 WPS；均无则渲染优雅降级） |
| API Key | `DEEPSEEK_API_KEY`（语义解读/教材文案）、`DASHSCOPE_API_KEY`（qwen VLM 兜底） |

### 安装

```bash
pip install pptx-wzq            # 核心
pip install "pptx-wzq[ocr]"     # 含本地公式 OCR（pix2tex，可选）
```

安装后自带完整文档（使用手册 + 技术分析）：

```python
from pptx_wzq import docs
print(docs.__path__)   # 查看 docs/ 目录（含 html/pdf/md）
```

### Windows 右键菜单（可选，推荐）

装包后**无需手动操作**：包内 `.pth` 钩子在任意 Python 进程启动时自动注册右键菜单
（幂等；非 Windows 自动跳过；也可手动 `pptx-wzq-menu install` 立即注册）。注册后：

| 右键对象 | 菜单项 | 行为 |
|---|---|---|
| `.pptx` 文件 | `pptx-wzq 生成知识库` | 弹出"选择输出目录"框（**默认定位到 pptx 同名子文件夹，自动创建**），确认后跑完整 `pptx-paser` 流水线 |
| `images/` 或 `sources/` 下 `slide_XX_blk_NN.png` | `pptx-wzq 删除图块` | 弹确认框，确认后自动执行 `pptx-del`（含备份与一致性校验）；**支持资源管理器多选**——选中多个 png 一次删除（一个确认框、每目录一份备份） |

相关命令：

```bash
pptx-wzq-menu install     # 手动注册右键菜单（HKCU，无需管理员）
pptx-wzq-menu uninstall   # 卸载右键菜单
pptx-wzq-menu status      # 查看已注册命令
```

> **排障**：右键流程每步写入 `%TEMP%\pptx_wzq_launcher.log`（目标/确认结果/子进程返回码），
> 异常时据此定位。若右键菜单不出现，先重启"Windows 资源管理器"刷新菜单缓存。

> **权重来源与许可**：图片过滤内置的 YOLO 模型权重 `yolov5su.pt` 来自
> [ultralytics](https://github.com/ultralytics/ultralytics)（YOLO 官方），采用 **AGPL-3.0**
> 许可证随包分发；商用或闭源使用请自行评估其开源条款。

## 三、快速开始

```bash
# 一条命令跑完 8 环节（推荐）
pptx-paser "C:\课件\战略管理.pptx" -o "C:\输出\战略管理知识库"

# 跳过相关性与文案（省 Token）
pptx-paser "C:\课件\战略管理.pptx" -o out --skip related,author

# 断点续传（中断后重跑同命令自动接续）
pptx-paser "C:\课件\战略管理.pptx" -o out
```

## 四、右键菜单（Windows 一键操作）

> 详见上文「二、安装与环境要求 → Windows 右键菜单」。一句话：`pip install -U pptx-wzq` 后
> 下一次任意 Python 启动即自动注册；手动方式 `pptx-wzq-menu install`。

## 五、命令一览

| 命令 | 功能 |
|---|---|
| `pptx-paser` | 总编排器：8 环节一条命令，断点续传 / 成功清理 |
| `pptx-blocks` | 图块提取+解构+渲染；`--caption-sources` 按 sources/ 顺序解读 |
| `pptx-text` | 文本提取（in_group / 表格 Markdown） |
| `pptx-formula` | 公式提取 → LaTeX |
| `pptx-caption` | 图片/图块解读（模型路由） |
| `pptx-related` | 块相关性过滤 + 审计 |
| `pptx-author` | 整篇分章分节教材文案（500 字为限） |
| `pptx-bind` | 图文绑定 JSON |
| `pptx-del` | **图块删除后处理**：`pptx-del images\slide_29_blk_01.png -all`，删除指定块及其全部关联（images/sources/JSON/binding/captions），等价于该组合不存在；默认备份 + dry-run + 一致性校验 |
| `pptx-wzq-menu` | **Windows 右键菜单管理**：`install` / `uninstall` / `status`（HKCU，无需管理员） |
| `pptx-doctor` | **环境依赖自检**：用**真实导入**逐项校验依赖（能发现 find_spec 看不出的半残包、语法不兼容），`--fix` 自动修复；另检出 pip 残留与 API Key |
| `pptx-html` / `pptx-deck` | 教材 HTML / 教学 Deck 导出 |

## 六、输入 PPTX 预处理要求

> **核心思想：用 PPT 自带工具给解析器打「块边界」标注。** 组合的数量 = 该页图块数量的上限基准，可据此验收。预处理不是必须的（无组合也能跑），但组合能让图块提取完全确定、可回归。

| 对象 | 预处理操作 | 解析器行为 |
|---|---|---|
| **逻辑图/示意图（要整体成块）** | 「开始 → 排列 → 组合」（Ctrl+G）把底图+文字框+箭头合成一个组合 | 整个组合 = 1 个 group 块，XML 段导出 sources/，渲染图存 images/ |
| **嵌套组合** | 有意为之才用：嵌套 = 外层块的子结构；想分开就移出外层 | 嵌套组合并入外层 children，不单独成块 |
| **Visio / vsdx 工程图** | **不要**组合进其他形状（否则被吞并）；保持独立 OLE 对象 | 独立成 visio 块：可剥离 → .vsdx；不可剥离 → XML 段 |
| **公式** | 行内公式保持独立（不组合）→ 自动进 formulas.md；想并入图块就移进组合 | 组合内公式随块转 LaTeX；非组合公式独立提取 |
| **首页/尾页** | 无需操作：默认按页序跳过第 1 页与末页的图块 | 封面/致谢不产块（文本/公式仍提取） |
| **小像素图（装饰图标）** | 小于整页 20% 默认舍弃；想保留 → 组合进相邻图形 | 组合内小图豁免；非组合小图丢弃并写入审计 |
| **表格** | 无需操作 | 输出 Markdown 表格（texts.md），不产块 |
| **带裁剪的图片（srcRect）** | 无需操作（自动保持显示一致） | 元数据/资源/渲染/描述全程按裁剪 |

## 七、工作流程（8 环节）

```
① blocks → ② text → ③ formula → ④ caption → ⑤ related → ⑥ author → ⑦ blocks_json → ⑧ 输出
```

| 环节 | 职能 | 消耗 | 要点 |
|---|---|---|---|
| ① blocks | 图块提取 + 解构 | 本地 | 单阶段确定性拆块（grpSp→group / visio / 像素图≥20% / SVG-WMF）；XML 段每组合一个文件导出 sources/；rldimg 资源图落盘；PowerPoint COM 渲染块 PNG |
| ② text | 文本提取 | 本地 | 排除页眉页脚/母版固定文本；组内文字标「[图块内文本]」；表格输出 Markdown |
| ③ formula | 公式提取 | 本地 | 非组合公式 → LaTeX（OMML/MTEF/OCR 三级）；**组合内公式也进 formulas.md**（双通道：组合块 XML 段保留 + 语义存档，标注「组合内公式」） |
| ④ caption | 图块 AI 解读 | DeepSeek+qwen | 按 sources/ 顺序：.xml → DeepSeek 读 XML（公式转 LaTeX）；.png → qwen 兜底；调用输入输出实时打印 |
| ⑤ related | 相关性过滤 | DeepSeek | 剔除 logo/作者/装饰块 → related_filter.json 审计；**并发判定 + 页面正文为空时保守保留**（防误删） |
| ⑥ author | 教材文案 | DeepSeek | 整篇自主分章分节（一节可含多页），每页标注章节；≤500 字扩写、>500 字直出整理 |
| ⑦ blocks_json | 组装 + 语义增强 | DeepSeek+qwen | visual_blocks.json（v2.0）+ semantic_description + 跨模态关系 + binding 导出；**qwen 视觉兜底仅对 caption 未解读的块触发**（读渲染图），DeepSeek 语义增强并发执行 |
| ⑧ 输出 | 归位 + 清理 | 本地 | 成功即删过程文件；中断保留断点自动接续 |

**模型路由（规则 11/16）**：DeepSeek-V4-Flash 读 sources/ 的 XML 段（grpSp/Visio-XML/SVG-WMF，公式转 LaTeX）；qwen3.7-plus 读像素图原图——仅纯像素图块 / DeepSeek 空响应兜底。

## 八、输出文档体系与内部格式

### 结果目录结构（成功运行后）

```
<名>_result/
├─ sources/                          # 图块源资源（解读唯一输入源）
│  ├─ slide_07_blk_01_grp.xml        # grpSp XML 段（页注释 + 原生前缀，规则10）
│  ├─ slide_05_blk_02.vsdx            # Visio 可剥离（规则13）
│  ├─ slide_05_blk_03_ole.xml         # Visio 不可剥离 → XML（规则14）
│  ├─ slide_08_blk_04_vec.xml         # SVG/WMF 矢量 XML（规则15）
│  ├─ slide_19_blk_01.png             # 像素图块原图（qwen 解读输入，规则16）
│  └─ rldimg/                         # grpSp XML 内 r:embed 引用的资源图片
├─ images/                           # 块渲染图（PowerPoint，仅供人阅览）
├─ <名>_visual_blocks.json             # 核心结构化（pptx_multimodal_slide_v2.0）
├─ <名>_visualBlock_text_binding.json  # 图文关联（pptx_visual_block_text_binding_v1.0）
├─ <名>_textbook.md                   # 教材文案（篇→章→节→页，每页标注章节）
├─ <名>_captions.md                   # 块解读（绑定 sources/ 文件名 + 解读通道）
├─ <名>_texts.md / _text_entries.json   # 文本清单（组内文字标[图块内文本]）
├─ <名>_formulas.md                     # 非组合公式（LaTeX）
└─ <名>_related_filter.json              # 相关性过滤审计
（v2.0 起成功即清理：无 过程文件/；中断时保留断点供续传）
```

### VisualBlock 内部格式（visual_blocks.json 的块对象）

```json
{
  "block_id": "blk_01", "page": 7, "block_type": "战略管理概念框架",
  "bbox": {"x": 142.3, "y": 226.4, "w": 1026.2, "h": 400.2},
  "z_index_range": [12, 45], "is_single": false,
  "text": "战略哲学 商道 天道 人道 …",
  "assets": {
    "xml_source": "./sources/slide_07_blk_01_grp.xml",
    "raster_png": null,
    "rldimg": ["./sources/rldimg/slide_07_blk_01_image4.png", "…"]
  },
  "internal_structure": {"nodes": ["…"], "edges": ["…"]},
  "semantic_description": {
    "block_type": "战略管理概念框架",
    "expression_goal": "展示战略管理概念框架的核心逻辑",
    "expression_role": "将抽象概念通过战略哲学/商道/天道/人道具象化…",
    "expression_features": ["概念框架", "层次结构", "关系图"],
    "vlm_caption": "该图块以“战略哲学”为中心…",
    "teaching_use": "教学辅助图示",
    "formula_latex": "", "caption_source": "deepseek_xml"
  },
  "member_obj_ids": ["…"], "vector_resources": []
}
```

### textbook.md 结构（v2.3 起）

```
# <名> 教材文案
# 第1章 战略管理概述          ← DeepSeek 自主命名
## 第1节 战略的概念与本质      ← 自主命名，一节可含多页
## 第 N 页
> 所属章节：第1章 战略管理概述 · 第1节 战略的概念与本质   ← 每页首行标注
（不少于 500 字正文…）
```

## 九、技术架构

| 模块 | 职责 | Token |
|---|---|---|
| `extract_pptx_images.py` | OOXML 原子对象提取（图片/形状/grpSp/表格/公式/图表）；XML 段原生提取；srcRect 裁剪；**OLE 预览图判定（preview_of：公式/Visio 快照不独立成块）**；PowerPoint/WPS COM 渲染（ProgID 自动探测 + DispatchEx 独立实例） | 0 |
| `extract_texts.py` | 页面文本（in_group 标记）、表格 Markdown | 0 |
| `visual_blocks.py` | 单阶段确定性拆块、块渲染、语义增强（DeepSeek **并发**）、跨模态关系、**qwen 视觉兜底按需触发（仅 caption 未解读块）** | DeepSeek+qwen |
| `cli_blocks.py` | XML 段导出、rldimg 复制、caption 路由、binding、captions.md | DeepSeek+qwen |
| `cli_author.py` | 整篇分章分节文案、500 字为限、自动分批（跨批章节延续） | DeepSeek |
| `cli_related.py` | 相关性过滤（**并发判定 + 正文为空保守保留**）+ 审计 | DeepSeek |
| `cli_paser.py` | 总编排、断点续传（state.json）、归位、成功清理 | — |

## 十、图块识别规则（16 条）

### 识别层（1-9）——「组合即图块声明」

| # | 规则 | 实现 |
|---|---|---|
| 1 | 一个 grpSp 组合即是一个图块，组合内一切内容读取为该图块内容 | kind="group"，children 递归收编 |
| 2 | 嵌套组合不单独提取 | 嵌套仅作外层 children |
| 3 | OLE Visio/vsdx 独立成块（除非在组合内） | kind="visio" 分支 |
| 4 | 非组合像素图单独成块；重叠文本并入 | raster 独立块 + 重叠文本并入 |
| 5 | 组合内公式作块内容；非组合公式独立提取 | formula 排除 in_group |
| 6 | 首页/尾页图块舍弃 | skip_cover_pages 整页跳过 |
| 7 | 非组合像素图 < 整页 20% 舍弃 | raster_min_area_ratio=0.20 |
| 8 | 表格仍读取为表格（Markdown） | 表格移交 text 步骤 |
| 9 | grpSp 内 srcRect 须全程一致 | children 携带 src_rect + 裁剪落盘 |

### 解构/解读层（10-16）

| # | 规则 | 实现 |
|---|---|---|
| 10 | grpSp 保留整段 XML，页标记，每组合一个独立 .xml 存 sources/ | `sources/slide_{页}_{块id}_grp.xml` |
| 11 | grpSp 块 caption 用 DeepSeek 读 XML；公式转 LaTeX | `_ds_read_xml` + 超长压缩 + 重试 |
| 12 | grpSp 块 PowerPoint 渲染 PNG 存 images/；PNG 不送 qwen | COM ExportAsFixedFormat PDF + PyMuPDF |
| 13 | Visio 可剥离 → .vsdx 存 sources/ + PNG 存 images/ | 原生剥离 + 渲染 |
| 14 | Visio 不可剥离 → XML 段，同 grpSp | `sources/slide_{页}_{块id}_ole.xml` |
| 15 | SVG/WMF 同 Visio：尽量 XML 段，不行才 PNG | `sources/slide_{页}_{块id}_vec.xml` |
| 16 | 仅无法 DeepSeek 解读的块才送 qwen | 模型路由 + 空响应降级链 |

## 十一、生命周期与可靠性

- **成功即清理**：全流程成功后中间产物（by_page/atomic_objects.json/manifest/各步骤工作目录）全部删除，结果目录只留交付物；删除失败打印警告而非静默。
- **中断续传**：中断保留 work + `state.json`（步骤状态机 pending/running/partial/done/failed）；再运行跳过已完成、自动接续缺失；`doc_md5` 换源检测 → 全量重跑；author 支持缺失页补跑（`--pages`）。

## 十二、常见问题

| 问题 | 说明 |
|---|---|
| 没有 Office 会怎样？ | 渲染降级：无块渲染图（images/ 为空）；XML/JSON/文案等其余产物正常 |
| 只有 WPS 没有 Office？ | 自动探测 `Kwpp.Application` COM，用 WPS 演示渲染（ProgID 依次尝试，有 Office 优先） |
| 渲染报 0x80070002（文件不可用）？ | 大概率是 PPT 正被 PowerPoint/WPS 打开编辑，或相对路径未解析——v3.0.2 起强制绝对路径 + 完整错误提示 |
| 没有 API Key？ | 跳过对应解读/文案步骤；`PPTX_PASER_NO_VLM=1` 可跳过 VLM 全流程 0 Token |
| 中途中断？ | 重跑同命令自动接续（state.json + doc_md5 换源检测） |
| DeepSeek 对某 XML 段空响应？ | 自动降级 qwen 读渲染图 → 规则模板兜底，保证每块有解读 |
| blocks_json 步骤为什么慢？ | v3.0.0 起语义增强/相关性判定为普通生成 + 并发（实测 10~160 倍提速）；若仍慢请确认已升级 |
| 如何查安装版本与文档？ | `pptx-paser --version`；`from pptx_wzq import docs` |
| 步骤失败只显示 `exit=1`，看不到原因？ | 升级到 v3.4.1：失败时会把**子进程 stderr 尾部**并入报错；若含 `ModuleNotFoundError` 会提示跑 `pptx-doctor --fix`。旧版可直接跑 `pptx-doctor` 定位 |
| 依赖处处报「已安装」却仍 `ModuleNotFoundError`？ | v3.4.1 前的自检用 `find_spec()`，**只查文件是否存在、不执行导入**——pip 卸载被中断留下的「元数据在、模块文件已删」半残包一律被判 OK。v3.4.1 改为真实导入判定 |
| `omml2latex` 导入报 f-string 语法错误？ | 上游包声明 `Requires-Python >=3.9`，但代码用了 3.12+ 才允许的 f-string（表达式含反斜杠）。Python 3.11 及更早无法导入 → 公式「OMML 原生→LaTeX」路径失效。仓库附 `tools/patch_omml2latex_py311.py` 可幂等修复（自动备份 + 编译/导入双验证） |
| 用了 `pip install --force-reinstall` 后环境坏掉？ | 该参数会**先卸载整棵依赖树**再重装；卸载阶段一旦被中断（安全软件/沙箱/强杀进程）就会留下大量半残包，且后续 `--no-deps` 补救不会补回依赖。请改用 `--ignore-installed`（跳过卸载阶段），并用 `pptx-doctor` 自检 |
| 图块图与 JSON/sources 不一致，或**图文错配**（图来自别的页）？ | v3.4.3 前共有**三处**渲染取页缺陷（同一类病根，分散在三条代码路径上，容易被逐个漏掉）：① `sorted(glob("page-*.png"))` 是**字典序**，页数 ≥100 时 `pages[99]` 实为 `page-181.png`（242 页课件实测）→ 按 `pages[page-1]` 取页即**裁错页**（62/88 页的课件正常，≥100 页的全错）；② 缓存复用用 `>=` 且无新鲜度校验，课件缩短后仍复用旧缓存（242 页复用了 257 页的旧渲染）；③ **v3.4.3 修**：「重新渲染后返回」那条路径仍在用 `sorted(glob)`（v3.4.2 只修了「复用缓存」分支）→ **首次处理一份新课件（无缓存）时必然踩中**：实测 117 页课件的第七章，slide 46 的图裁自 page-28、slide 12 裁自 page-101、slide 28 裁自 page-116（图与错页同矩形 MAE 0.0~1.5，与本页 37~84，像素级同一张）。现三处统一走 `_cache_pages()` 按页码数值排序，并新增页数一致性告警。修复后重跑即可；历史产物可用仓库内 `tools/repair_block_images.py <结果目录> --force` 就地重建 images/（`--force` 只覆盖块图、不重渲染，需重渲染另加 `--force-render`；自动兼容 px@96 与归一化 bbox；对组合子坐标系内的 Visio/OLE 块用 OOXML 组合变换还原）。自检脚本：`tools/selftest_page_order.py` |
| `captions.md` 的「内容理解」出现 `{"block_type":…` 这种 **JSON 残片**？ | v3.4.4 前的缺陷：`_parse_desc_json()` 在 `json.loads` 失败时执行 `return {"semantic_description": text[:500]}`——**把模型原始输出当解读落盘**。根因是 XML 通道 `max_tokens=800` 截断 JSON → 解析失败 → 走该兜底。实测某知识库 XML 通道 294 条里 **68 条（23.1%）**受损，逐章 13.7%~37.5%（越新的版本解读更长、越易触发）。v3.4.4 已修：① `max_tokens` 800→2000；② 解析失败**先重试**（第 2 次起用"只输出 JSON"强约束提示）；③ 新增**截断 JSON 修复**（逐字段抽取值，未闭合字符串也能救回）；④ 仍失败则返回空值 + `parse_failed`，降级到 qwen 读图/规则模板，**绝不写残片**；⑤ `captions.md` 写入侧加护栏，残片一律拦截为占位说明。历史产物用 `python tools/repair_caption_json.py "<结果目录>" --recursive` 就地修复（自动备份；如需完整解读再跑 caption 步骤） |
| `internal_structure.formula_list` 全量空？ | v3.4.4 前是**死代码**：`_filter_noise()` 会把 `kind=="formula"` 的对象整体剔除（公式走 formulas.md），而块成员只从过滤后的列表里挑 → `_infer_topology()` 里收集公式那段永远不执行，实测 674 块全空，造成"块内公式信息双重丢失"（既无 formula_list，也非每块都有 formula_latex）。v3.4.4 起改从**块自己的组合 XML** 提取（内联 OMML 无独立区域——实测公式对象 469/469 个 bbox 全为 0，几何归属不可行）：按 `<m:oMath>` 配对切出每个公式，元素为 dict（`index`/`text`/`latex`/`source`），并新增 `formula_count`。实测第七章：103 个组合中 **31 个含公式、合计 370 个，LaTeX 转换 370/370 成功**（额外耗时 0.2s）。页级公式清单仍在 `*_formulas.md`，组合块的整体 LaTeX 见 XML 通道的 `semantic_description.formula_latex` |
| 右键菜单没出现？ | 装包后需**下一次 Python 启动**触发自动注册（或手动 `pptx-wzq-menu install`）；注册后仍不显示 → 任务管理器重启「Windows 资源管理器」刷新菜单缓存 |
| 右键菜单**突然消失**（过几天没了）？ | 多为 **WPS/Office 升级重写其 ProgID 的 shell、清掉了第三方动词**（.pptx 生效 ProgID 下的 pptxwzq 被删，扩展名残留键不生效）。处理：`pptx-wzq-menu install` 一键恢复；v3.3.13 起自动钩子按「Windows 实际读取位置」判定，升级清掉后**下次任意 Python 启动即自动补装** |
| 右键菜单项点击报"没有与之关联的应用"？ | 多因系统 .pptx 默认打开程序未设置（建议「设置→默认应用」指定 PowerPoint/WPS），或点了无 command 的级联父项；v3.3.x 已改平铺动词 + 自动修复文件类 |
| 多选 png 后右键没有"删除图块"？ | 需 v3.4.0+（`MultiSelectModel=Player` 才支持多选，旧版默认 Document 会每文件单开一次且 15 项以上菜单消失）；升级后 `pptx-wzq-menu install` 或跑一次任意 Python 即自动生效 |
| 右键删除/生成无反应？ | 查看 `%TEMP%\pptx_wzq_launcher.log` 定位卡点（确认框结果/子进程返回码均记录）；v3.3.9 起子进程改为直接列表调用，消除 cmd 嵌套引号问题 |

## 十三、版本演进

| 版本 | 里程碑 |
|---|---|
| 1.5.0 | 六步管线重构（img 并入 blocks 自举）、三件套交付物、版本统一 |
| 2.0.0 | 16 条规则落地：组合即块、单阶段确定性拆块、XML 段导出、DeepSeek caption 路由、PowerPoint 渲染、_organize 修复 |
| 2.1.0 | captions 绑定 sources/ 源文件 + 通道标注；每页扩写 500 字 |
| 2.2.0 | textbook 规则改 500 字为限（不足扩写、超出直出整理 _tidy_direct） |
| 2.3.0 | Author 整篇自主分章分节（一节可含多页），每页标注章节 |
| 2.4.0 | 技术分析报告（html/pdf/md）随安装包分发 |
| 2.5.0 | 使用手册+技术分析合并文档（html/pdf）、README 全量更新 |
| 2.5.1 | 渲染静默降级修复：dependencies 补 pymupdf；渲染失败给明确警告 |
| 2.5.2 | images/ 不再被 caption/blocks_json 步骤清空（--skip-render）；渲染子进程改 DispatchEx 独立实例（Open 0x80070002） |
| 2.6.0 | **OLE 预览图判定**（pic 前 300 字符检测 oleObj → preview_of，公式/Visio 快照不独立成块，vector 块 63→0）；**组合内公式进 formulas.md**（双通道 + 标注） |
| 3.0.0 | **语义增强/关系生成提速**（去 thinking + 并发 8，实测 10 倍）；**模型调用实时打印**（[DeepSeek]/[qwen] 输入输出可见） |
| 3.0.1 | **related 相关性过滤提速**（并发 + 去 thinking，实测 162 倍）；**正文为空保守保留**（消除误删） |
| 3.0.2 | 渲染 Open 失败根因修复：pptx 强制 resolve 绝对路径（子进程 CWD 继承问题）+ 错误完整显示 |
| 3.1.0 | **WPS 渲染支持**：ProgID 自动探测（PowerPoint.Application → Kwpp.Application）+ ExportAsFixedFormat 简化参数回退 |
| 3.1.1 | **qwen 视觉兜底按需触发**：仅 caption 未解读的块读渲染图（调用 73→13）；图路径接通 images/（修复此前"从未真正读图"） |
| 3.1.2 | 文档体系更新：README/使用手册/技术分析同步 3.0.x~3.1.x 全部变更（渲染通道、性能优化、实时打印、按需兜底） |
| 3.1.3 | README 补「开发原因」；**PyPI 主页显示完整 README**（wheel METADATA 携带 long_description） |
| 3.1.4 | ExportAsFixedFormat E_FAIL 可读化 + 旧 PDF 占用警告 |
| 3.1.5 | author 大输出批截断根治：max_tokens + 缺页重试 + 诚实统计 |
| 3.2.0 | **新增 `pptx-del` 命令**：流水线完成后的图块删除后处理（清理用户预处理不干净的 grpSp 组合），删除块及其全部关联输出，默认备份 + dry-run + 一致性校验 |
| 3.3.0 | **Windows 右键菜单**：`pptx-wzq-menu install/uninstall/status`（HKCU 无管理员），.pptx→生成知识库、.png→删除图块，命令路径由 `sys.executable` 动态写入（无绝对路径） |
| 3.3.1 | 右键注册改挂 `HKCU\Software\Classes\.ext\shell\`（不再依赖 SystemFileAssociations）；uninstall 清理旧残留；install 自检 .pptx 文件类 |
| 3.3.2 | **install 自动修复 .pptx 文件类**（ProgID 缺失/损坏时复用 PowerPoint/WPS 或自建 `pptx-wzq.PPTFile` + open 命令）；附 `repair_pptx_assoc.ps1` |
| 3.3.3 | **平铺动词取代级联子菜单**（Win11 不渲染扩展名级联，点父项报"无关联应用"）；uninstall/status 兼容新旧结构 |
| 3.3.4 | **动词双位置注册**（扩展名 shell + 生效 ProgID shell——ProgID 存在时 Windows 只读后者）；`.png` 另挂 `SystemFileAssociations\image`（v3.3.8） |
| 3.3.5 | 修复右键命令程序路径被存成正斜杠导致"无法访问指定设备、路径或文件"；`_command()` 加 `os.path.normpath` 兜底 |
| 3.3.6 | **生成默认输出目录 = pptx 同名子文件夹**（自动创建，对话框定位 + 等待确认，取消清理空目录） |
| 3.3.7 | **pip 安装后自动注册**：`.pth` 启动钩子（`pptx_wzq_menu.pth` 注入 wheel 根目录 → site-packages），任意 Python 启动即幂等静默注册 |
| 3.3.8 | png 删除动词加挂 `SystemFileAssociations\image`（图片感知类型恒显示） |
| 3.3.9 | **子进程改直接列表调用**（消除 cmd /c 嵌套引号在中文路径下的解析失败——右键删除"确认后无反应"根因）；generate 用 `input="y\n"` 等效 `echo y |` |
| 3.3.10 | 删除/生成失败弹窗可见化（RC + 提示看控制台/备份） |
| 3.3.11 | **确认框改 tkinter**（进程内返回值，消除 PowerShell 捕获丢值）；全流程日志 `%TEMP%\pptx_wzq_launcher.log` |
| 3.3.12 | 版本号维护（供本地测试） |
| 3.4.4 | **修"解读落盘成 JSON 残片"与 `formula_list` 死代码**：① `_parse_desc_json` 不再把模型原始输出当解读（旧实现 `text[:500]` 兜底造就 68/294=23.1% 的残片），改为「严格解析 → 围栏剥离 → **截断 JSON 逐字段修复** → 散文直采 → 才判失败」，并且**解析失败会重试**（提示"只输出 JSON"）、`max_tokens` 800→2000 消除截断根因；② 解析彻底失败时返回空值 + `parse_failed`，降级到 qwen 读图/规则模板，`captions.md` 另加残片护栏；③ `internal_structure.formula_list` 由死代码改为**从块组合 XML 提取**（含逐条 LaTeX + `formula_count`）；④ 新增 `tools/repair_caption_json.py` 就地修复历史残片（自动备份 + 重写 captions.md） |
| 3.4.3 | **补全取页缺陷第三处（重新渲染返回路径）**：`_render_pptx_pages_com` 在「渲染完成后返回」分支仍用 `sorted(glob(\"page-*.png\"))`——字典序在 ≥100 页课件下使 `pages[page_no-1]` 取到别的页，**首次处理新课件（无缓存）必中**（第七章 117 页实测：slide 46→page-28、slide 12→page-101、slide 28→page-116；图与错页同矩形 MAE 0.0~1.5，与本页 37~84）。三处取页统一改 `_cache_pages()` 数值排序 + 页数不一致告警；新增 `tools/selftest_page_order.py`（合成 117 页/无补零/非页图混杂三类用例，含事故数字回归断言）；`tools/repair_block_images.py` 拆分 `--force`（只覆盖块图）与 `--force-render`（重渲染页图），此前两者耦合导致只想覆盖错图却触发整份课件重渲染 |
| 3.4.2 | **修复渲染缓存两处缺陷（图文错配根因）**：① 页图映射改**按数字解析**（原 `sorted(glob)` 字典序在 ≥100 页课件下 `pages[99]=page-181`，导致块图取自错误页面）；② 缓存复用条件 `>=` 改**严格相等** + 新增**新鲜度校验**（任一页早于课件 mtime 即重渲染）+ 渲染后清理超范围残留页。附 `tools/repair_block_images.py`：就地重建 `images/` 使与 JSON 一致（含缓存校验、数值映射、旧图隔离保留） |
| 3.4.1 | **环境自检与故障可见化**：新增 `pptx-doctor`（真实导入判定依赖，能识别半残包/语法不兼容，`--fix` 自动修复）；编排器依赖检查由 `find_spec` 改为真实导入并在缺必需依赖时**提前中止**（不再跑到中途 exit=1 或静默降级）；步骤失败时输出**子进程 stderr 尾部**；新增 caption 降级识别（`caption_source` 全为空 → 判定该步未真正完成，续跑自动重做）；附 `tools/patch_omml2latex_py311.py` 修复上游包在 Python≤3.11 的语法不兼容 |
| 3.4.0 | **右键多选删除**：`MultiSelectModel=Player`（旧式动词上限 15→100）+ 删除命令改不带引号 `%1` + 启动器同批聚合（首个实例收集同批投递的目标，一次性 cli_del；空格路径自动还原；跨结果目录分组处理）；子进程注入 `PYTHONPATH` 提升环境兼容性 |
| 3.3.13 | **修复右键菜单"突然消失"**：WPS/Office 升级会重写其 ProgID shell 清掉第三方动词；注册判定改为按 Windows 实际读取位置（生效 ProgID shell），任一必备动词缺失即触发幂等自动补装 |

## 十四、文档与许可

- **文档**：完整「使用手册与技术分析」（HTML/PDF）随安装包分发于 `src/pptx_wzq/docs/`，安装后可 `from pptx_wzq import docs` 定位；README（本文档）为全量文字版。
- **许可证**：MIT（本项目代码）；随包 YOLO 权重 `yolov5su.pt` 为 **AGPL-3.0**（见上文）。
