面试官:“你简历写做过 RAG,怎么还会引用旧制度?”我:“向量只认相似,不认作废。”

0 阅读12分钟

面试日记 第 34 天

工单刚升级到二线,客服还在照着一条已经作废的制度回复用户。

现行版和旧版明明都进了知识库。页面上也能搜到两份全文,文件名、正文、附件一个不少。可客服问“这种情况该怎么处理”,系统引用的偏偏是旧版里的处理口径。

面试官把我的简历翻到“RAG 知识库”那一行,又把两份制度的检索结果并排放在我面前:“你写做过这套系统,数据都入库了,为什么还会答错?”

我先看了命中的片段:“向量只认相似,不认作废。”旧版正文和用户问题更像,现行版虽然更新,却没有把生效时间、失效状态和替代关系带进可过滤的元数据。向量检索只负责找语义相近的内容,不会自动知道哪一份还有效。

“先把旧版删掉?”我问。

她摇了摇头:“历史工单还要追溯。删了,去年为什么这么处理就查不到了。”

这就不能靠删文件解决。原始文档要保留,进入当前客服知识库的检索资格却要单独管理:解析阶段提取制度编号、发布时间、生效时间和版本;清洗阶段识别重复正文与替代关系;分块时让每个片段继承版本状态;召回前先按业务时间和权限过滤,再做向量与关键词检索。

我把流程写到白板上,她又追问:“如果新版只改了其中一条,怎么避免整份文档重新切完以后,新旧段落混在一起?”

“不能只给整份文件一个版本号。”我说,“块 ID 要稳定,每个块带来源、章节和版本。更新时对比清洗前后的差异,只重建受影响的块和索引;旧块转为历史状态,但保留原文与处理日志。上线前再用一组同时覆盖当前问题和历史追溯的问题做回归。”

她把检索结果往下滑了一页。另一个旧版本因为页眉重复,连续占了三个召回位置。系统不是没有知识,而是把错误版本和重复内容排在了正确答案前面。

我原先那句“把制度全文入库就行”,到这里已经不够用了。RAG 的数据预处理并不是让文本看起来更干净,而是让每个片段都保留结构、身份、时间和可追溯关系。

她随后给出了今天的原题:

在 RAG 应用中为了优化检索精度,其中的数据清洗和预处理怎么做?

这道题表面上问清洗,真正考的是候选人有没有处理过脏数据。只回答“去重、去符号、做分块”,最多算知道几个术语;能把多源接入、结构保留、隐私处理、质量校验、元数据和索引串起来,才像做过生产知识库。

回答重点

面试时可以按照数据进入 RAG 系统的顺序回答:接入与标准化、清洗与脱敏、结构恢复与分块、元数据标注、索引构建、质量验收。

1. 数据接入与格式统一

企业数据通常来自 PDF、Word、CSV、Excel、HTML、API 和图片,不同来源要使用不同的解析方式。

  • 文本型 PDF、Word:使用对应文档解析器提取正文、标题和段落。
  • 扫描 PDF、图片:通过 OCR 获取文字,同时尽量保留坐标、表格和版面结构。
  • HTML:用 BeautifulSoup 等工具提取正文,过滤导航、广告和页脚。
  • Excel、CSV:用 Pandas 等工具读取表格,保留工作表、表头、行列和数据类型。
  • API:记录接口来源、请求时间、数据版本和字段含义。

解析结果最好先转换成统一的中间数据结构。例如每条内容都包含 contentsourcepagesectioncontent_typemetadata,后面的清洗、分块和索引就不用反复适配文件格式。

编码可以统一为 UTF-8,异常乱码、不可见字符和多余空白要规范化。大小写、标点和 Markdown 不应该无差别删除:iPhone、代码标识符、章节标题和列表结构都可能影响语义,应根据业务场景选择性处理。

2. 清洗降噪与敏感信息处理

