AI测试 LLMCase-V4 从策划文档到测试用例 - 第 3 章 convert2md:把策划文档变成机器可读的 Markdown

zhangbp · 2026年09月20日 · 15 次阅读

第 3 章 convert2md:把策划文档变成机器可读的 Markdown

本章目标:理解流水线第一站。学完你能回答:为什么不直接把 docx z 喂给 LLM?转换主链怎么走?
图片和表格这两类"特殊内容"怎么进 Markdown?什么是约定区、为什么要落标?

3.1 为什么不直接把 docx 喂给大模型

理论上你可以把 docx 的纯文本抽出来直接丢给 LLM。本工程不这么做,有三条硬理由:

  1. 格式信息就是语义。"队伍上限 3 人"是正文还是表格里的一行?某个列表项是顶级功能还是子功能? 这些答案编码在 Word 的样式与缩进里。纯文本抽取会拍平层级——这是本工程真实修过的 bug (session#68"列表层级拍平":文档层级编码在缩进里,ilvl 恒 0,早期代码信了 ilvl 导致父子关系全丢)。
  2. 上下文长度与成本。60 页文档全文塞进每次 LLM 调用,token 成本爆炸且长上下文稀释注意力。 转成结构化 md 后,后续每个环节可以精确切片取用。
  3. 可审计的中间产物。md 是人可直接阅读编辑的——策划文档转换错了,用户在 dashboard 源文档视图里 改了再存,全链以修正后的 md 为准。这个"人机协作检查点"只有中间产物形态才做得到。

所以 convert2md 的产出 *_SRC_*.md下游全链的唯一真相源(SRC 后缀的由来)——
后续所有环节(提取/检索/评估对照)都只认这份 md,不再回看 docx。

3.2 转换主链与三档策略

主链一句话:docx →(图片先抽走换成理解块)→ 格式映射成 md 行流 → 清洗 → 落盘

data/workspace/01_raw/*.docx
    │ 按后缀分流:.xlsx 走 ExcelParser 单跳直转(3.5 节)
    ▼ .docx
DocumentProcessor 四个 pass:段落图 → 表格图 → 页眉页脚图 → 公式
    ▼ 产出 processed_*.docx(图片 run 已原位替换为文字描述块)
UnifiedConverter.convert —— 三档策略 dispatch:
    CODE   纯 python-docx 确定性映射(默认档,零 LLM)
    MODEL  全文喂 LLM 重排(几乎不用)
    HYBRID 先 CODE,启发式检测到层级问题才请 LLM 修复
    ▼ 三步后处理:SOURCE 溯源标签 → 约定区落标(3.6 节)→ 转换统计
data/workspace/02_markdown/{stem}_SRC_{时间戳}.md
    + assets/{md主名}/img-NNN.*(原图)+ .meta.json(血缘)+ .report.json(统计)

三档策略是个值得记住的成本分级设计:默认走最便宜的确定性路径(CODE),LLM 只做"修复器"
不做"主转换器"(HYBRID 里启发式守门、命中才请模型、失败静默回退 CODE 结果)。
服务端默认 codeweb/main.py 的 convert 端点),未知 method 兜底也是 CODE。

另一个新人要知道的事实:convert2md 启动不强依赖 LLM——启动预检只告警不阻断
(对比 generator:LLM 不可达直接拒绝启动)。没有 LLM key 也能做纯文本转换,只是图片理解会降级成占位符。

拼接也是零语义的:转换器按文档流原序逐块判定(每块走"表格 → 标题 → 列表 → 普通段落"
的判定链),每块产出的 md 片段自带空行结尾,最后按序 join 成整篇——没有重排、没有跨块合并,
原序即真相序。后续所有"这块内容上下文是什么"的判断都建立在这个原序假设上。

3.3 格式映射:标题与列表层级

这是纯确定性代码(core/md_converter.py),但藏着 Word 格式的两个大坑,恰好是"为什么转换是专业问题"的例证:

坑一:标题不总是"标题样式"。规范文档用 Heading N 样式,映射到 md 的 #×N 很直接;
但很多策划文档的标题没有样式,得靠内容特征兜底(行很短、无句号、像标题)。反过来,文档的第一个标题
会被降格成粗体
——md 语法里每篇只能有一个 H1,而这个位置留给文档名,防止层级污染。

3.4 图片理解:VLM 三环管线

策划文档里的图(UI 截图、流程图、图表)必须被"看懂"才能进入下游。管线是三环:

抽取环:遍历段落/表格/页眉页脚里的图片 run,原图先落盘 assets/{md主名}/img-NNN.*
理解环:每张图 → 判定五类(flowchart/chart/ui_screenshot/text_screenshot/decorative)
        → VLM(glm 视觉模型)按类型化 prompt 生成描述 → 拒识幻觉检测+重试
落块环:描述包装成 <ImgThinking> 块(头部带"图片类型/源文件"),原位替换图片 run
docx 段落: "战斗流程如下图所示 [图片Run]"
   │ ① 提取图片引用 rId(blip 正则解析 → 对象遍历 → XPath,三级回退)
   │ ② 按 rId 从 docx 关系表取图片二进制
   │ ③ 原图落盘 assets/(分析失败也可重跑)
   │ ④ VLM 识图 → (描述 desc, 流程图结构 flow, 图片类型)
   │ ⑤ paragraph.add_run("<ImgThinking>…</ImgThinking>" + flow)   ← 文字插回原段落
   │ ⑥ 从 XML 删除原图片 Run
   ▼
docx 段落: "战斗流程如下图所示\n<ImgThinking>图为三阶段循环…</ImgThinking>"
   │ 阶段二格式转换:<ImgThinking> 就是普通文本,随段落自然进 md
   ▼
out.md 携带 <ImgThinking> 块
   │ generator 预处理(4.2 ②)正则抽走块、原位留 IMG_REF 占位符
   ▼
实体提取按占位符就近把图片理解挂回对应小节(两跳协议)

多图场景的处理节奏是逐张串行、当场回插:碰到一张图就完成"取 rId → 取字节 → 落盘 →
VLM 识图 → 描述回插"全程,再处理下一张——不存在"攒齐 a/b/c 一批再统一回填"(VLM 本就
单张调用,逐张也天然做到一张失败不拖累后续、图元级进度可上报)。两个精确到实现的事实:
①回插调的是 paragraph.add_run,即追加到所在段落末尾——同段多图时各描述按处理顺序
依次排在段末(策划文档的图通常独占一段,实际无感知);②删除原图片 Run 是按段落攒批的:
该段所有图处理完后统一从 XML 删掉,回插与删除不混在同一个循环里。

3.5 表格双通道:docx 管道表 vs xlsx 结构块

表格是策划文档的重头(数值表、配置表)。本工程对两类来源用可靠性等级不同的两条通道

  • docx 内嵌表格:原样转成 md 管道表(| a | b |)。结构化程度为零,进正文 section, 下游 LLM 按 prompt 规则自行理解表格行——灵活但随模型波动。
  • xlsx 独立表格ExcelParser 单跳直转,每行升格为 <ExcelTableThinking> 结构化块 (带 sheet 名/行号/跨表引用),下游用确定性代码把行合成 ConfigItem 配置实体,不经 LLM—— 稳定可重复,适合大批量数值配置(BOSS 技能表 930 行这种)。

一句话记住设计意图:轻表可读(md 表格给人看),重表可算(结构块给机器算)

暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册