Cohere 发布了 Parse 5,面向复杂文档的多模态解析。所谓多模态,指的不只是文字:版式、表格、图表、文字混排,Parse 5 都能从复杂文档里高效提取。对长期被 PDF 表格、扫描件、PPT 版式折磨的接入方来说,这算得上一次关键升级。

为什么把这一期放进"接入降本"系列?因为文档解析是 RAG 与智能体知识库的第一道工序,也是被低估最严重的成本黑洞。解析质量差,后面所有环节都在为错误买单。

Parse 5 更新的重点在多模态信息的保真:图表不再是一张孤立的截图,而是连同位置、标题与可检索的描述一起输出;表格还原成真实的行列结构,而不是被拍平成字符串。对知识库接入来说,这两点直接决定检索能不能命中。

2. 痛点:解析翻车,答案连带翻车

先讲一个我经手过的真实场景。某团队把一批产品手册接入知识库,PDF 里是双栏排版、带页眉页脚、夹杂大量表格和截图。他们图省事,用最简单的文本抽取工具直接切块,结果:表格被抽成一行行散落的字符串,双栏文字交叉拼接,页眉页脚混进正文。入库之后,Embedding 算出来的向量一团糟,检索召回率惨不忍睹。用户问"第三季度退货率是多少",系统召回的是"联系电话:400-xxx"。

关键认知是:解析质量直接决定 RAG / 智能体知识库的命中率。切块是 API 之前的第一道工序,切块之前还有解析。这道工序做不好,后面模型再强也答不对——不是模型不行,是喂进去的东西本身就是碎的。答案翻车,十有八九是解析先翻车。

再算一遍成本账,就知道解析省下的钱迟早加倍还回去。解析碎了,切块跟着碎,Embedding 向量混在一起,召回差,用户问不到答案就开始反复重试;重试还不行,就得人工返工——重新解析、重新切块、重新入库、重新验证,每一轮都是工程师工时和 API 调用费。我见过一个团队在脏数据上返工了三轮,花费早超过当初买托管解析的钱。解析这道工序,省不得。

智能体场景更敏感。智能体要自主调用工具、拆解任务,靠的就是知识库给的上下文;上下文里是一堆错位文字,智能体就会一本正经地胡说。解析质量差,最先暴露的就是这类问题,因为重试多少次都一样,错的底料做不出对的菜。

3. 原理速览:一条完整的解析链路

要把"解析翻车"讲清楚,先看这条链路:

原始文档(PDF / DOCX / PPTX / XLSX / 扫描件)
        │
        ▼
   版面分析(Layout Analysis)
   识别标题、段落、表格、图表、页眉页脚、阅读顺序
        │
        ▼
   元素抽取(Element Extraction)
   文字 OCR、表格结构还原、图表区域定位与描述
        │
        ▼
   结构化输出(Structured Markdown / JSON)
   保留标题层级、表格行列、图片与图表占位符
        │
        ▼
   切块入向量库(Chunking → Vector DB)
   按标题层级切块、控制重叠,再喂给 Embedding API
        │
        ▼
   RAG 检索 → 大模型生成最终答案

Parse 5 这类托管解析能力做的是前三段:把复杂版式还原成结构化的 Markdown / JSON。后两段——切块与检索——由我自己的管线负责。这条链路里任何一段偷懒,最终答案都会连带翻车。反过来,前三段做得越扎实,切块越有语义边界,检索命中越准,模型答对的概率越高。

结构化输出的价值正在于切块。按标题层级切块,块与块之间才有清晰的语义边界;表格保持行列结构,切块才不会把一行数据腰斩;图表带着描述文字,检索时才能被关键词命中。这些都是 markdown 结构直接带来的好处,也是为什么托管解析和开源转换都把 markdown 当作标准中间格式。

4. 托管解析 API vs 开源自建

接入方真正纠结的选型问题是:Parse 5 这类托管解析 API,和 microsoft/markitdown 这类开源自建,到底选哪个。我把两边的账摊开:

