
面试官:"你说你们做了个企业知识库问答,RAG 检索效果怎么样?"候选人:"还行,检索得挺准的。"面试官:"那我问你,你们知识库里的文档,是怎么进到向量库里的?"候选人:"就……把文档读出来,切开,调 embedding,存进去。"面试官:"读出来——PDF 读过吗?扫描件读过吗?Word 里的表格呢?切的时候按什么切?切完的块带来源和页码吗?"候选人:"这个……我接手的时候管道已经搭好了。"面试官心里就有数了。RAG 这类项目,模型是现成的,框架是现成的,真正决定效果上限的,是那条没人愿意细看的入库管道。检索不准,八成不是模型不行、不是 top_k 没调好,而是你把一坨脏数据喂进了向量库。这篇聊的就是这条脏活管道:解析、清洗、切分、挂元数据。听起来不高级,但面试官一追问,就知道你到底是"跑过 demo"还是"上过生产"。一句话看清管道全貌一份文档从硬盘到能被检索,要过七道工序:采集 → 解析 → 清洗 → 切分 → 挂元数据 → 向量化 → 入库。这条链是"串联"的:前面错一步,后面全白干。解析出了乱码,清洗救不回来;切分切碎了,向量化再准也白搭。所以顺序不能乱,越靠前越要抠。第一关:解析——PDF 是重灾区坑一:扫描件根本没有文字层很多人的第一反应是"PDF 我随便找个库读一下就行"。问题是,PDF 分两种:一种是电子版 PDF,文字是真实的字符,直接抽取就能拿到文本。另一种是扫描件 PDF,本质是一张张图片,里面全是像素,没有任何文字。你用普通 PDF 文字抽取库去读扫描件,得到的要么是空白,要么是一堆乱七八糟的符号——因为库翻遍了页面,也没找到真正的"字"。怎么判断是哪种?简单粗暴的办法:看抽出来的文本长度是不是明显偏短、或者几乎为空。是的话,这份 PDF 就是图片,得走 OCR。OCR 就是"图片转文字"。Java 里常见的做法:- 本地跑:调用 Tesseract(配合 Tess4J),或者用 PaddleOCR 这类中文识别效果更好的方案- 云服务:调云厂商的 OCR 接口OCR 的代价是慢、吃计算资源,而且识别有错字。所以 OCR 结果最好做一遍后处理:修正常见错字、把识别置信度低的段落标出来。无脑把 OCR 结果塞进去,等于把噪声直接喂给检索。坑二:版式乱,文字顺序会串电子版 PDF 也不省心。PDF 内部记录的是"这个字画在哪个坐标",不是"这句话是什么段落"。所以抽取库是按坐标顺序把字捞出来的。后果就是:双栏排版的 PDF,左右两栏会被交错拼接,读起来像两个人同时说话;表格里的文字会按坐标顺序乱入正文;页脚、水印、侧边页码也会混进来。这个问题没有银弹。生产上常见的补救:按坐标做分栏识别(判断这个字属于左栏还是右栏,再分别拼接)、识别表格区域单独处理、识别并剔除页眉页脚区域。做得糙一点的就是后处理时把重复出现的行删掉——下一节讲。用 Spring AI 读一份 PDFSpring AI 内置了 PDF 读取器,底层用的就是 Apache PDFBox:import org.springframework.ai.reader.pdf.PagePdfDocumentReader; import org.springframework.ai.reader.pdf.config.PdfDocumentReaderConfig; import org.springframework.core.io.FileSystemResource; // 一份 PDF 按页读取,每页变成一个 Document FileSystemResource resource = new FileSystemResource("/data/docs/handbook.pdf"); PagePdfDocumentReader reader = new PagePdfDocumentReader( resource, PdfDocumentReaderConfig.builder() .withPagesPerDocument(1) // 一页一个 Document,方便后续带页码 .build(