从零学RAG0x06:传统RAG的痛点

0 阅读7分钟

前言

前面系统学习了 RAG 的基础用法。那么这些用法从 Demo 迁移到真实、复杂、高要求的企业级应用会有问题吗?

传统RAG的流程可以简化为一个线性管道:解析文档 -> 切块向量化 -> 存储 -> 用户提问 -> 向量相似度检索 -> 将Top-K片段拼接入Prompt -> LLM生成答案。这个流程的成败完全押注在“向量检索”这一步的精准度上。

所以,上述问题就转换成了“在RAG向量检索的过程中可能有哪些因素导致检索结果的准确问题?”。

他山之石

澳大利亚一位人工智能工程师在其论文 Seven Failure Points When Engineering a Retrieval Augmented Generation System 中指出:RAG技术在商业化中的7个致命问题(痛点)。

image.png

1. 缺失内容(Missing Content)

问题

当用户提出的问题无法从当前文档中找到答案时,系统可能给出错误回答。

  • 原文本身缺失:原本的文本中可能就没有问题的答案。
  • 文档加载准确性和效率: 比如pdf文件的加载,如何提取其中的有用文字信息和图片信息等。提取技术的准确性直接关系到文档质量。
  • 文档切分的粒度: 文本切分的大小和位置会影响后面检索出来的上下文完整性和与大模型交互的token数量,怎么控制好文档切分的度,是个难题。

优化

  • 增加相应知识库:将相应的知识文本加入到向量知识库中。
  • 数据清洗与增强:Garbage In, Garbage Out。任何RAG工作流程想要获得优良表现,都必须先清洁数据。
  • 更好的Prompt设计:通过Prompts,比如让大模型在找不到答案的情况下,输出“根据当前知识库,无法回答该问题”等提示。
  • 优化文档读取器:一般知识库中的文档格式都不尽相同,针对每一类文档,涉及一个专门的读取器。
  • 文档切分的粒度
    • 内容重叠分块:为了保持文本块之间语义上下文的连贯性,在分块时,保持文本块之间有一定的内容重叠。
    • 基于结构的分块:基于结构的分块方法利用文档的固有结构,如HTML或Markdown中的标题和段落,以保持内容的逻辑性和完整性。
    • 基于递归的分块:重复的利用分块规则不断细分文本块。比如先通过段落换行符(\n\n)进行分割。然后,检查这些块的大小。
      • 如果大小不超过一定阈值,则该块被保留。对于大小超过标准的块,使用单换行符(\n)再次分割。以此类推,不断根据块大小更新更小的分块规则(如空格,句号)。
    • 分块大小的选择:
      • 不同的嵌入模型有其最佳输入大小。比如Openai的text-embedding-ada-002的模型在 256 或 512 大小的块上效果更好。
      • 文档的类型和用户查询的长度及复杂性也是决定分块大小的重要因素。

2. 检索排序(Missed the Top Ranked Documents)

问题

传统RAG的检索核心是 “语义向量相似度排序”。但是,语义相似 ≠ 答案相关:这向量模型更擅长捕捉语义和主题的相似性,而非答案的针对性

  • 🌰:query:当下企业如何应对如火如荼的AI浪潮?一个详细解释“AI浪潮”的文档块,其向量可能与查询高度相似,排名第一。而真正关键的、描述企业级应对策略的段落,可能因为语义更具体,向量表达反而“距离”查询较远,被排到了第五名之后。

优化

  • 混合检索:关键词检索(如BM25) ​ 能很好地解决“词汇鸿沟”和“精确术语匹配”问题。将向量和关键词的结果融合(Hybrid Search),能大幅提升召回相关片段的概率。
  • 增加召回数量:增加召回的 topK 数量,也就是说,例如原来召回前3个知识块,修改为召回前5个知识块。不过此种方法,因为知识块多了,不光会增加token消耗,也会增加大模型回答问题的干扰。
  • 重排(Reranking):将初步检索到的Top-k候选片段,交给一个更强大但更耗资源的交叉编码器模型进行精细化的“相关性打分”。这个模型能同时阅读查询和每一个候选片段,进行深度的语义交互判断,从而把真正包含答案的片段重新排到最前面。

image.png

3. 上下文不充分(Not in Context - Consolidation strategy)

问题

检索出的文档中包含了答案,但在合并/压缩上下文(最大Token数超过了LLM限制)时被剔除。

优化

  • 动态上下文选择(MMR):不简单截断,而是使用Max Marginal Relevance等方法,在保证相关性的同时兼顾信息多样性。
  • 总结性引用:对长文档,先让LLM对每个块生成简短摘要,再将摘要而非全文送入最终上下文。

4. 提取问题(Not Extracted)

问题

上下文中包含答案,但语言模型未能提取出正确的答案。这可能是由于上下文中的噪声或矛盾信息导致。

优化

  • 提示工程强化:在系统提示中明确指令,如“严格基于以下上下文回答,如果上下文没有明确提及,则说不知道”。
  • 结构化上下文:为检索到的片段添加元数据(如来源、页码)。论文发现这能显著提升提取准确性。
  • 后处理与验证:对生成的答案,可反向在上下文中进行事实核验(Citation Verification)。

5. 格式问题(Wrong Format)

问题

问题要求提取特定格式的信息,但语言模型未能遵循格式要求。例如需要Json,给了字符串。需要改进答案格式提取策略。

优化

  • Prompt调优优化Prompt逐渐让大模型返回正确的格式。
  • 进行结果格式验证,例如使用LangChain中的PydanticOutputParser类来校验输出格式。
  • Auto-Fixing自修复:对不符合要求的格式进行自动修复

6. 准确性问题(Incorrect Specificity)

问题

答案本身正确,但详细程度不符合用户需求(太泛或太细)。

  • 🌰:学生可能需要概念解释,但系统只给出了名词定义。

优化

  • 查询理解与重写:引入一个轻量级LLM对原始查询进行意图分析或重写,例如将“解释一下”重写为“用简单的例子解释一下X的概念及其应用”。
  • 用户画像/会话记忆:在对话中记住用户身份(如“新生”vs“助教”)和过往问题,动态调整回答粒度。
  • 提供可控选项:前端提供“简洁/详细”等交互选项,并将其作为条件输入提示词。

7. 不完整(Incomplete)

问题

答案不完整,未能涵盖所有相关信息。这可能是由于语言模型未能从上下文中完全提取信息。

  • 🌰:问“A、B、C三篇文档的共同点”,LLM可能只列出了A和B的。

优化

  • 指令强化:在提示中明确要求“请列出所有要点”或“务必完整”。
  • 迭代追问:设计自问(Self-Ask) ​ 流程,让LLM先列出可能答案的子问题,再逐个检索回答,最后合成。
  • 采用Map-Reduce策略:将复杂查询对每个相关文档块分别提问(Map),再汇总所有回答(Reduce)。