维度 托管解析 API(Parse 5 一类) 开源自建(markitdown 一类)
上手成本 调 API 即用,几行代码接入 本地安装依赖,脚本自维护
复杂版式质量 版面分析 + OCR + 表格还原开箱即用 简单版式够用,复杂版式需自己调
成本结构 按页计费,量大可谈阶梯 基本只花算力与维护时间
数据隐私 文档会经过第三方服务 全程本地,适合敏感文档
更新维护 厂商持续迭代,比如 Parse 5 依赖开源社区,升级自己跟进
适合场景 公开资料、海量文档、要速度 敏感文档、固定版式、要省钱

我的结论是:这不是二选一,而是按文档类型分流。公开手册走托管 API 省事,含客户数据、合同、内部制度的文档走本地路线。两条腿走路,成本和风险才都能压住。

5. 开源侧的风向:markitdown 为什么这么火

开源侧的信号同样强烈。microsoft/markitdown 在 GitHub 趋势榜上热度极高,星标数约 18 万,做的事情朴素到极致:把 Office 文件批量转成 markdown。为什么一个"格式转换工具"能火成这样?因为越来越多的接入方终于意识到,解析选型如今是自提管线的第一步——后面接的 RAG、智能体、知识库,全都要吃这份 markdown。

markitdown 的意义在于把"解析"从黑盒变成了可控管线:转出来的 markdown 我能直接看、直接改、直接抽检。托管 API 返回的是结果,开源自建返回的是过程,过程在手,质量才有抓手。这正是下一节教程的思路:先批量转换,再逐项抽检,把质量问题堵在入库之前。

从成本账看也说得通:解析引擎的研发投入极高——版面分析、OCR、表格识别、阅读顺序还原,每一块都是独立的技术栈,自建到生产级要养一个团队。markitdown 把门槛降到了 pip install 一条命令,而 Parse 5 这类托管 API 把质量做到了开箱即用。开源负责把下限抬高,托管负责把上限顶高,中间的空间留给接入方自己权衡。

6. Python 教程:markitdown 批量转换

先说清楚定位:下面的脚本走的是本地自建路线,适合文档不打算外传的场景;如果手头是海量公开文档、想省维护时间,托管解析 API 按页计费也不贵,混合策略在第 4 节和第 9 节有展开。转换脚本如下:

from pathlib import Path

from markitdown import MarkItDown

md = MarkItDown()
input_dir = Path("./docs_in")
output_dir = Path("./docs_out")
output_dir.mkdir(exist_ok=True)

# 支持的 Office 与 PDF 格式
SUFFIXES = {".docx", ".pptx", ".xlsx", ".pdf"}

for f in sorted(input_dir.rglob("*")):
    if f.suffix.lower() not in SUFFIXES:
        continue
    try:
        result = md.convert(str(f))
        out = output_dir / f"{f.stem}.md"
        out.write_text(result.text_content, encoding="utf-8")
        print(f"[OK] {f.name} -> {out.name} ({len(result.text_content)} 字符)")
    except Exception as e:
        print(f"[FAIL] {f.name}: {e}")