清洗的目标是减少无效重复,同时保留会影响理解和追溯的信息。

  • 去除重复页眉、页脚、水印、导航栏和连续重复段落。
  • 处理乱码、异常换行、扫描噪声和无意义控制字符。
  • 识别文档版本,避免同一制度的旧版和新版被同时当成当前答案。
  • 使用正则或 PII Detection 工具识别手机号、邮箱、身份证号等敏感信息,根据权限进行删除、掩码或加密。
  • 保留标题层级、列表、代码块、表格边界和引用关系,避免为了“干净”把文档结构洗掉。

生产环境里不要覆盖原始文件。原始数据、清洗结果和规则版本应分别保存,方便误删后回溯。

3. 恢复结构并进行分块

分块不能只看字符数,还要看内容类型。

普通正文可以按照“标题 -> 段落 -> 句子 -> 字符或 token”逐级切分,尽量避免把同一个观点从中间截断。300-500 个汉字或 500-800 tokens 可以作为初始实验值,但不是所有项目的固定答案,最终还要结合文档密度、Embedding 模型、召回数量和上下文预算调优。

相邻块可以保留约 10%-15% 的重叠,降低跨段语义被切断的概率。重叠比例过高会产生大量重复召回,过低又容易丢失上下文,需要用真实查询集验证。

表格、代码、FAQ 和合同条款应单独处理:

  • 表格保留表头与数据行的对应关系,必要时转成 Markdown 或“字段名:字段值”的文本。
  • 代码优先按类、函数或语法结构切分,不从字符中间截断。
  • FAQ 尽量让问题和答案处于同一块。
  • 合同条款保留章节编号、定义和引用关系。

实现时可以使用递归字符分割器,优先按标题、段落和句子切分,超长后再按字符数截断;也可以使用 token 分割器,直接控制每块的 token 数量。

4. 元数据标注

元数据既用于过滤,也用于结果追溯。常见字段包括:

  • 文档来源、文件名、页码和章节。
  • 发布时间、生效时间和版本号。
  • 部门、产品、地区和权限等级。
  • 内容类型,如正文、表格、FAQ 或代码。
  • 关键词、实体和业务标签。

元数据可以通过规则提取,也可以让 LLM 提取结构化字段。例如用户查询“2025 年的政策”时,系统可以先通过年份和状态过滤,再进行向量召回,减少旧文档干扰。

5. 构建索引

预处理完成后再构建索引。为了支持混合检索,通常会同时维护:

  • 向量索引:处理语义相似查询,可使用 Milvus 等向量数据库。
  • 关键词倒排索引:处理产品名、编号、金额和精确术语,可使用 Elasticsearch 的 BM25。
  • 元数据索引:支持时间、部门、权限和文档类型过滤。

这一步要保证每个索引都能追溯到同一份原文和同一个块 ID,避免召回结果无法定位来源。

6. 建立预处理验收

不能等用户问错了才发现数据有问题。入库前至少要检查:

  • 解析成功率和空文档率。
  • 标题、段落和表格结构保留率。
  • 乱码率、重复率和敏感信息漏检率。
  • 清洗前后关键字段的一致性。
  • 抽样查询的召回率,以及答案能否定位到正确页码和原文。

完整流程可以概括为:

graph TD
    A[原始数据] --> B{数据提取}
    B -->|PDF HTML 图像| C[OCR 或文档解析]
    B -->|文本 表格 API| D[结构化抽取]
    C --> E[格式标准化]
    D --> E
    E --> F[清洗 脱敏 去重]
    F --> G[结构恢复与质量校验]
    G --> H[语义分块]
    H --> I[元数据标注]
    I --> J[向量索引 关键词索引 元数据索引]
    J --> K[数据库]

扩展知识

全文都在,不代表现行版本会被召回

向量检索解决的是语义相似,不会天然理解“已废止”“部分替代”或“从某天起生效”。如果新旧制度正文相似,旧版甚至可能因为措辞更接近用户问题而排在前面。

版本治理至少要覆盖四层:文件层记录制度编号与版本关系,片段层继承生效时间和状态,检索层先做时间与权限过滤,答案层必须能返回块 ID 和原文位置。历史文件可以保留,但不应该和现行文件拥有相同的默认召回资格。

客服引用旧制度时,先查哪一层

