想象你是一座联合国图书馆的馆长。每天都有世界各地的人来捐书:有法国人写的精装小说(PDF)、德国学者打印的学术报告(Word)、带着炫目插图和超链接的电子画册(HTML)、还有排版简洁的散文集(Markdown)。这些书语言不同、装帧不同、阅读方式也完全不同——如果直接把它们乱七八糟地堆进书库,读者永远找不到想要的那一页,图书馆也很快会乱成一锅粥。 于是你定下一条铁规矩:所有书必须先经过统一流程才能上架——在统一柜台收书、登记每本书的“身份证”信息、把内容翻译成同一门“世界语”抄本、最后按索书号摆上统一书架。RAG 系统的多格式文档处理,做的正是这件事:让 PDF、Word、HTML、Markdown 等千奇百怪的文档,变成知识库(向量索引)里整齐划一的高质量知识块,供大模型随时检索调用。

岗位关联

本文帮助 AI 应用开发工程师掌握多格式文档的统一解析、标准化与索引构建方法,让各类文档快速落地为 RAG 知识库的“弹药”,是搭建企业级知识库的基本功。

目录
  1. 可视化演示
  2. 统一解析接口:图书馆的“统一收书台”
  3. 格式差异处理:看懂每位捐书人的“装帧语言”
  4. 元数据提取:给每本书办一张“借书卡”
  5. 内容标准化:把各地方言改写成同一种“世界语”
  6. 统一索引构建:按索书号把书摆上统一书架
  7. 方案整体落地:图书馆的开馆全流程
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

动态演示RAG多格式文档处理方案的核心机制,通过开始/执行/监控/完成四种模式展示关键概念与交互流程。

本节配有交互动效(进入视口后自动加载,约 3KB)。

RAG多格式文档处理方案:多格式文 多格式文档统一解析接口架构设计 不同格式文档的差异处理与适配策 核心机制可视化 RAG多格式文档处理方案整体架构与关键组件

统一解析接口:图书馆的“统一收书台”

图书馆开门第一件事,不是搬书,而是设一个“统一收书台”。不管谁来捐书、捐什么装帧的书,工作人员都只做三件事:收下、盖章、开回执。捐赠者绝不允许抱着书直接冲进书库乱放——那样书库早就天下大乱了。RAG 系统的第一层,就是这个“收书台”:统一解析接口。

在代码层面,这个接口长得很简单:一个 parse(filepath) 方法,接收文件路径,返回一个标准化的 Document 对象,里面有 content(正文)和 metadata(元数据)两个字段。调用方完全不需要关心背后是 PDF 库还是 Word 解析器,只要对着同一个接口喊“我要解析这个文件”,就能拿到统一结构的结果。

实现这个接口的经典做法是“注册表 + 策略模式”:每种格式对应一个解析器类,启动时注册进一个字典,调用时按扩展名查表取出对应解析器。这样设计最大的好处是符合开闭原则——将来新增一种格式,只需要写一个新解析器并注册进去,老代码一行都不用改。

就这样,法语精装书还是日语漫画,收书台都能从容接待;至于书怎么拆封、怎么翻阅、要不要戴手套,那都是后台解析器的事,读者们(上层应用)永远只面对同一个干净的窗口。

D=Registry⁡[ext⁡(path)].parse⁡(path)D = \operatorname{Registry}[\operatorname{ext}(path)].\operatorname{parse}(path)
公式说明:ext(path) 从文件路径里取出扩展名,Registry 是一张“格式→解析器”的对照表,整个公式的意思是:按照扩展名找到对应解析器,调用它的 parse 方法,得到统一的 D(Document 文档对象)。
  1. 取扩展名从文件路径中提取出后缀名,作为查找解析器的钥匙。
    ext⁡(path)=文件路径的后缀,如 pdf、docx、html\operatorname{ext}(path) = \text{文件路径的后缀,如 pdf、docx、html}
  2. 查注册表用扩展名去注册表中找到对应的解析器类。
    Registry⁡[ key ]=与 key 绑定的具体解析器\operatorname{Registry}[\,key\,] = \text{与 key 绑定的具体解析器}
  3. 调用解析让解析器处理文件,产出统一结构文档。
    D=Document(content=…, metadata=…)D = \text{Document}(\text{content}=\ldots,\ \text{metadata}=\ldots)