运行前先安装依赖:pip install "markitdown[all]"。批量转换产出的是原始 markdown,质量如何,还要抽检。这套管线跑通之后,Embedding 与推理环节我走 4sapi(https://4sapi.com)的合规中转接入,把整条链路的 API 成本统一管起来。

7. 质量抽检脚本:三件事必须查

批量转换之后,我永远先跑一遍抽检再入库。抽检只查三件事:标题层级、表格行列数、图表占位符。

import re
from pathlib import Path

def check_md(path: Path) -> list[str]:
    text = path.read_text(encoding="utf-8")
    issues = []

    # 1. 标题层级:出现跳跃说明版面分析丢了中间层级
    levels = [len(m.group(0)) for m in re.finditer(r"^(#+)\s", text, re.M)]
    for prev, cur in zip(levels, levels[1:]):
        if cur > prev + 1:
            issues.append(f"标题层级跳跃: H{prev} -> H{cur}")

    # 2. 表格行列数:行首行尾管道符数量不一致说明表格还原失败
    table_lines = [ln for ln in text.splitlines() if ln.strip().startswith("|")]
    if table_lines:
        expected = len(table_lines[0].strip("|").split("|"))
        bad = [ln for ln in table_lines[1:]
               if len(ln.strip("|").split("|")) != expected]
        if bad:
            issues.append(f"表格列数不一致: 期望 {expected} 列")

    # 3. 图表占位符:图片引用指向不存在的文件,说明图表被丢弃
    imgs = re.findall(r"!\[[^\]]*\]\(([^)]*)\)", text)
    missing = [src for src in imgs if not (path.parent / src).exists()]
    if missing:
        issues.append(f"图表占位符缺失 {len(missing)} 处: {missing[:3]}")

    return issues

for md_file in sorted(Path("./docs_out").glob("*.md")):
    issues = check_md(md_file)
    print(f"{md_file.name}: {'OK' if not issues else issues}")

三条检查对应三种最常见的解析事故。标题层级跳跃意味着标题树断了,切块会失去语义边界,检索时上下文被拦腰截断;表格列数不一致意味着结构化信息退化成字符串,数值型问题从此无解;图表占位符缺失意味着图表内容根本没进知识库——而图表恰恰是复杂文档信息密度最高的部分,丢了图表,等于丢了半份文档。

8. 解析质量评分表

抽检脚本只能抓硬伤,完整的质量评估我用下面这张评分表,每项满分 10 分:

质量维度 检查方法 权重 达标线
标题层级完整 抽检脚本 + 人工抽查目录 25% ≥ 8
正文连贯性 抽查段落是否错乱、重复 20% ≥ 8
表格结构还原 行列数校验 + 抽样核对数值 20% ≥ 9
图表与图片 占位符完整性 + 描述文字 15% ≥ 7
OCR 准确率 扫描件抽样人工比对 15% ≥ 8
特殊符号 公式、单位、代码块抽查 5% ≥ 7

加权总分低于 8 分的文档,我会打回重新解析或换解析路线。规则很简单:质量分不达标,宁可不上库,也不把脏数据喂给 Embedding API——脏数据进库容易,清洗返工的成本高得多。

评分表还有一个隐藏作用:给托管 API 和自建管线设定同一把尺子。同一批文档,两条路线各解析一遍,对照评分,谁的分高就用谁,分数接近就看成本。选型不再靠感觉,而是靠数据说话。

9. 成本与风险提示

托管解析 API 与自建路线各有各的账,三个风险点必须提前想清楚:

混合路线的具体做法,我一般这样切:公开产品手册、行业报告这类非敏感文档,量大时交给托管解析 API,质量高、维护省;合同、员工档案、客户数据一律本地解析,解析结果同样进同一套抽检与评分流程。两条管线的产出格式统一成 markdown,后续切块与入库完全共用一套代码,切换成本接近于零。

10. 验收清单

上生产前,我按这张清单逐项打勾:

清单里最后两项最容易偷懒,恰恰最关键:切块参数不验证,向量库就是盲盒;典型问题不实测,上线即翻车。这两项做完了,这一期的接入成本账才算真正闭环。

11. 总结

这一期讲清楚了文档解析这条第一道工序:解析质量决定 RAG 命中率,Parse 5 把托管解析的复杂版式质量拉到了新的水位,而 markitdown 的 18 万星证明开源自建管线同样可行,真正聪明的做法是按文档敏感度和版式复杂度分流,把解析成本花在该花的地方。4sapi 在 https://4sapi.com 提供合规的大模型 API 中转,把 Embedding、推理这一串 API 的接入成本压下来,解析这一步做扎实,整条链路的钱才算花得值。我踩过的解析坑,多半都是同一条管线上的常见坑——评论区聊聊,交换各自的翻车经历。