🧠AI RAG

检索链路与降幻觉

面试回答

常见问法

在 RAG 系统里,怎么降低模型幻觉?

回答

降低幻觉不能只盯模型输出,重点要看整个检索链路。通常要从检索质量、上下文拼接、回答边界约束和评测回归四层一起做。优先级一般是先保证召回和重排质量,再优化 Prompt 和回答策略。

# 完整RAG链路示例
class RAGPipeline:
    def __init__(self, retriever, reranker, generator, evaluator):
        self.retriever = retriever
        self.reranker = reranker
        self.generator = generator
        self.evaluator = evaluator
    
    def generate_with_hallucination_control(self, query):
        """
        完整的RAG流程,包含幻觉控制
        
        1. 高质量召回
        2. 精确重排序
        3. 幻觉检测
        4. 安全生成
        """
        # 1. 多源召回
        retrieved_docs = self.retriever.retrieve(query, top_k=15)
        
        # 2. 重排序
        reranked_docs = self.reranker.rerank(query, retrieved_docs, top_n=5)
        
        # 3. 构建上下文
        context = self.build_safe_context(reranked_docs, max_tokens=2000)
        
        # 4. 幻觉检测
        hallucination_risk = self.detect_hallucination_risk(query, context)
        
        # 5. 根据风险调整生成策略
        if hallucination_risk > 0.7:
            # 高风险:严格模式
            response = self.generator.generate(
                query, context, 
                mode='strict',
                citation_required=True
            )
        elif hallucination_risk > 0.3:
            # 中风险:平衡模式
            response = self.generator.generate(
                query, context,
                mode='balanced',
                citation_required=True
            )
        else:
            # 低风险:正常模式
            response = self.generator.generate(
                query, context,
                mode='normal'
            )
        
        # 6. 后验验证
        verified_response = self.verify_response(response, context)
        
        return verified_response
    
    def build_safe_context(self, documents, max_tokens=2000):
        """
        构建安全的上下文,包含引用标记
        """
        context_parts = []
        total_tokens = 0
        
        for i, doc in enumerate(documents):
            # 为每个文档添加引用标记
            doc_content = f"[引用{i+1}] {doc['content']}"
            doc_tokens = self.count_tokens(doc_content)
            
            if total_tokens + doc_tokens <= max_tokens:
                context_parts.append(doc_content)
                total_tokens += doc_tokens
            else:
                # 超出限制,截断文档
                truncated_content = self.truncate_to_fit(
                    doc_content, max_tokens - total_tokens
                )
                context_parts.append(truncated_content)
                break
        
        # 添加引用索引
        context_parts.append("\n## 可用引用")
        for i, doc in enumerate(documents):
            context_parts.append(f"{i+1}. {doc['title'][:50]}...")
        
        return "\n".join(context_parts)
    
    def detect_hallucination_risk(self, query, context):
        """
        检测幻觉风险
        
        1. 查询复杂度评估
        2. 文档覆盖度检查
        3. 语义一致性分析
        """
        risk_score = 0.0
        
        # 查询复杂度
        if self.is_complex_query(query):
            risk_score += 0.3
        
        # 文档覆盖度
        coverage = self.calculate_coverage(query, context)
        if coverage < 0.5:
            risk_score += 0.4
        elif coverage < 0.8:
            risk_score += 0.2
        
        # 语义一致性
        consistency = self.check_semantic_consistency(context)
        if consistency < 0.7:
            risk_score += 0.3
        
        return min(risk_score, 1.0)

追问

  • 为什么错误检索比检索不到更危险?(错误信息误导)
  • 引用出处为什么能提升系统可信度?(可追溯性)
  • 拒答机制应该放在检索前还是生成后?(多层防护)

原理展开

RAG 里的幻觉经常来自”拿错资料""资料不完整”或”资料虽对但拼接方式让模型误解”。因此工程优化往往要看 chunk 质量、检索召回、rerank、上下文裁剪和回答模板。

真正成熟的 RAG 系统一般还会补评测集和回归流程,否则你很难判断一次参数调整到底是在变好,还是只是在某几个样例上看起来更好。

# 幻觉检测与预防
class HallucinationPrevention:
    def __init__(self, fact_checker, confidence_threshold=0.8):
        self.fact_checker = fact_checker
        self.confidence_threshold = confidence_threshold
    
    def verify_facts(self, response, source_documents):
        """
        事实验证:检查生成内容是否与源文档一致
        
        1. 提取生成内容中的事实陈述
        2. 与源文档进行比对
        3. 标记无法验证的陈述
        """
        import re
        
        # 提取事实陈述
        facts = self.extract_facts(response)
        
        verified_facts = []
        hallucinated_facts = []
        
        for fact in facts:
            # 检查事实是否在源文档中
            support_score = self.check_support_in_sources(
                fact, source_documents
            )
            
            if support_score >= self.confidence_threshold:
                verified_facts.append({
                    'fact': fact,
                    'confidence': support_score,
                    'sources': self.find_supporting_sources(
                        fact, source_documents
                    )
                })
            else:
                hallucinated_facts.append({
                    'fact': fact,
                    'confidence': support_score,
                    'risk_level': 'high' if support_score < 0.5 else 'medium'
                })
        
        return {
            'verified_facts': verified_facts,
            'hallucinated_facts': hallucinated_facts,
            'hallucination_rate': len(hallucinated_facts) / len(facts)
        }
    
    def generate_with_citations(self, query, context, generator):
        """
        强制生成带引用的回答
        
        每个事实陈述都要标注来源
        """
        prompt = f"""
        回答以下问题,每个事实必须标注来源编号:
        
        问题:{query}
        
        参考资料:
        {context}
        
        回答格式:
        - 陈述1 [来源x]
        - 陈述2 [来源y]
        
        如果无法从资料中找到答案,请回答"我不知道"。
        """
        
        response = generator.generate(prompt)
        
        # 验证引用有效性
        validated_response = self.validate_citations(response, context)
        
        return validated_response
    
    def extract_facts(self, text):
        """从文本中提取事实陈述"""
        # 简单实现:提取以句号、问号、感叹号结尾的句子
        sentences = re.split(r'[.!?]+', text)
        facts = [s.strip() for s in sentences if s.strip()]
        return facts
    
    def check_support_in_sources(self, fact, sources):
        """检查事实在源文档中的支持程度"""
        # 使用语义相似度计算支持度
        max_similarity = 0
        
        for source in sources:
            similarity = self.semantic_similarity(fact, source['content'])
            max_similarity = max(max_similarity, similarity)
        
        return max_similarity

易错点

  • 把幻觉全部归因于模型本身(忽略检索链路问题)
  • 没有评测集,只靠主观体验调链路
  • 过度依赖Prompt工程而忽略数据质量
  • 引用机制设计不当导致信息丢失

记忆技巧

记住降幻觉四层防护:

  1. 召回质量 = “找对资料”
  2. 重排序 = “选最相关的”
  3. 生成控制 = “边界约束”
  4. 评测验证 = “持续改进”

典型应用场景:

  • 问答系统:事实准确性要求高
  • 文档分析:引用溯源需求
  • 客服系统:安全风险控制
  • 教育应用:知识准确性保证