格式差异处理:看懂每位捐书人的“装帧语言”

收到的书五花八门:有的书封皮硬挺(PDF),内页文字却像印刷体一样凝固在纸面上;有的书带着目录和页码结构(Word),翻起来章节分明;有的书里嵌着超链接和图片(HTML),像精装画册;还有的书排版极简,只靠几个井号标记章节(Markdown)。收书台不能要求所有捐书人都用同一种装帧,只能派人学会每种装帧的“打开方式”。

对应到技术上,就是摸清每种格式的脾气。PDF 的问题最棘手:它本质上是“打印后的快照”,没有稳定的文字流。对普通 PDF 可以用 pypdf 这类库按照阅读顺序提取文本;可一旦遇到扫描版 PDF,里面根本不存在文字,只是一张张图片,这时就必须动用 OCR(光学字符识别),把图片里的字“读”出来。

Word(.docx)其实是一个压缩包,内部是 XML 结构。用 python-docx 可以按段落(paragraphs)和表格(tables)两个维度遍历内容,段落顺序天然对应阅读顺序,表格也能逐行逐列读取。HTML 则需要用 BeautifulSoup 这样的解析器剥掉标签,保留 <h1>、<h2> 的标题层级和段落文本。Markdown 最轻松,语法本身接近纯文本,但要注意保留井号的层级信息,转成标准文本后这些层级还能用于后续分块。

看懂装帧方式只是第一步,接下来还得给每本书办“身份证”——这正是下一节的故事。