更稳妥的排查顺序是:

  1. 从答案引用定位到被召回的块,确认系统引用的是旧文档,还是新版里的旧段落。
  2. 查看该块的版本、生效时间、失效状态和替代文档 ID 是否完整。
  3. 检查召回前的元数据过滤是否真正生效,而不是只把过滤条件写进 Prompt。
  4. 回到清洗结果,确认重复页眉、旧附件和合并文档没有生成额外的高相似块。
  5. 修复后用同一批“当前办理”和“历史追溯”问题回归,避免只修当前问法却破坏历史查询。

这个顺序能避免团队一看到回答错误,就同时调整分块长度、召回数量、Embedding 和 Prompt,最后仍不知道是哪一层放行了旧版本。

预处理要像代码一样可回滚

制度更新时直接覆盖旧文件,短期看最省事,真正出问题后却无法解释历史工单。更稳妥的方式是保留原始文件哈希、解析器和清洗规则版本、清洗前后差异、入库时间、块 ID 与索引版本。

新制度上线时,先计算文档差异,只重建受影响的片段;旧片段转为历史状态,保留原文和处理日志。这样既能让客服默认命中现行口径,也能在审计或复盘时还原当时依据。

别只看召回率,还要测版本正确率

一套测试集里如果只有“有没有召回相关内容”,旧版和新版同时命中也可能被算作成功。制度型知识库还需要单独记录:当前版本命中率、失效版本误召回率、生效时间过滤准确率、答案引用版本正确率。

测试问题也要覆盖不同时间语义,例如当前怎么办、某个日期当时怎么规定、旧规则何时失效。只有把这些查询分开,才能确认系统既没有拿历史口径回答当前问题,也没有为了追求“永远最新”而失去历史追溯能力。

这项能力怎么写进简历

简历里只写“负责 RAG 知识库数据清洗”很难证明深度。更有效的写法要包含数据规模、问题、措施和结果,例如:

负责多源制度文档入库链路,统一处理 PDF、网页和表格数据;通过重复内容识别、版本元数据抽取、片段级状态管理和可回滚清洗日志,降低失效版本误召回,并用真实客服查询集持续评估召回与引用版本正确率。

面试官随后通常会追问:版本号怎么提取、部分更新怎么重建索引、历史文档如何保留、过滤条件如何验收。能把这些问题连成一条生产链路,才说明你做的不只是调用一个文档加载器。

面试官追问

追问:分块的时候重叠太多会有什么问题?不重叠又会怎样?

详细解答可以看:在 RAG 中,常见的分块策略有哪些?分别有什么区别?

回答:重叠太多会导致相同的内容在多个块里重复出现,检索的时候召回一堆内容差不多的块,浪费 token 额度,还可能让模型产生"这段话好像说了好几遍"的困惑。不重叠的话,跨段落的语义就容易被切断,比如"它的优点是..."这句话里的"它"指的是上一段的某个概念,切断了就成了孤儿句。一般 10-15% 的重叠率是个平衡点。

追问:如果原始文档里有大量表格数据,怎么处理比较好?

回答:表格不能直接切成文本块,会丢失行列关系。常用的做法是把表格转成自然语言描述,比如"产品A的价格是100元,产品B的价格是200元"这种。或者保留表格的结构化形式,检索的时候单独处理,用 SQL 式的查询而不是向量检索。如果表格特别重要,可以考虑专门训练一个 Table QA 模型来处理。

追问:清洗过程中误删了重要信息怎么办?有没有什么保底措施?

回答:清洗规则上线前一定要在小样本上先跑一遍,人工抽检看看有没有误伤。保底措施是保留原始数据不删,清洗后的数据单独存一份,出问题了能回溯。还可以加一个"清洗日志",记录每条数据被哪条规则处理过,方便排查。生产环境里我们还会定期抽样对比清洗前后的数据,监控误删率。

那条被客服引用的旧制度,不该被简单删除,也不能继续和现行制度站在同一条召回起跑线上。

数据清洗真正要保住的,不只是文字本身,还有结构、版本、状态和追溯关系。面试里能说清每一步会丢什么、怎么验收、出了错如何沿着块 ID 找回原文,才算把这道题答到了生产环境。