RAG 实战踩坑录:知识库检索不准怎么办?常见问题与解决方

Frieren 发布于 22 天前 10 次阅读


大家都说大模型落地的最后一步是 RAG,但真正动手敲代码、做落地的时候才发现,RAG 的水有多深。

文本解析乱码、分块策略翻车、向量检索被大文件“绑架”…… 任何一个环节的数据处理没做好,都会直接导致 LLM 最后生成的回答像是在“胡言乱语”。在搭建 RAG 时,如果不注意这些工程细节,很可能会导致整个问答功能失效。

本文梳理了我在开发 RAG 过程中遇到的几个最典型的痛点(比如长短文件混合检索问题等),并附上了排查过程和具体的解决思路。如果你也正在被知识库检索的准确率折磨,希望这篇排坑记录能给你提供一些灵感。

1.大文文件分块超多,小文件分块少导致的小文件匹配不到

现象描述:小文件在检索中“隐身”了

在构建 RAG 系统的初期,我把所有知识库文件(包括长达几百页的 PDF 操作手册,以及只有几句话的 TXT 配置说明)用统一的 Chunk Size 进行了切分并存入向量数据库。

遇到的坑: 当用户提问时,如果问题同时关联到大文件和小文件,召回的 Top-K(比如 Top-5)结果几乎全部被大文件的内容切片占满。那份至关重要的小文件就像隐身了一样,永远无法进入 LLM 的上下文,导致大模型最终的回答完全偏离预期。

原因分析:向量空间的“马太效应”

排查后发现,这是典型的分块数量极度不平衡导致的。

  • 几百页的大文件切出了成千上万个 Chunk,在向量空间中占据了极大的分布范围。
  • 小文件可能只有 1-2 个 Chunk。
  • 当进行向量相似度计算时,由于大文件 Chunk 基数太大,从概率上讲,它有更高的几率挤进前 5 名。这就是向量检索里的“人多势众”,导致小文件精准的内容直接被淹没。

解决方案:限制单文件召回上限(分组召回 + 二次重排)

针对这个问题,我当时在项目中采用的最终落地策略是:打破全局 Top-K 限制,改为“每个文件先取 Top-N,再进行全局二次排序”。

具体思路如下:

不再直接从向量库中取全局分数最高的 5 个 Chunk,而是先扩大初筛范围,然后在内存中按文件(file_id)进行分组。强制规定每个文件最多只能提供 N 个最高分的 Chunk 进入候选池,最后在候选池中选出最终的分数最高的几项给到 LLM。

这样一来,即使大文件有 100 个高分 Chunk,它最多也只能占据最终名额中的 N 个,强行给小文件留出了生存空间。

核心代码逻辑(伪代码示意):

# 1. 扩大初始召回范围,比如先召回 Top 50
initial_chunks = vector_db.similarity_search(query, k=50)

# 2. 按文件 ID 进行分组
chunks_by_file = {}
for chunk in initial_chunks:
    file_id = chunk.metadata.get("file_id")
    if file_id not in chunks_by_file:
         chunks_by_file[file_id] = []
    chunks_by_file[file_id].append(chunk)

# 3. 限制单文件上限:每个文件最多只取前 2 个最高分的 Chunk
candidate_pool = []
for file_id, chunks in chunks_by_file.items():
    # 假设 chunks 已经按分数由高到低排序
    top_n_for_file = chunks[:2] 
    candidate_pool.extend(top_n_for_file)

# 4. 二次全局排序:在合并后的候选池中,选出最终分最高的 Top 5
# sort by similarity score descending
final_top_k = sorted(candidate_pool, key=lambda x: x.score, reverse=True)[:5] 

return final_top_k

补充说明(其他可选优化方向)

当然,除了上述的代码层控制,业界还有一些其他思路可以作为补充方案:

  • 混合检索 (Hybrid Search): 引入基于关键词的 BM25 检索,小文件核心关键词密度高,在 BM25 中得分往往极高,再配合向量检索做 RRF 重排序。
  • 父子文档检索 (Parent-Child Retriever): 为大文件生成段落级的 Summary 作为检索节点(子);命中后,再召回其对应的完整上下文(父),让长短文件的检索颗粒度保持平等。

2. 表格排版被“撕裂”与图片型PDF解析为空的问题

现象描述:

解决了检索数量分配后,紧接着在文档解析(Parsing)环节又栽了跟头。

  1. 表格失效:当用户提问“2023年Q3的营收是多少?”时,大模型开始胡编乱造。排查后发现,原始 PDF 里明明有个清晰的财务表格,但在分块切分时被“暴力撕裂”了——表头在 Chunk A,数据被切到了 Chunk B,导致大模型看到的上下文支离破碎。
  2. 扫描件“隐身”:有些老旧的产品手册明明传到了知识库,但死活搜不出内容。打开后台一看,解析出来的文本全是空的或乱码,因为这些 PDF 是纯图片构成的扫描件。