T={fpdf(P),如果格式是 pdffdocx(W),如果格式是 docxfhtml(H),如果格式是 htmlfmd(M),如果格式是 mdT =\begin{cases}f_{\text{pdf}}(P), & \text{如果格式是 pdf}\\f_{\text{docx}}(W), & \text{如果格式是 docx}\\f_{\text{html}}(H), & \text{如果格式是 html}\\f_{\text{md}}(M), & \text{如果格式是 md}\end{cases}
公式说明:T 表示提取出来的纯文本,f_pdf、f_docx、f_html、f_md 分别是四种格式的专属提取函数。它们输入各自的原始文件,输出统一的纯文本 T。
  1. PDF 提取f_pdf 处理 PDF 文件,扫描件还要叠加 OCR 步骤。
    fpdf(P)=PdfReader(P).逐页提取文本或 OCR 识别f_{\text{pdf}}(P) = \text{PdfReader}(P).\text{逐页提取文本或 OCR 识别}
  2. Word 提取f_docx 遍历 Word 的段落与表格。
    fdocx(W)=拼接所有 paragraph 与 table 的文本f_{\text{docx}}(W) = \text{拼接所有 paragraph 与 table 的文本}
  3. HTML 提取f_html 剥离标签、保留下文字内容。
    fhtml(H)=BeautifulSoup(H).get_text()f_{\text{html}}(H) = \text{BeautifulSoup}(H).\text{get\_text}()
  4. Markdown 提取f_md 直接读取文本并保留标题层级标记。
    fmd(M)=读取原文件并保留 # 标记f_{\text{md}}(M) = \text{读取原文件并保留 \# 标记}

元数据提取:给每本书办一张“借书卡”

一本新书入库前,编目员会给它办一张借书卡,上面写着书名、作者、出版时间、来源馆藏。没有这张卡,书丢了都不知道去哪儿找,读者按作者检索时也寸步难行。在 RAG 知识库里,这“借书卡”就是元数据(metadata)。

元数据是“关于数据的数据”。一个完整的文档元数据通常包括:标题(title)、作者(author)、创建时间(created)、来源路径(source)和格式类型(format)。这些信息从哪来?不同格式的“老底”不一样:PDF 文件头里自带标题和作者字段;Word 的核心属性 XML 里有 creator 和 created;HTML 的 <title> 标签和 <meta> 标签直接写着;Markdown 常在开头用 YAML 格式写 --- 分隔的 front matter。

提取元数据的价值体现在检索阶段。比如用户问“去年发布的财报里提到营收增长多少”,系统可以先按 created 年份过滤,再按 source 锁定期刊来源,精准缩小检索范围。更关键的是,每个分块(chunk)都携带元数据,那么答案生成后就能顺着来源一路回溯到原始文档,实现可追溯——这在大模型“一本正经胡说八道”的今天尤为重要。

借书卡办好了,内容还是各自的语言,得翻译成同一种“世界语”,才能统一入库。

Meta⁡={title, author, created, source, format}\operatorname{Meta} = \{\text{title},\ \text{author},\ \text{created},\ \text{source},\ \text{format}\}
公式说明:Meta 是文档的元数据集合,用一组键值对描述文档的身份信息。后续每个文本分块都会复制这份元数据,作为检索过滤和溯源的依据。
  1. 身份字段title 和 author 标记文档是谁写的、叫什么名字。
    title=文档标题,author=作者\text{title}=\text{文档标题},\quad \text{author}=\text{作者}
  2. 时间字段created 记录创建时间,支持按时间范围过滤。
    created=2024-01-15 这样的时间戳\text{created}=\text{2024-01-15 这样的时间戳}
  3. 来源字段source 记录文档原始路径,回答问题时可以往回溯源。
    source=文件路径或 URL\text{source}=\text{文件路径或 URL}
  4. 格式字段format 记住文档原本的格式,便于后续按类型筛选。
    format=pdf / docx / html / md\text{format}=\text{pdf / docx / html / md}

内容标准化:把各地方言改写成同一种“世界语”

联合国图书馆的书来自世界各地,如果只登记不翻译,读者借到一本德文书照样看不懂。于是编目员还要做一件苦差事:把所有书的内容改写成统一的“世界语”抄本。在 RAG 流水线里,这一步叫内容标准化(Normalization)。

标准化要处理几类脏东西。第一是噪声字符:PDF 提取结果里常常混入多余的换行、乱码和页眉页脚,需要用正则把连续空白压缩成一个空格;第二是编码问题:有的老文档是 GBK 编码,读进来全是乱码,要先探测编码再统一转成 UTF-8;第三是结构规整:把 PDF 里被硬切开的行合并成完整段落,保留 Word 和 Markdown 的标题层级。第四是表格处理:直接把表格内容扔掉太可惜,可以把每一行转成用竖线分隔的文本,既保留了关系又方便向量化。

标准化的黄金法则是:宁可清洗得彻底,不要原样塞进去。脏文本进向量库,检索时就会召回一堆垃圾;字符乱码进 embedding,语义直接被污染。清洗后的文本应该是干净的、连续的、语义完整的“世界语”——不管原始格式是什么,大家读起来都一样顺畅。

现在,所有书都变成了同样的“世界语”抄本。下一步,就是给这些抄本编号、贴上索书号,摆上统一书架。

Snorm=Trim⁡(Unify⁡(NoiseRemove⁡(Sraw)))S_{\text{norm}} = \operatorname{Trim}\Big(\operatorname{Unify}\big(\operatorname{NoiseRemove}(S_{\text{raw}})\big)\Big)
公式说明:原始文本 S_raw 先经过 NoiseRemove 去除噪声(空白、乱码、页眉页脚),再经过 Unify 统一编码和换行,最后 Trim 去掉首尾多余空格,得到标准化文本 S_norm。
  1. 去噪声把连续空白、制表符、页眉页脚等无意义内容清理掉。
    NoiseRemove⁡(S)=用正则替换连续空白为单个空格\operatorname{NoiseRemove}(S)=\text{用正则替换连续空白为单个空格}
  2. 统一编码与换行把所有字符统一成 UTF-8,换行符统一为 \n。
    Unify⁡(S)=检测编码并转换为 UTF-8 格式\operatorname{Unify}(S)=\text{检测编码并转换为 UTF-8 格式}
  3. 规整段落与首尾合并被硬切的行、裁掉首尾多余空白。
    Trim⁡(S)=去除首尾空格,恢复完整段落边界\operatorname{Trim}(S)=\text{去除首尾空格,恢复完整段落边界}

统一索引构建:按索书号把书摆上统一书架

图书馆里真正决定检索效率的,是书架和索书号。编号一旦混乱,读者报出书名,管理员跑遍全馆也找不到。RAG 的索引构建,就是给标准化后的长文本“编号上架”——不过它编的不是整本书,而是把书拆成一章一章、一页一页,每一页都贴上自己的“书架号”。

第一步是分块(Chunking)。整本书太长,embedding 模型一次装不下,检索时精度也差,所以按固定窗口把文本切成小块。切块不能简单一刀切到底,否则一个完整句子可能被拦腰斩断,语义就碎了。于是引入重叠窗口(overlap):下一块从上一块结尾往回退一小段再开始,让衔接处的语义既被前一块覆盖,也被后一块覆盖。行业里常用的配置是:块大小 500 字符,重叠 50 字符。

第二步是向量化。每一块文本送入 embedding 模型,映射成一个高维向量。语义相近的文本,向量在空间里也靠得近。第三步是索引存储。把向量连同元数据一起写入向量数据库(如 FAISS、pgvector),形成一个带“目录卡”的统一书架。检索时,用户的问句同样被向量化,去书架上找距离最近的几块内容,再交给大模型生成答案。

拆好块、编好号、贴上“书架号”,接下来要考虑的就是整座图书馆怎么日常运转了。

Chunki=Text [ si:si+L ],si+1=si+L−O\text{Chunk}_i = \text{Text}\,[\,s_i : s_i + L\,],\qquad s_{i+1} = s_i + L - O
公式说明:Text 是标准化后的全文,L 是块大小,O 是重叠长度。第 i 块从位置 s_i 截取到 s_i + L,下一块起点 s_{i+1} 则从 s_i + L - O 开始,保证块与块之间有一段语义重叠。
  1. 确定块长L 是一次截取的字符数,通常取 400~800。
    L=500 字符L = 500\ \text{字符}
  2. 截取第 i 块从起点 s_i 开始连续截取 L 个字符。
    Chunki=Text[si:si+L]\text{Chunk}_i = \text{Text}[s_i : s_i + L]
  3. 计算下一块起点起点向后推进 L - O,O 是重叠长度。
    si+1=si+500−50s_{i+1} = s_i + 500 - 50
  4. 向量化入库每块文本 Chunk_i 变成向量,连同元数据写入向量库。
    Embedding(Chunki)→向量数据库\text{Embedding}(\text{Chunk}_i) \rightarrow \text{向量数据库}

方案整体落地:图书馆的开馆全流程

基础能力都齐了,但图书馆真正开馆迎客,靠的是整套运营流程:书怎么排队进馆、书坏了怎么办、新书来了怎么上架、读者满意度怎么度量。落到 RAG 工程上,就是流水线设计、失败重试、增量更新和效果评估四件事。

流水线设计:解析 → 清洗 → 提取元数据 → 分块 → 向量化 → 入库,六个环节串成一条管道。每个环节只依赖前一个的输出,这样任何一段都能单独替换。失败重试:企业里有几千份文档,难免有几份损坏的 PDF 或编码诡异的 Word。绝不能因为一个坏文件就让整个流水线崩溃——记录日志、跳过坏文件、继续跑完,最后统一生成失败报告让运维处理。

增量更新:全量重建索引成本太高,最好只处理新加或改动的文件。判断依据是文件内容哈希:新文件的哈希和旧文件不同,才重新解析入库;相同的直接跳过。效果评估:入库不是终点,要定期用一批代表性问题验证召回率——理想情况是 10 个问题里有 9 个能从知识库中找到正确支撑材料。评估结果反推调整分块大小、清洗规则和元数据策略。

到这一步,图书馆已经有了完整的收书、编目、翻译、上架、运营体系。不同语言、不同装帧的书籍终于在这座“联合国图书馆”里和谐共处,读者(大模型)随时能找到自己想要的那一页——这就是多格式文档统一处理的全部意义。

changed=1{hash⁡(filenew)≠hash⁡(fileold)}\text{changed} = \mathbb{1}\{\operatorname{hash}(\text{file}^{\text{new}}) \neq \operatorname{hash}(\text{file}^{\text{old}})\}
公式说明:hash 是文件的内容指纹,只要文件内容有一个字节变化,哈希值就会完全不同。1 表示文件变了、需要重新处理,0 表示没变、可以跳过。这就是增量更新高效的原因。
  1. 计算新指纹对当前磁盘上的文件计算内容哈希。
    hash⁡(filenew)=md5 或 sha256 指纹\operatorname{hash}(\text{file}^{\text{new}}) = \text{md5 或 sha256 指纹}
  2. 对比旧指纹与知识库里记录的历史哈希比较是否相同。
    hash⁡(filenew)≠hash⁡(fileold)\operatorname{hash}(\text{file}^{\text{new}}) \neq \operatorname{hash}(\text{file}^{\text{old}})
  3. 输出变更标记不相等为 1(重新解析),相等为 0(跳过)。
    1{条件成立}=1 或 0\mathbb{1}\{\text{条件成立}\} = 1\ \text{或}\ 0

AI 应用场景

企业知识库建设:让散落的文档变成可检索的资产

阶段:文档加载与处理

解决的问题:企业里沉淀着 PDF 合同、Word 制度、HTML 帮助中心网页、Markdown 技术文档,格式五花八门。过去做知识库要把文档一个个手动复制粘贴成纯文本,费时费力还丢信息。用统一解析方案后,只需把文件路径交给解析器,系统自动完成解析、清洗、元数据提取和入库。

具体案例:某制造企业把 5000 份设备手册(含扫描 PDF)和 200 份内部 SOP(Word 文档)批量导入 RAG 知识库。扫描 PDF 走 OCR、Word 走段落遍历,全部统一成标准块并标注设备型号元数据。工程师提问“XX 型号的液压系统故障代码 07 怎么处理”,系统按元数据过滤出对应手册,准确给出操作步骤并附上原文页码。

客服知识库:从多格式 FAQ 到智能问答

阶段:索引构建与检索

解决的问题:客服资料分散在官网 HTML 页面、产品经理写的 Markdown 文档、还有 PDF 版用户协议里。格式不统一导致检索召回混乱。统一处理后,所有 FAQ 被分块向量化,并按产品线、文档类型打上元数据标签。

具体案例:某 SaaS 公司用统一解析管道构建客服知识库后,智能客服对“如何导出报表”这类问题的准确解答率从 61% 提升到 84%。原因是 HTML 中的标题层级在标准化时被保留,分块策略按层级切分,每个块都对应一个完整小节,语义更完整。

学术论文检索系统:让 PDF 变成可引用证据源

阶段:文档解析与元数据管理

解决的问题:学术论文几乎全是 PDF,且常含双栏排版、页眉页脚、图表等噪声。同时论文的标题、作者、年份、期刊等元数据高度结构化,但零散混在首页。统一解析方案可以在提取正文的同时,解析出论文标题、作者和年份,让检索系统支持“2023 年之后关于大模型的综述”这类组合查询。

具体案例:某高校实验室搭建论文知识库,用统一解析器批量处理 3000 篇 PDF,双栏文本按阅读顺序重组,页眉页脚被清除,年份与作者写入元数据。检索“近两年 Transformer 变体的效率对比”,系统先按元数据过滤时间范围,再在正文中召回高相关段落,回答时直接标注出自哪篇论文。

Python 演示

python75 行
"""RAG 多格式文档统一处理演示:统一解析接口 + 标准化 + 分块"""import reimport hashlib  class DocParser:    """统一解析器:按扩展名分发到具体格式的解析逻辑"""     def __init__(self):        self.parsers = {            "pdf": self._parse_pdf,            "html": self._parse_html,            "md": self._parse_md        }     def parse(self, path):        ext = path.rsplit(".", 1)[-1].lower()        if ext not in self.parsers:            raise ValueError("{} 格式暂不支持".format(ext))        raw = self.parsers[ext](path)        return self.clean(raw)     def _parse_pdf(self, path):        try:            from pypdf import PdfReader        except ImportError:            return "[PDF 解析器未安装 pypdf]"        pages = []        for page in PdfReader(path).pages:            pages.append(page.extract_text() or "")        return "\n".join(pages)     def _parse_html(self, path):        try:            from bs4 import BeautifulSoup        except ImportError:            return "[HTML 解析器未安装 beautifulsoup4]"        with open(path, "r", encoding="utf-8") as fh:            soup = BeautifulSoup(fh.read(), "html.parser")        return soup.get_text("\n")     def _parse_md(self, path):        with open(path, "r", encoding="utf-8") as fh:            return fh.read()     def clean(self, text):        text = re.sub(r"\s+", " ", text)        text = re.sub(r"[ \t]+", " ", text)        return text.strip()  def chunk_text(text, size=500, overlap=50):    """按固定窗口切分文本,带重叠避免切断语义"""    chunks = []    start = 0    step = size - overlap    while start < len(text):        chunks.append(text[start:start + size])        start += step    return chunks  def file_digest(path):    """计算文件哈希,用于增量更新的判断"""    return hashlib.md5(open(path, "rb").read()).hexdigest()  if __name__ == "__main__":    import sys    parser = DocParser()    for path in sys.argv[1:]:        content = parser.parse(path)        chunks = chunk_text(content)        print("文件:{} 块数:{} 指纹:{}".format(            path, len(chunks), file_digest(path)))
目的:演示一个极简的 RAG 多格式文档处理流程——按扩展名分发到对应解析器,统一清洗后按窗口分块,最后输出每份文件的块数与内容哈希指纹。
  1. ext = path.rsplit(".", 1)[-1].lower()取出文件后缀,作为解析器的查找钥匙。
  2. self.parsers = {...}用字典维护“格式→解析函数”的注册表,新格式只需加一项。
  3. raw = self.parsers[ext](path)从注册表取函数并调用,这就是统一调度核心。
  4. return "\n".join(pages)把 PDF 每页提取结果拼接成完整文本。
  5. soup.get_text("\n")BeautifulSoup 剥离 HTML 标签,用换行分隔文本块。
  6. self.clean(raw)统一清洗:压缩空白、去除多余空格,产出标准化文本。
  7. chunks.append(text[start:start + size])按 500 字符窗口切块。
  8. start += step步长 = 块大小 - 重叠,保证相邻块语义衔接。
  9. hashlib.md5(...).hexdigest()计算文件指纹,为增量更新做准备。

常见误区

误区我用 pypdf 读 PDF,有的文件读出空文本,一定是我的代码写错了。PDF 不都是直接能提取文字的吗?

正解PDF 分两种:文字型 PDF 有文本层,可以提取;扫描型 PDF 本质是图片,根本没有文本层,必须用 OCR 识别。就像一本印刷的书可以快速翻页扫描,但如果是一本“手抄影印本”,必须连认带猜才能读出内容。遇到空文本时,先判断是否扫描件,再决定是否上 OCR。

为何常见:很多人第一次接触 PDF 是从网上下载的文字电子书,没见过扫描件,于是默认所有 PDF 都能直接提取文本。

误区先用工具把 PDF、Word 都转成纯文本文件,再统一读入知识库,不就不需要处理格式差异了吗?

正解转换会丢大量信息。Word 转 TXT 后表格结构没了、标题层级没了;PDF 转 TXT 后元数据丢了、双栏排版可能错乱。这就像把精装书拆页压平当散纸保存——字还在,但书的“结构”全毁了。统一解析的意义恰恰在于:在保留结构的同时标准化内容,而不是简单降级成纯文本。

为何常见:纯文本是最容易处理的形式,大家本能地想“曲线救国”,却没意识到转换本身就在破坏数据结构。

误区分块嘛,设个 500 字符全局套用就行,反正都是文本,切碎了不都一样?

正解不同格式的语义密度完全不同。Markdown 的标题层级适合按 # 语义切块,代码块要整块保留;表格按行转成的文本,一条记录就是完整语义;而 PDF 双栏页面切出的文本可能是两栏内容的交错。就像菜谱按“步骤”切分最合理,而小说按“章节”切分更自然。分块策略必须结合格式特点设计,而不是一刀切。

为何常见:分块参数看起来只是一个数字,初学者容易忽略文本结构对语义完整性的影响。

误区知识库只要有正文向量就够了吧?元数据不就是文件名和日期的边角料,存不存无所谓。

正解元数据在检索中的作用举足轻重。有了 created 字段,可以过滤“去年”的文档;有了 source 字段,回答时能追溯原文;有了 format 字段,可以按类型筛选。没有元数据,向量检索就像在图书馆里闭着眼摸书,摸到什么算什么;有了元数据,你可以在精确到书架的范围内查找,精度和效率天差地别。

为何常见:因为向量检索“按语义找内容”非常直观,让人误以为一切内容问题都能靠语义解决,忽略了结构化筛选的价值。

误区流水线里一个 PDF 解析失败了,程序立刻崩掉,我觉得这样“及时暴露问题”是对的。

正解真实知识库动辄数千文件,一定有个别损坏文件。让整个流水线崩溃,等于因为一本书破损就把图书馆关门。正确的做法是:捕获异常、记录日志、跳过坏文件,让其余 999 份正常入库,最后生成一份失败清单人工复核。容错机制不是一个功能,而是多格式处理的必备能力。

为何常见:开发者在写代码时默认“输入是合法的”,但现实世界的文件五花八门,缺页、加密、损坏都是常态。

面试题

Q1. RAG 系统中多格式文档统一解析的首要目标是什么?

单选题 · easy

  • A.将不同格式统一为标准结构文档← 正确答案
  • B.压缩文件以节省磁盘存储空间
  • C.文档格式转换后再翻译成英文
  • D.直接删除格式不规范的内容

答案:A。统一解析的核心是“把千差万别的格式收敛成一个标准结构”(如 content + metadata),让上层不再关心格式差异。压缩、翻译、删除都偏离了主线,就好比图书馆收书的目的是统一编目,而不是把书压扁或扔进碎纸机。

Q2. 拿到一份扫描版 PDF,最常见的处理手段是什么?

单选题 · easy

  • A.直接调用 OCR 识别图像中的文字← 正确答案
  • B.用 pypdf 直接提取文本层内容
  • C.将 PDF 转为 Word 再提取文本
  • D.用正则表达式搜索 PDF 内容

答案:A。扫描版 PDF 本质是图片,没有文本层,pypdf 提取不到内容。OCR 是把图片中的字识别为文字的正确手段,类似人工读图抄写。转 Word 或正则搜索都不能凭空变出文字。

Q3. 设计统一解析接口时,最符合开闭原则的做法是什么?

单选题 · mid

  • A.新增格式注册解析器且不修改旧代码← 正确答案
  • B.每次调整 parse 函数适配新格式
  • C.用 if-else 堆叠所有格式分支
  • D.让调用方自行完成格式解析

答案:A。开闭原则说的是“对扩展开放、对修改关闭”。注册表 + 策略模式让新格式只增不改;if-else 堆叠每加一种格式就得改老代码;让调用方自己解析则把复杂度踢给了上层,违背了统一接口的初衷。

Q4. 文档分块时设置 overlap(重叠窗口)的主要目的是什么?

单选题 · mid

  • A.防止语义在边界处被硬切断← 正确答案
  • B.减少向量索引的存储开销
  • C.缩短文本向量化的计算时长
  • D.保证每个分块长度完全相等

答案:A。overlap 让相邻块在边界处有一段重复内容,即使一个句子恰好落在块边界,也能完整出现在某一块中,避免语义断裂。它反而会增加存储和计算开销,也无法保证块长相等。

Q5. 元数据在 RAG 检索中的作用有哪些?

多选题 · mid

  • A.支持按来源精确过滤文档← 正确答案
  • B.直接替代文本向量完成召回
  • C.为回答内容提供溯源信息← 正确答案
  • D.替代分块策略的唯一依据

答案:AC。元数据可以像索引卡一样按来源、时间、格式过滤候选文档(选 A),也能在回答问题时回溯原始路径(选 C)。但元数据不参与语义匹配,无法替代向量召回,更不是分块策略的依据。

Q6. PDF 格式解析困难通常体现在哪些方面?

多选题 · mid

  • A.扫描件缺少可提取的文本层← 正确答案
  • B.文本块与排版顺序不固定← 正确答案
  • C.XML 嵌套层级过深难以遍历
  • D.标签语义不明确必须依赖猜测

答案:AB。扫描 PDF 只有图像没有文字(选 A);PDF 的文本块在页面上的顺序与阅读顺序可能不一致,双栏排版尤其麻烦(选 B)。XML 嵌套是 Word 的问题,标签语义模糊是 HTML 的毛病,各有各的“方言”。

Q7. 某文档用 GBK 编码保存,进入内容标准化阶段后应当如何处理?

单选题 · hard

  • A.检测编码并统一转换为 UTF-8← 正确答案
  • B.保留 GBK 编码直接写入索引
  • C.丢弃非 UTF-8 的全部内容
  • D.把二进制内容直接去做向量化

答案:A。标准化要求所有文档统一为 UTF-8,这样后续向量化和检索才有一致的编码基准。保留 GBK 会导致其他环节默认按 UTF-8 解码时报错;丢弃内容更是损失数据;二进制无法直接向量化。

Q8. 内容标准化通常包含哪些动作?

多选题 · mid

  • A.去除连续空白和乱码字符← 正确答案
  • B.统一换行与段落格式规范← 正确答案
  • C.删除所有表格内容以减噪
  • D.统一字符编码为 UTF-8 格式← 正确答案

答案:ABD。标准化像给文本“洗脸梳头”:清空白(A)、理段落(B)、统一编码(D)。但表格内容往往承载关键信息,应转为竖线分隔的文本保留,而不是直接删掉(C 错)。

Q9. 知识库增量更新时,判断文件是否变化最可靠的方法是哪个?

单选题 · hard

  • A.对比文件内容哈希是否改变← 正确答案
  • B.对比文件名称是否发生改动
  • C.对比文件大小是否发生变化
  • D.对比文件修改时间戳是否更新

答案:A。内容哈希是文件的“指纹”,任何字节变化都会导致哈希改变,最可靠。文件改名不一定是内容变;大小相同内容可能不同;时间戳可以被手动修改。就像判断书是否改版,最准的办法是对照内容,而不是看封面换没换。

Q10. 多格式文档流水线中,单个文件解析失败时最合理的策略是什么?

单选题 · hard

  • A.记录日志跳过该文件并继续处理← 正确答案
  • B.终止全部流水线等待人工修复
  • C.静默忽略失败并删除源文件
  • D.将失败文件内容原样写入索引

答案:A。真实文件总有损坏或加密的情况,让流水线具备“跳过 + 日志 + 失败清单”的容错能力,才能保证批量任务不中断。终止全部会让几十万份好文档陪葬;静默忽略且删源文件会弄丢原始数据;写坏数据进索引等于埋毒药。