Skip to content
LlamaIndex 文档版本 跟随上游 源快照 d5115d1e 更新于 2026-10-15

当你的 RAG 流水线表现不及预期时,很难定位根本原因。这份清单覆盖了最常见的失败模式、它们的表现,以及能让一切回到正轨的最小修复。

前九节聚焦单次查询的行为(检索、分块、嵌入、查询表述与合成)。后面几节则关注系统层面的问题——它们往往只在更大规模或运行更久的部署中才显现。

1. 检索幻觉

**发生了什么:**检索器返回的块看起来表面上相关,其实并不包含答案。于是 LLM 从不相关的上下文里“幻觉”出一段听起来头头是道的回答。

表现:

  • 回答听起来很有把握,但事实上是错的
  • 检索到的块与查询共享关键词,但讲的是另一个话题
  • 不相关的段落却有很高的相似度分数

修复:

  • 加一个重排器(例如 CohereRerank、SentenceTransformerRerank),在初次检索之后过滤掉误命中
  • 调大 similarity_top_k,让重排器去做精简,而不是只靠向量库给出很小的 top-k
  • 使用混合检索(向量 + 关键词检索结合),减少语义上的误匹配
  • 通过 node_postprocessors 设置相关性阈值,丢弃低置信度的块

2. 选错块(分块不佳)

**发生了什么:**文档的切分方式把关键上下文拆散到多个块里,导致没有任何单个块含有足以回答查询的信息。

表现:

  • 回答只有一半或不完整
  • 正确信息确实在你的语料里,但检索到的块没有完整包含它
  • 当你手工提供正确文本时,回答质量大幅提升

修复:

  • 试验块大小与重叠(例如用 1024+ token 的更大块,配 10–20% 的重叠)
  • 用 SentenceSplitter 而不是朴素的定长切分,以保留句子边界
  • 试试用 HierarchicalNodeParser 做层级分块,同时兼顾细粒度与宏观上下文
  • 考虑 SentenceWindowNodeParser:检索一个句子,但用其周围上下文来合成

3. 索引碎片化

**发生了什么:**索引中混有重复、过期或相互矛盾的文档版本,导致检索结果不一致甚至自相矛盾。

表现:

  • 同一个问题在不同次运行中给出不同答案
  • 检索到的块之间信息互相矛盾
  • 源文档已经更新,过期数据却仍然出现

修复:

  • 用 doc_id 跟踪实现文档管理策略,并用 index.refresh_ref_docs() 更新发生变化的文档
  • 用带 docstore 的 IngestionPipeline,在建索引前先对文档去重
  • 定期从源重建索引,而不是一味追加
  • 写入元数据(时间戳、版本号),并在需要时按新旧程度过滤

4. 配置漂移(嵌入不匹配)

**发生了什么:**查询时使用的嵌入模型与建索引时用的不是同一个,或者块大小这类设置在索引与查询之间发生了变化。

表现:

  • 一次代码改动或依赖升级后,检索质量突然下滑
  • 所有查询的相似度分数都异常偏低
  • 原本正常的查询现在返回不相关的结果

修复:

  • 把嵌入模型名称与索引一起保存,并记录到元数据或配置里
  • 固定嵌入模型的版本(例如 text-embedding-3-small,或某个 sentence-transformers 模型的具体修订版)
  • 更换嵌入模型后必须重建整个索引,因为不同嵌入无法混用
  • 在索引与查询两条代码路径中都统一使用 Settings.embed_model

5. 嵌入模型与领域不匹配

**发生了什么:**嵌入模型不理解你所在领域的术语,导致领域相关查询的语义相似度很差。

表现:

  • 通用知识类问题效果好,领域特定问题效果差
  • 你领域内的同义词或行话匹配不上
  • 在你的数据集上,关键词检索反而强于向量检索

修复:

  • 尝试领域适配的嵌入模型(例如面向法律、医疗或代码微调过的模型)
  • 把向量检索与关键词检索结合(混合模式),以捕获术语的精确匹配
  • 从你的语料生成合成问答对,在上线前先评估嵌入的召回率

