RAG 常见问题与调试¶
RAG 系统的失败模式是多样的——回答不好可能因为任何一个环节存在缺陷。这章帮你系统性定位问题。
这章解决什么问题¶
你的 RAG 系统上线了。用户问了一个看起来很简单的问题,但 LLM 给出了一个离谱的答案。
是检索没找到正确的资料?是 LLM 忽略了资料?是切分切断了关键信息?还是 Embedding 模型没理解用户的表达?
RAG 的调试比单环节系统更复杂,因为每一个环节都可能是瓶颈。这章提供一套系统性的故障排查思路。
核心排查方法论¶
RAG 调试的十一问¶
从回答不好到找到根因的排查路径
-
资料库里有正确答案吗?
- 先去知识库里手动搜一下。如果库里就没有答案,RAG 不可能给出正确答案
- 如果资料不全 → 补充知识库
-
资料被正确切分了吗?
- 找到包含答案的原始片段,看它是否被切散到了多个 chunk 中
- 是否在 chunk 边界处被截断?
- 如果切分有问题 → 调整切分策略(用更大 chunk size 或语义切分)
-
切分后的 chunk 语义完整吗?
- 逐个检查被你命中的 chunk,上下文是否自包含?
- chunk 包含的信息是否完整到能独立回答问题?
- 不完整?→ 增加 chunk size 或 chunk_overlap
-
用户提问和答案之间的语义鸿沟大吗?
- 用户说「怎么取消」,答案叫「退款政策」——虽然相关但不完全匹配
- 语义鸿沟大?→ 在索引中加入同义词或别名,或者直接用 LLM 做 Query 重写
-
Embedding 模型能捕捉这种语义关系吗?
- 手动算一下用户问题和 chunk 之间的相似度
- 相似度明显偏低?→ 换更合适的 Embedding 模型(如针对中文场景优化)
-
Top-K 够用吗?
- K=3 只返回了 3 个 chunk,正确答案排在第四
- K 太小?→ 增加 K 值,然后配合重排提高精确率
-
重排模型有没有把正确答案排下去?
- 检查正确答案在重排前后的排名变化
- 重排分数偏低?→ 检查重排模型是否适配当前语言/领域,或者重排的候选集是否足够大
-
LLM 用上了资料吗?
- 把 LLM 的 Prompt 打印出来,看资料有没有正确传递到 LLM 的输入中
- 如果资料传过去了但 LLM 没用 → 强化 Prompt 约束
- 如果资料根本没传 → 检查你的代码
-
Prompt 约束足够强吗?
- Prompt 里说了「只根据资料回答」吗?
- 说了「如果找不到相关信息就说不知道」吗?
- 约束不够 → 优化 Prompt
-
temperature 高不高?
- temperature > 0.5 会增加 LLM 的生成随机性
- 对于 RAG 场景,0.1~0.3 更合适
-
是不是 token 限制被截断了?
- 如果 Prompt 加上资料后超过了模型的上下文窗口,后边的资料会被截掉
- 截断了?→ 减少 K 值或 chunk size,或使用更大上下文窗口的模型
快速诊断表¶
生产环境的调试技巧¶
1. 记录检索日志
在每个查询中记录:用户问题、检索到的 Top-K 片段及分数、重排后的片段及分数、最终 Prompt、LLM 回答:
```python import json
def log_query(user_question, retrieval_results, final_prompt, llm_answer): log = { "question": user_question, "retrieval_top_k": [ {"text": t, "score": float(s)} for t, s in retrieval_results ], "final_prompt": final_prompt, "llm_answer": llm_answer, } with open("query_log.jsonl", "a", encoding="utf-8") as f: f.write(json.dumps(log, ensure_ascii=False) + "\n") ```
有了日志,你就可以离线分析失败模式,而不是在线上盲目调试。
2. 建立测试集(Golden Dataset)
收集 50~100 个典型问题和正确答案,自动化跑回归测试:
```python
golden_dataset.json¶
[ { "question": "RAG 由哪些部分组成?", "expected": ["检索器", "知识库", "生成器"], "category": "概念理解" }, { "question": "年付套餐怎么退款?", "expected": ["30 天内可全额退款", "超过 30 天按比例"], "category": "业务政策" } ] ```
每次修改系统后,用数据集跑一遍,看召回率(Retrieval Recall) 和回答准确率(Answer Accuracy) 是否下降。
3. A/B 测试
要验证一个改动是否有效,在线上分流对比最可靠。以下变量值得做 A/B 测试:
- 不同切分策略
- 不同 Embedding 模型
- 不同 K 值
- 有/无重排
- 不同 Prompt 模板
- 不同 temperature
每次只改一个变量,观察回答质量指标的变化。
各环节常见问题速查¶
切分相关问题¶
| 症状 | 可能原因 | 解决方案 |
|---|---|---|
| 检索到的 chunk 信息不完整 | chunk size 太小或 overlap 不足 | 增加 chunk size 或 overlap |
| 同一个话题被切成多个 chunk | 固定长度切分没考虑语义边界 | 改用语义切分或按标题切分 |
| 检索结果很精确但上下文太少 | 切分太细,chunk 缺少上下文 | 适当增大 chunk size |
检索相关问题¶
| 症状 | 可能原因 | 解决方案 |
|---|---|---|
| 完全找不到匹配 | Embedding 模型不合适 | 换一个更适合语种的 Embedding 模型 |
| 分数偏低 | 语义鸿沟大 | 加 Query 重写或用 HyDE |
| 答案在 Top-4,K=3 找不到 | K 值太小 | 增大 K 值,配合重排 |
| 检索结果看似相关但不解决问题 | 语义检索没捕捉到意图 | 加混合检索(语义 + BM25) |
重排相关问题¶
| 症状 | 可能原因 | 解决方案 |
|---|---|---|
| 正确答案重排后排到了后面 | Rerank 模型不匹配语言/领域 | 换个 Rerank 模型 |
| 重排后 Top-1 和 Top-3 效果差异不大 | 候选集本身质量高 | 可考虑减少 K 值跳过重排 |
| 重排严重影响延迟 | 模型太大或候选太多 | 换轻量模型,或减少候选数 |
生成相关问题¶
| 症状 | 可能原因 | 解决方案 |
|---|---|---|
| LLM 编造了资料中没有的信息 | Prompt 没约束「只根据资料回答」 | 添加约束,降低 temperature |
| LLM 回答了「不知道」但资料里有 | Prompt 在资料前,模型更依赖训练数据 | 把资料放在 Prompt 开头 |
| 回答太长或结构混乱 | 没指定输出格式 | 在 Prompt 中限定格式 |
| 回答了「我不知道」但资料中有 | 上下文被截断 | 减少 K 值或 chunk size |
| 回答自相矛盾 | 多个 chunk 信息冲突 | 检查切分是否产生矛盾片段 |
最小示例¶
```python import openai import numpy as np
def diagnose_rag(question, chunks, answer): """RAG 问题诊断助手""" issues = []
# 1. 资料中有相关片段吗?
q_resp = openai.embeddings.create(
model="text-embedding-3-small", input=[question]
)
q_vec = np.array(q_resp.data[0].embedding)
chunk_resp = openai.embeddings.create(
model="text-embedding-3-small", input=chunks
)
chunk_vecs = np.array([d.embedding for d in chunk_resp.data])
scores = np.dot(chunk_vecs, q_vec) / (
np.linalg.norm(chunk_vecs, axis=1) * np.linalg.norm(q_vec)
)
best_score = float(np.max(scores))
if best_score < 0.3:
issues.append(f"低相似度:最高相似度仅 {best_score:.2f},可能检索没找到正确内容")
elif best_score < 0.6:
issues.append(f"中等相似度:最高 {best_score:.2f},需要确认结果是否真的相关")
else:
issues.append(f"相似度良好({best_score:.2f}),检索环节可能没问题")
# 2. 回答中有没有可能编造的内容?
check_prompt = f"""以下回答完全基于提供的资料吗?
资料(前3个chunk):{chunks[:3]} 回答:{answer} 是否完全基于资料?回答 Yes 或 No 并说明理由。""" check = openai.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": check_prompt}], temperature=0, ) issues.append(f"事实性检查结果:{check.choices[0].message.content}")
return issues
使用示例¶
issues = diagnose_rag( question="RAG 是什么?", chunks=["RAG 是一种文本生成方法。", "机器学习的应用很广泛。"], answer="RAG 是检索增强生成技术。", ) for issue in issues: print(f"- {issue}") ```
常见误区¶
误区 1:最后一个环节出问题就是 LLM 的错
多数 RAG 回答质量差的问题,根因在检索环节(没找到正确答案),而不是生成阶段。先检查检索。
误区 2:上线之后就不需要关注了
文档更新、用户提问方式变化、Embedding 模型升级——任何一个变动都可能影响 RAG 质量。建议定期跑测试数据集。
误区 3:调试靠直觉
不要猜。检索日志、测试集、A/B 测试——三个工具能解决 90% 的 RAG 调试问题。直觉不可靠,数据才可靠。
延伸阅读¶
练习题¶
练习 1:一次完整的 RAG 调试
假设你的 RAG 系统遇到了以下问题:
用户问:「你们公司的退款政策是什么?」LLM 回答:「一般来说,大多数公司接受 30 天内退款。」
这个回答明显不对——你们公司的政策是「软件产品一旦激活概不退款」。
请按照「十一问」排查流程,写出你的排查思路和可能的解决方案。
练习 2:搭建你的测试数据集
为你的 RAG 场景准备 20 个测试用例,每个包含:
question:用户问题expected:期望的回答关键词(3~5 个)category:问题类型
然后写一个脚本:
- 用你的 RAG 系统回答这 20 个问题
- 判断回答是否包含所有
expected关键词 - 输出通过率
这个测试集将成为你后续所有 RAG 改动的基本验证工具。