原因分析:

传统的文本处理库(如 PyPDF2)和切分器(如 LangChain 的 RecursiveCharacterTextSplitter)存在严重的“视觉盲区”:

  • 无版面理解能力:它们不理解文档的视觉排版(Layout),遇到双栏排版、复杂表格时,只会机械地按字符数一刀切断,彻底破坏了表格的二维结构。
  • 无视觉提取能力:遇到由图片组成的扫描版 PDF 时,由于底层没有文本流,传统工具无法提取内容,导致知识在入库的第一步就彻底丢失了。

解决方案:

单纯靠基础文本提取走不通,必须在数据预处理(Pipeline)阶段引入OCR 与版面分析能力来进行兜底和结构化。

  • 对策一:引入 OCR 引擎兜底图片/扫描件在处理 PDF 流时增加判断逻辑。如果单页提取的文本量极少(比如小于 50 个字符),就判定为图片型 PDF,自动触发 OCR 引擎(如开源的 PaddleOCR 或云厂商的 OCR API)进行兜底识别,硬提取出文字。
  • 对策二:使用版面分析模型 (Layout Analysis) 提取表格弃用暴力的字符切分,改用专门的文档解析模型(如 UnstructuredMarker 等工具)。这些工具能看懂版面,将 PDF 转化为保留了表格结构的 Markdown 格式。随后,我们按 Markdown 的标题层级(#, ##)进行 Chunk 切分,保证表格作为一个完整的整体保留在同一个切片中。

核心预处理逻辑(伪代码示意):

def process_pdf_page(page_content, page_image):
    # 1. 尝试常规文本提取
    text = extract_text_from_pdf(page_content)
    
    # 2. 如果文本极少,说明大概率是图片型 PDF,触发 OCR
    if len(text.strip()) < 50:
        text = run_paddle_ocr(page_image)
        
    # 3. 如果页面包含复杂排版/表格,调用版面分析转为 Markdown
    if detect_table_or_complex_layout(page_image):
        markdown_text = layout_analysis_to_markdown(page_image)
        return markdown_text # 后续按 Markdown Header 切分
        
    return text

3. 口语化提问与专业文档存在“语义鸿沟”导致匹配不到的问题

现象描述:

文档成功入库后,我又发现了一个诡异的现象:知识库里明明有标准答案,但系统就是回复“抱歉,知识库中没有相关信息”。

比如,知识库里有一份《企业账号凭据重置安全操作规范》,而真实用户的提问往往是极其口语化的:“喂,我密码忘了怎么弄?”。这时候向量数据库给出的匹配分数极低,导致正确文档根本没进 Top-K。

原因分析:

这是由 Embedding 模型计算高维空间相似度的特性决定的。

用户的提问往往是高度口语化、简短的;而企业的知识库文档则是极其专业、书面化的。尽管在人类认知中,“密码忘了”和“账号凭据重置”是一回事,但在向量的高维空间里,这两个词的距离可能非常遥远。这种“语言隔离”直接导致了精准内容的召回失败。

解决方案:

针对这个问题,核心思路是:不要让用户的原始问题直接去碰向量库,而是先用 LLM 做一次意图对齐。

  • 对策一:查询重写 (Query Rewrite)在检索发生前,增加一个低延迟的小参数大模型节点。它的任务不是回答问题,而是把口语化的问题扩写、翻译为包含专业术语的“检索词”。(比如把“密码忘了”重写为:“忘记密码、账号凭据重置、密码找回流程”)。拿着重写后的检索词去查向量库,命中率会有质的飞跃。
  • 对策二:假设性文档嵌入 (HyDE)一种更进阶的玩法。直接让大模型根据用户问题“瞎编”一个答案(不连接知识库的幻觉回答),然后拿这个“假答案”的文本去向量库里搜。因为大模型生成的假答案,其行文风格和专业词汇往往和知识库正式文档极为接近,能极大抹平语义鸿沟。

查询重写 Prompt 示例:

你是一个专业的知识库检索意图分析专家。
用户输入了一个口语化的问题,请将其重写为更正式、包含更多专业同义词的搜索查询,以便于在企业知识库中进行准确的向量检索。
仅输出重写后的查询字符串,不要有任何多余的解释。

用户原问题:{user_query}
重写后的查询:

此作者没有提供个人介绍。
最后更新于 2026-04-28