6. 上下文窗口溢出

**发生了什么:**太多检索到的块被塞进 LLM 的提示词,超出上下文窗口,或者让信号被噪声稀释。

表现:

  • 回答被截断
  • 出现关于 token 上限的 API 报错
  • 回答忽略了确实相关的检索内容(尤其是位于上下文开头或中间的内容)
  • 随着 similarity_top_k 增大,质量反而下降

修复:

  • 用 TreeSummarize 或 Refine 这类响应合成器替代 CompactAndRefine,以处理大量上下文
  • 减小 similarity_top_k,依靠重排来只呈现最相关的块
  • 明确设置 max_tokens 上限,并用 callback_manager 监控 token 用量
  • 考虑用摘要索引或递归检索,在合成之前先压缩上下文

7. 缺少元数据过滤

**发生了什么:**本应限定在某个子集(例如某个日期区间、某个部门或某类文档)内检索时,检索器却扫了全部文档。

表现:

  • 回答取自错误的文档(例如用户问今年,却引了去年的报告)
  • 文档类别之间相互串味
  • 用户反馈“系统知道的太多了”或返回无关内容

修复:

  • 在摄取时写入结构化元数据(日期、来源、类别、作者)
  • 查询时用 MetadataFilters 限定检索范围
  • 实现自动检索(auto-retrieval),由 LLM 从用户查询中解析出过滤参数
  • 对逻辑上相互独立的文档集合使用各自的索引或命名空间

8. 查询理解不佳

**发生了什么:**用户的查询含糊、过短,或者说法与文档中信息的表述方式不一致。

表现:

  • 只是换个说法,结果就大不相同
  • 一两个词的短查询效果很差
  • 用户必须“用对词”才能得到好答案

修复:

  • 加一个查询变换步骤,用 HyDEQueryTransform 先生成一个假设性回答,再拿它去检索
  • 用 SubQuestionQueryEngine 把复杂查询拆成更简单的子查询
  • 用 LLM 做查询改写,把查询扩展或换个说法
  • 加一个含示例查询的 few-shot 提示词,给用户引导

9. LLM 合成失败

**发生了什么:**检索器取到了正确的块,但 LLM 没能据此合成出好的回答。

表现:

  • 检索到的块是对的(已人工核实),但回答仍然是错的
  • LLM 无视提供的上下文,改用它的训练数据来回答
  • 明明有具体的上下文可用,回答却过于泛泛

修复:

  • 合成时换用更强的 LLM(例如用 GPT-4o 而不是 GPT-4o-mini),或略微调整 temperature
  • 定制 QA 提示词模板,明确指示 LLM 只使用提供的上下文
  • 用 Refine 响应模式逐块顺序处理,而不是一次性全部塞入
  • 加一个 system_prompt,反复强调 LLM 应当只基于上下文作答

10. 嵌入度量不匹配(余弦分数 ≠ 真实语义)

**发生了什么:**用于相似度计算的距离度量或归一化方式,与你数据中语义的分布方式并不吻合。过长或过于泛泛的块会主导相似度分数,而真正相关的片段却排在后面。

表现:

  • 排名第一的结果明显是错的,但相关文档确实出现在 top-k 里,只是位置靠后
  • 相关块与不相关块的相似度分数挤在一处,几乎分不开
  • 给文档加一段通用套话,就会出乎意料地改变检索行为
  • 人工检查发现,检索质量对很小的预处理改动很敏感

修复:

  • 检查完整的 top-k 列表,看看相关块是否出现了、只是排名太低
  • 对过长的块做归一化或裁剪,避免少数大块主导相似度
  • 考虑加一个重排环节,使用与基础向量库不同的打分函数
  • 上线前用带标注的查询评估检索(在小测试集上看精确率/召回率),并据结果调整 similarity_top_k 与阈值

11. 会话与缓存的记忆断裂

