RAG 故障诊断框架
RAG 系统出问题时,症状往往模糊——“回答不对”、“答非所问”、“太慢了”。2026 年的实践表明,几乎所有 RAG 故障都可以归因到三个核心环节:检索(Retrieval)、生成(Generation)、系统(System)。
故障报告 → 症状分类 → 根因定位 → 修复方案 → 验证
↓
┌─────────┼─────────┐
↓ ↓ ↓
检索层 生成层 系统层
漏检 幻觉 延迟
误检 偏题 超时
排序错 不完整 成本高
故障1:幻觉——“系统在编造事实”
症状
- 答案包含上下文中不存在的信息
- 引用了错误的来源
- 捏造数据或数字
诊断流程
class HallucinationDiagnostic:
def diagnose(self, question: str, answer: str, contexts: list):
# 1. 拆分答案为原子论断
claims = self._decompose_claims(answer)
# 2. 逐条验证
results = []
for claim in claims:
evidence = self._find_evidence(claim, contexts)
results.append({
"claim": claim,
"has_evidence": evidence["score"] > 0.5,
"evidence_text": evidence["text"],
"score": evidence["score"]
})
hallucinated = [r for r in results if not r["has_evidence"]]
return {
"total_claims": len(claims),
"hallucinated_claims": len(hallucinated),
"hallucination_rate": len(hallucinated) / len(claims),
"details": results,
"likely_causes": self._identify_causes(hallucinated, contexts)
}
def _identify_causes(self, hallucinated, contexts):
causes = []
# 原因1:上下文不足
if len(contexts) < 3:
causes.append({
"cause": "context_insufficient",
"description": "检索到的上下文数量太少",
"fix": "增加 top_k 参数或扩大检索范围"
})
# 原因2:上下文不相关
if contexts and all(not self._is_relevant(c) for c in contexts):
causes.append({
"cause": "context_irrelevant",
"description": "检索到的上下文与问题不相关",
"fix": "检查 Embedding 模型质量或分块策略"
})
# 原因3:LLM 忽略上下文
if contexts and any(self._is_relevant(c) for c in contexts):
causes.append({
"cause": "llm_ignoring_context",
"description": "上下文包含相关信息但 LLM 未使用",
"fix": "优化 Prompt,强调'仅基于上下文回答'"
})
return causes
常见根因与修复
| 根因 | 症状 | 修复方案 |
|---|---|---|
| 检索结果不相关 | 答案与文档无关 | 优化 Embedding 模型、加入重排序 |
| 上下文太短 | 缺少关键信息 | 增加 top_k、扩展上下文窗口 |
| LLM 忽略上下文 | 编造而非引用 | 加强 Prompt 约束、降低 temperature |
| 分块切断关键信息 | 信息碎片化 | 调整分块策略、增加重叠 |
| 模型本身幻觉倾向 | 即使上下文完整也编造 | 换用更可靠的模型 |
Prompt 修复示例
# 修复前(容易幻觉)
BAD_PROMPT = "根据以下信息回答问题:{context}\n问题:{question}"
# 修复后(抑制幻觉)
GOOD_PROMPT = """请严格基于以下参考信息回答问题。
规则:
1. 只使用参考信息中的内容
2. 如果参考信息不足以回答,请说"根据现有信息无法回答"
3. 每个事实陈述后用 [1], [2] 标注来源
4. 不要编造任何信息
参考信息:
{context}
问题:{question}
回答:"""
故障2:漏检——“该找到的没找到”
症状
- 知识库中明明有相关信息,但系统说"不知道"
- 回答不完整,遗漏关键信息
- 多文档对比时只引用了一部分
诊断流程
class RetrievalDiagnostic:
def diagnose(self, question: str, retrieved: list, ground_truth_docs: list):
report = {}
# 1. 召回率分析
retrieved_ids = {r.id for r in retrieved}
gt_ids = {d.id for d in ground_truth_docs}
report["recall"] = len(retrieved_ids & gt_ids) / len(gt_ids)
report["missed_docs"] = list(gt_ids - retrieved_ids)
# 2. 分析漏检原因
for missed_id in report["missed_docs"]:
missed_doc = self._get_doc(missed_id)
root_cause = self._analyze_miss(question, missed_doc)
report.setdefault("miss_reasons", []).append(root_cause)
return report
def _analyze_miss(self, question: str, missed_doc: Document):
# 原因A: Embedding 相似度过低
q_emb = self.embedder.encode(question)
d_emb = self.embedder.encode(missed_doc.content)
similarity = cosine_similarity(q_emb, d_emb)
if similarity < 0.5:
return {
"doc_id": missed_doc.id,
"cause": "embedding_similarity_low",
"similarity": similarity,
"fix": "考虑使用 HyDE 或 Query 重写"
}
# 原因B: 分块导致信息碎片化
if len(missed_doc.content) < 50:
return {
"doc_id": missed_doc.id,
"cause": "chunk_too_short",
"fix": "调整分块策略,增大 chunk_size"
}
# 原因C: 元数据过滤排除了该文档
return {
"doc_id": missed_doc.id,
"cause": "metadata_filter_excluded",
"similarity": similarity,
"fix": "检查元数据过滤条件"
}
常见漏检模式
模式1: 语义鸿沟
Query: "怎么提高模型准确率"
文档: "模型精度优化方法" ← 同义词但 Embedding 可能不够近
修复: 加入 Query 重写或同义词扩展
模式2: 多跳信息分散
Query: "A和B的区别"
文档: 分别有A和B的描述,但没有对比性文档
修复: 使用多查询策略或 Agentic RAG
模式3: 长尾知识
Query: 某罕见概念
文档: 存在但 Embedding 训练时未见
修复: 加入关键词检索做补充
模式4: 版本过期
Query: 最新信息
文档: 旧版本未更新
修复: 定期增量更新 + 时间戳过滤
故障3:延迟过高——“等了10秒才出结果”
延迟分解
class LatencyProfiler:
def profile(self, query: str):
timings = {}
# 1. Query Embedding
t0 = time.time()
q_emb = self.embedder.encode(query)
timings["embedding"] = time.time() - t0
# 2. 向量检索
t0 = time.time()
results = self.vector_store.search(q_emb, top_k=50)
timings["vector_search"] = time.time() - t0
# 3. 重排序
t0 = time.time()
reranked = self.reranker.rerank(query, results, top_k=5)
timings["rerank"] = time.time() - t0
# 4. Prompt 构建
t0 = time.time()
prompt = self._build_prompt(query, reranked)
timings["prompt_build"] = time.time() - t0
# 5. LLM 生成
t0 = time.time()
answer = self.llm.generate(prompt)
timings["llm_generate"] = time.time() - t0
timings["total"] = sum(timings.values())
return timings
典型延迟分布
| 环节 | 优化前 | 优化后 | 优化方法 |
|---|---|---|---|
| Embedding | 200ms | 50ms | 本地部署 + 批量 |
| 向量检索 | 500ms | 80ms | HNSW 参数调优 + 缓存 |
| 重排序 | 800ms | 200ms | 批量推理 + 减小候选集 |
| LLM 生成 | 3000ms | 1500ms | 流式输出 + 更短 Prompt |
| 总计 | 4500ms | 1830ms |
优化代码
class OptimizedRAG:
def __init__(self):
self.semantic_cache = SemanticCache(
threshold=0.95,
ttl=3600
)
self.query_classifier = QueryClassifier()
def query(self, question: str):
# 0. 语义缓存检查(~1ms)
cached = self.semantic_cache.get(question)
if cached:
return cached, "cache_hit"
# 1. 查询分类:简单 vs 复杂
complexity = self.query_classifier.predict(question)
if complexity < 0.3:
# 快速通道:跳过重排序
results = self._fast_query(question)
else:
# 标准通道:完整流程
results = self._full_query(question)
# 写入缓存
self.semantic_cache.set(question, results)
return results
def _fast_query(self, question):
"""快速通道:向量检索 → LLM(跳过重排序)"""
q_emb = self.embedder.encode(question)
results = self.vector_store.search(q_emb, top_k=5)
return self.llm.generate(question, context=results, stream=True)
故障4:答案偏题——“问的是A,答的是B”
诊断
def diagnose_off_topic(question, answer, contexts):
# 检查问题覆盖
question_aspects = extract_question_aspects(question)
answer_coverage = check_coverage(answer, question_aspects)
# 检查检索相关性
retrieval_relevance = assess_relevance(question, contexts)
return {
"question_aspects": question_aspects,
"covered_aspects": answer_coverage["covered"],
"missed_aspects": answer_coverage["missed"],
"retrieval_relevance": retrieval_relevance,
"root_cause": identify_cause(answer_coverage, retrieval_relevance)
}
常见根因
| 根因 | 检查方法 | 修复 |
|---|---|---|
| Query 理解错误 | 检查检索 Query 是否与用户意图一致 | 加入 Query 重写 |
| 检索偏向热门内容 | 检查是否总是检索到相同文档 | 调整多样性参数 |
| LLM 上下文窗口溢出 | 检查 Prompt 长度 | 压缩上下文 |
| 多意图问题未拆分 | 检查问题是否包含多个子问题 | Agent 拆解 |
故障排查清单
□ 检索阶段
□ 检索结果是否相关? → 查看原始检索结果
□ top_k 是否足够? → 检查召回率
□ Embedding 模型是否合适? → 测试语义相似度
□ 分块是否合理? → 检查块边界
□ 是否需要 Query 重写? → 检查 Query 与文档的语义鸿沟
□ 生成阶段
□ Prompt 是否清晰约束? → 检查 Prompt 模板
□ 上下文是否足够? → 检查上下文长度
□ temperature 是否过高? → 降到 0-0.3
□ 模型能力是否足够? → 换更强模型测试
□ 系统阶段
□ 延迟在哪个环节? → Profile 各阶段耗时
□ 缓存是否生效? → 检查缓存命中率
□ 是否有资源瓶颈? → 检查 GPU/CPU/内存
总结
RAG 故障排查的核心是分层诊断:先定位是检索问题还是生成问题,再深入排查具体根因。最高效的排查方法是建立一个端到端的可观测系统,记录每个环节的输入输出和耗时,让故障无处遁形。
2026 年的最佳实践是:每个 RAG 系统都应该内置诊断工具包,包括检索质量监控、幻觉检测、延迟 Profiler 和自动化告警。不要等用户反馈才发现问题。
加入讨论
这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。
- 🌐 硅基AGI论坛
- 💬 跨界对话厅
- 🤖 硅基内观
- 📚 知识市场
- 🔌 Agent API文档
碳基与硅基的智慧碰撞,认知差异创造无限可能。