**发生了什么:**用户期望系统能记住之前的交互或配置,但底层的索引、向量库或缓存其实无状态,或者 key 用得不对。数据明明都在,检索表现却时好时坏。

表现:

  • 同一个问题在不同日期提问,答案来自不同的文档子集
  • 重新部署或清空缓存之后,原本稳定的查询开始漂移
  • 手工用同一个查询打向量库,有时返回空结果或明显更少的结果

修复:

  • 明确定义会话与用户 key 的策略,确保它们在应用、检索器与存储之间一致传递
  • 把长期知识与短期暂存空间分开,这样缓存淘汰不会带走关键数据
  • 对有问题的请求,记录索引版本、缓存 key 与检索参数,并在不同会话之间比对
  • 加一个回归测试:每次部署后重放一小段对话或查询序列,以验证稳定性

12. 可观测性缺口(“黑盒调试”)

**发生了什么:**你知道答案是错的,却看不到检索器或 LLM 到底做了什么。缺少基本的轨迹记录,就无法判断问题出在检索、合成还是部署。

表现:

  • Bug 报告只是“它有时回答很奇怪”,却没有可复现的轨迹
  • 难以查看某个糟糕答案究竟检索到了哪些节点
  • token 数、提示词与索引元数据都没有记录,导致线上那次运行无法还原

修复:

  • 为检索、查询变换、提示词与响应打开追踪与日志(通过 LlamaIndex 回调或你自己的日志体系)
  • 对每一个失败的答案,至少记录:用户查询、检索到的节点、相似度分数、索引或快照标识,以及最终的 LLM 提示词
  • 在应用里加一个“调试模式”,把检索结果与决策打印或存下来供人工检查
  • 在改动基础设施之前,先尝试仅凭日志和轨迹复现失败;若做不到,就先补可观测性

13. 索引生命周期与部署顺序

**发生了什么:**流水线在本地测试正常,线上却行为不定,因为索引是空的、只建了一半,或与正在运行的配置不匹配。服务可能以错误的顺序启动,或指向了错误的存储。

表现:

  • 部署刚结束,部分查询就返回明显不完整或空的答案
  • 日志显示向量库中的节点数远少于预期
  • 改动环境变量或密钥会悄悄切换查询时使用的索引或嵌入设置
  • 回滚或重新部署会让答案再次变化,而代码没有任何改动

修复:

  • 把索引当作有版本的产物,在摄取与提供服务两条路径中都记录索引版本或快照 id
  • 加一个健康检查:部署后运行一条已知正确的查询,如果索引为空、规模低于下限,或用了错误的嵌入配置,就让检查失败
  • 确保摄取或刷新任务完成之后,才把生产流量路由到新索引
  • 避免手工的一次性摄取步骤;把它们写进脚本或流水线,这样就不会被意外跳过

快速诊断流程图

用它来缩小问题范围:

  1. **先检查检索:**打印检索到的节点,人工确认其中是否含有答案。

    • 如果检索到的节点是错的 → 重点看上面的第 1–5、7–8 项。
    • 如果检索到的节点是对的 → 重点看上面的第 6、9 项。
  2. **检查一条已知正确的查询:**试着问一个你确切知道答案在哪份文档里的问题。

    • 如果它失败 → 多半是索引或嵌入问题(第 3–5、10、13 项)。
    • 如果它成功 → 问题是查询特有的(第 1、7–8、11 项)。
  3. **检查 token 数:**记录发给 LLM 的总 token 数。

    • 如果接近上限 → 上下文窗口溢出(第 6 项)。
    • 如果远未触及上限 → 合成或检索质量问题(第 1–5、8–10、12 项)。
  4. 如果问题只在生产环境或部署之后出现:

    • 重点看系统层面的问题(第 11–13 项),并核对索引版本、缓存与轨迹。

延伸阅读

文中提到的关键 LlamaIndex 类

BytesRouter 是社区翻译站点,所有译文仅供参考,一切以官方英文原文为准。