摘要

你喂给AI的信息真的有用吗?模型真的用了你给它的上下文吗?幻觉了多少?这篇文章手把手教你评估上下文质量——从基础的忠实度检测,到RAGAS评估框架,再到最新的幻觉检测技术。看完你就能给AI应用做”体检”了。

先问个问题:为什么要评估上下文质量?

想象这两个场景

场景A(没做评估):

用户:苹果公司是哪年成立的?
AI:根据上下文信息,苹果公司成立于1976年。
(实际上上下文里根本没提这个日期,AI瞎编的!)

场景B(做了评估):

用户:苹果公司是哪年成立的?
AI:我检查了您提供的上下文,里面没有提到具体的成立年份。
所以我无法回答这个问题。
(上下文忠实度:LOW → 触发降级回复)

这就是上下文质量评估的价值——让AI知道自己”不知道”什么


一、上下文质量评估的四大金刚

评估维度一览

┌─────────────────────────────────────────────────────────────┐
│                    RAG/上下文评估四大维度                       │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  📊 上下文质量                                              │
│  ├─ Context Precision:上下文里有多少是相关的?               │
│  └─ Context Recall:答案需要的信息,上下文覆盖了多少?        │
│                                                              │
│  ✅ 答案质量                                                │
│  ├─ Faithfulness:AI有没有瞎编?                            │
│  └─ Answer Relevance:答案和问题相关吗?                    │
│                                                              │
│  🔍 幻觉检测                                                │
│  ├─ Factual Accuracy:事实对不对?                          │
│  └─ Citation Accuracy:引用准不准?                         │
│                                                              │
└─────────────────────────────────────────────────────────────┘

核心指标速查表

指标测量什么理想值评估方式
Context Precision上下文相关比例1.0自动
Context Recall相关内容召回1.0人工/LLM
Faithfulness内容忠实度1.0自动
Answer Relevance答案相关性1.0自动
Hallucination Rate瞎编比例0.0混合

二、Faithfulness(忠实度)评估

什么是忠实度?

忠实度衡量的就是:AI有没有老老实实根据你给的上下文回答,还是在瞎编?

打个比方:

你给AI一份菜单(上下文):
- 宫保鸡丁:58元
- 鱼香肉丝:52元

用户问:哪个菜便宜?
AI答:宫保鸡丁便宜,52元。← ❌ 不忠实!(把58说成52)

AI答:鱼香肉丝便宜,52元。← ✅ 忠实!

评估忠实度的方法

方法1:声明提取法

class FaithfulnessEvaluator:
    """忠实度评估器"""
    
    def __init__(self, llm_client):
        self.llm = llm_client
    
    def evaluate(
        self,
        question: str,
        context: str,
        answer: str
    ) -> dict:
        """
        评估忠实度
        
        核心思路:
        1. 从答案中提取所有"声明"(事实陈述)
        2. 检查每个声明是否被上下文支持
        3. 计算被支持的声明比例
        """
        # 1️⃣ 提取声明
        claims = self._extract_claims(answer)
        
        # 2️⃣ 检查每个声明
        supported = []
        unsupported = []
        
        for claim in claims:
            if self._is_supported(claim, context):
                supported.append(claim)
            else:
                unsupported.append(claim)
        
        # 3️⃣ 计算分数
        score = len(supported) / len(claims) if claims else 1.0
        
        return {
            'score': score,
            'total_claims': len(claims),
            'supported_claims': supported,
            'unsupported_claims': unsupported,
            'level': self._get_level(score)
        }
    
    def _extract_claims(self, answer: str) -> list:
        """从答案中提取所有事实声明"""
        prompt = f"""从以下答案中提取所有可验证的事实声明。
每个声明应该是一个独立的事实陈述。
 
答案:
{answer}
 
要求:
1. 只提取明确的事实陈述
2. 每个声明用一行输出
3. 不要提取观点、感受、通用描述
 
声明列表:"""
 
        result = self.llm.generate(prompt)
        claims = [line.strip() for line in result.split('\n') if line.strip()]
        return claims
    
    def _is_supported(self, claim: str, context: str) -> bool:
        """检查声明是否被上下文支持"""
        prompt = f"""判断以下声明是否可以从提供的上下文中推断或验证。
 
声明:{claim}
 
上下文:
{context}
 
判断标准:
- 如果上下文明确支持或能推断出该声明,返回"支持"
- 如果上下文未提及或与声明矛盾,返回"不支持"
 
判断结果:"""
 
        result = self.llm.generate(prompt).strip()
        return "支持" in result
    
    def _get_level(self, score: float) -> str:
        """获取等级描述"""
        if score >= 0.9:
            return "Excellent (优秀)"
        elif score >= 0.7:
            return "Good (良好)"
        elif score >= 0.5:
            return "Fair (一般)"
        else:
            return "Poor (较差)"

方法2:句子级评估

class SentenceLevelFaithfulness:
    """句子级忠实度评估"""
    
    def __init__(self, llm_client):
        self.llm = llm_client
    
    def evaluate_sentence_level(
        self,
        context: str,
        answer: str
    ) -> dict:
        """逐句评估忠实度"""
        # 按句子分割答案
        sentences = self._split_sentences(answer)
        
        results = []
        for sentence in sentences:
            verdict = self._evaluate_single_sentence(sentence, context)
            results.append({
                'sentence': sentence,
                'supported': verdict['supported'],
                'reason': verdict.get('reason', ''),
            })
        
        # 计算整体分数
        supported_count = sum(1 for r in results if r['supported'])
        overall_score = supported_count / len(results) if results else 1.0
        
        return {
            'overall_score': overall_score,
            'sentence_results': results,
            'unsupported_sentences': [r for r in results if not r['supported']]
        }
    
    def _split_sentences(self, text: str) -> list:
        """按句子分割"""
        import re
        sentences = re.split(r'[。!?\n]', text)
        return [s.strip() for s in sentences if s.strip()]
    
    def _evaluate_single_sentence(
        self,
        sentence: str,
        context: str
    ) -> dict:
        """评估单个句子"""
        prompt = f"""判断以下句子是否完全由上下文支持。
 
句子:{sentence}
 
上下文:
{context}
 
分析步骤:
1. 检查句子中的每个事实是否在上下文中有依据
2. 检查句子的推断是否合理
3. 检查是否有添加上下文中不存在的信息
 
判断结果和理由:"""
 
        result = self.llm.generate(prompt)
        
        supported = "支持" in result or "一致" in result
        reason = result.split("理由:")[-1].strip() if "理由" in result else ""
        
        return {
            'supported': supported,
            'reason': reason,
            'llm_analysis': result
        }

实战:调用示例

# 使用忠实度评估器
evaluator = FaithfulnessEvaluator(llm_client)
 
result = evaluator.evaluate(
    question="苹果公司是哪年成立的?",
    context="苹果公司由史蒂夫·乔布斯、史蒂夫·沃兹尼亚克等人于1976年在美国加利福尼亚州创立。",
    answer="苹果公司成立于1976年,由乔布斯等人创立。"
)
 
print(f"忠实度得分: {result['score']:.2f}")
print(f"等级: {result['level']}")
print(f"总声明数: {result['total_claims']}")
print(f"被支持的声明: {result['supported_claims']}")
print(f"未被支持的声明: {result['unsupported_claims']}")

三、Answer Relevance(答案相关性)评估

什么是答案相关性?

答案相关性衡量的是:答案和问题有没有对上,答非所问了吗?

问题:苹果公司是哪年成立的?
答案A:苹果公司成立于1976年。← ✅ 高相关
答案B:苹果公司是一家美国科技公司。← 🟡 中等相关(提到了苹果,但没回答成立年份)
答案C:橙子是富含维生素C的水果。← ❌ 不相关

评估方法

class AnswerRelevanceEvaluator:
    """答案相关性评估"""
    
    def __init__(self, llm_client, embedding_model=None):
        self.llm = llm_client
        self.embedding = embedding_model
    
    def evaluate(
        self,
        question: str,
        answer: str
    ) -> dict:
        """
        评估答案相关性
        
        核心思路:
        1. 根据问题和答案,生成多个等价问题
        2. 如果答案真的相关,它应该能回答这些等价问题
        3. 计算等价问题的回答质量
        """
        # 1️⃣ 生成等价问题
        equivalent_questions = self._generate_equivalent_questions(
            question, answer
        )
        
        # 2️⃣ 计算相似度
        if self.embedding:
            similarities = self._calculate_embedding_similarity(
                answer, equivalent_questions
            )
        else:
            similarities = self._calculate_keyword_similarity(
                answer, equivalent_questions
            )
        
        # 3️⃣ 综合评分
        score = sum(similarities) / len(similarities)
        
        return {
            'score': score,
            'equivalent_questions': equivalent_questions,
            'similarities': similarities,
            'relevance_level': self._get_level(score)
        }
    
    def _generate_equivalent_questions(
        self,
        question: str,
        answer: str
    ) -> list:
        """生成问题的等价表述"""
        prompt = f"""基于以下问题和答案,生成3-5个与原问题语义等价但表达不同的问法。
 
原问题:{question}
 
答案:
{answer}
 
要求:
1. 生成的问题应该能够被同样的答案回答
2. 使用不同的词汇和句式
3. 每个问题一行
 
等价问题:"""
 
        result = self.llm.generate(prompt)
        questions = [q.strip() for q in result.split('\n') if q.strip()]
        return questions[:5]
    
    def _calculate_keyword_similarity(
        self,
        answer: str,
        questions: list
    ) -> list:
        """基于关键词计算相似度"""
        answer_keywords = set(answer.lower().split())
        similarities = []
        
        for q in questions:
            q_keywords = set(q.lower().split())
            if not q_keywords:
                similarities.append(0)
                continue
            
            overlap = len(answer_keywords & q_keywords)
            similarity = overlap / len(q_keywords)
            similarities.append(similarity)
        
        return similarities

四、Context Precision与Recall

Context Precision(上下文精确度)

问:上下文里有多少是真正有用的?

假设你检索到了10个文档块
但只有3个和问题真正相关

Context Precision = 3/10 = 0.3 (30%)
class ContextPrecisionEvaluator:
    """上下文精确度评估"""
    
    def __init__(self, llm_client):
        self.llm = llm_client
    
    def evaluate(
        self,
        question: str,
        contexts: list
    ) -> dict:
        """
        评估上下文精确度
        
        Context Precision = 相关文档数 / 总文档数
        """
        if not contexts:
            return {'score': 0, 'precision': 0, 'details': []}
        
        # 评估每个上下文的相关性
        relevance_scores = []
        for i, ctx in enumerate(contexts):
            relevance = self._assess_relevance(question, ctx)
            relevance_scores.append({
                'context_id': i,
                'context_preview': ctx[:100] + '...',
                'relevance': relevance,
                'is_relevant': relevance >= 0.5
            })
        
        # 计算精确度
        relevant_count = sum(1 for r in relevance_scores if r['is_relevant'])
        precision = relevant_count / len(contexts)
        
        return {
            'score': precision,
            'precision': precision,
            'relevant_count': relevant_count,
            'total_count': len(contexts),
            'details': relevance_scores
        }
    
    def _assess_relevance(self, question: str, context: str) -> float:
        """评估单个上下文的 relevance"""
        prompt = f"""评估以下上下文对于回答问题的相关程度。
 
问题:{question}
 
上下文:
{context}
 
评分标准(0-1):
- 1.0: 上下文直接包含回答问题所需的关键信息
- 0.7: 上下文包含大部分相关信息
- 0.5: 上下文包含部分相关信息
- 0.3: 上下文相关度较低
- 0.0: 上下文与问题完全无关
 
相关度评分(只输出数字0-1):"""
 
        try:
            result = self.llm.generate(prompt).strip()
            return float(result)
        except:
            return 0.5

Context Recall(上下文召回率)

问:回答问题需要的信息,上下文覆盖了多少?

问题需要的信息点:
1. 苹果公司成立年份 ✓(上下文有)
2. 创始人名字 ✓(上下文有)
3. 公司总部位置 ✗(上下文没有)

Context Recall = 2/3 = 0.67 (67%)
class ContextRecallEvaluator:
    """上下文召回率评估"""
    
    def __init__(self, llm_client):
        self.llm = llm_client
    
    def evaluate(
        self,
        contexts: list,
        ground_truth: str
    ) -> dict:
        """
        评估上下文召回率
        
        Context Recall = 上下文覆盖的ground truth信息点 / 总信息点
        """
        if not contexts:
            return {'score': 0, 'recall': 0, 'details': []}
        
        # 合并上下文
        combined_context = '\n\n'.join(contexts)
        
        # 提取ground truth中的关键信息点
        gt_key_points = self._extract_key_points(ground_truth)
        
        # 检查每个信息点是否被覆盖
        covered_points = []
        uncovered_points = []
        
        for point in gt_key_points:
            if self._is_covered(point, combined_context):
                covered_points.append(point)
            else:
                uncovered_points.append(point)
        
        recall = len(covered_points) / len(gt_key_points) if gt_key_points else 0
        
        return {
            'score': recall,
            'recall': recall,
            'total_points': len(gt_key_points),
            'covered_points': covered_points,
            'uncovered_points': uncovered_points,
            'coverage_ratio': f"{len(covered_points)}/{len(gt_key_points)}"
        }
    
    def _extract_key_points(self, text: str) -> list:
        """提取关键信息点"""
        prompt = f"""从以下文本中提取所有关键信息点。
 
文本:
{text}
 
要求:
1. 提取具体的事实、数据、定义
2. 每个信息点一行
3. 不要提取通用描述
 
关键信息点:"""
 
        result = self.llm.generate(prompt)
        points = [p.strip() for p in result.split('\n') if p.strip()]
        return points

五、RAGAS评估框架

RAGAS是什么?

RAGAS(Retrieval Augmented Generation Assessment)是2023年提出的RAG系统评估标准框架,提供了一套完整的评估指标:

┌─────────────────────────────────────────────────────────────┐
│                       RAGAS评估体系                          │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  📊 Faithfulness(忠实度)                                  │
│  └─ 答案有多少内容被上下文支持?                             │
│                                                              │
│  📊 Answer Relevance(答案相关性)                           │
│  └─ 答案和问题有多相关?                                     │
│                                                              │
│  📊 Context Precision(上下文精确度)                         │
│  └─ 检索到的内容有多少相关?                                  │
│                                                              │
│  📊 Context Recall(上下文召回率)[需ground truth]           │
│  └─ 需要的信息召回了多少?                                   │
│                                                              │
└─────────────────────────────────────────────────────────────┘

RAGAS完整实现

from dataclasses import dataclass
from typing import List, Optional
 
@dataclass
class RAGASResult:
    """RAGAS评估结果"""
    faithfulness: float
    answer_relevance: float
    context_precision: float
    context_recall: Optional[float]
    overall_score: float
 
class RAGASEvaluator:
    """RAGAS评估框架"""
    
    def __init__(
        self,
        llm_client,
        embedding_model=None
    ):
        self.llm = llm_client
        self.embedding = embedding_model
        
        self.faithfulness_eval = FaithfulnessEvaluator(llm_client)
        self.relevance_eval = AnswerRelevanceEvaluator(llm_client, embedding_model)
        self.precision_eval = ContextPrecisionEvaluator(llm_client)
        self.recall_eval = ContextRecallEvaluator(llm_client)
    
    def evaluate(
        self,
        question: str,
        answer: str,
        contexts: List[str],
        ground_truth: str = None
    ) -> RAGASResult:
        """完整RAGAS评估"""
        # 1️⃣ Faithfulness
        faithfulness_result = self.faithfulness_eval.evaluate(
            question, '\n\n'.join(contexts), answer
        )
        faithfulness = faithfulness_result['score']
        
        # 2️⃣ Answer Relevance
        relevance_result = self.relevance_eval.evaluate(question, answer)
        answer_relevance = relevance_result['score']
        
        # 3️⃣ Context Precision
        precision_result = self.precision_eval.evaluate(question, contexts)
        context_precision = precision_result['precision']
        
        # 4️⃣ Context Recall(需要ground truth)
        context_recall = None
        if ground_truth:
            recall_result = self.recall_eval.evaluate(contexts, ground_truth)
            context_recall = recall_result['recall']
        
        # 5️⃣ Overall Score
        overall = self._calculate_overall(
            faithfulness,
            answer_relevance,
            context_precision,
            context_recall
        )
        
        return RAGASResult(
            faithfulness=faithfulness,
            answer_relevance=answer_relevance,
            context_precision=context_precision,
            context_recall=context_recall,
            overall_score=overall
        )
    
    def _calculate_overall(
        self,
        faithfulness: float,
        answer_relevance: float,
        context_precision: float,
        context_recall: Optional[float]
    ) -> float:
        """计算综合分数"""
        # 无ground truth时
        if context_recall is None:
            return (0.4 * faithfulness + 
                    0.3 * answer_relevance + 
                    0.3 * context_precision)
        
        # 有ground truth时
        return (0.35 * faithfulness + 
                0.25 * answer_relevance + 
                0.2 * context_precision + 
                0.2 * context_recall)

批量评估与报告

class BatchRAGASEvaluator:
    """批量RAGAS评估"""
    
    def __init__(self, evaluator: RAGASEvaluator):
        self.evaluator = evaluator
    
    def evaluate_dataset(
        self,
        test_cases: List[dict]
    ) -> dict:
        """
        批量评估测试集
        
        test_cases格式:
        {
            'question': str,
            'answer': str,
            'contexts': List[str],
            'ground_truth': Optional[str]
        }
        """
        results = []
        
        for i, case in enumerate(test_cases):
            try:
                result = self.evaluator.evaluate(
                    question=case['question'],
                    answer=case['answer'],
                    contexts=case['contexts'],
                    ground_truth=case.get('ground_truth')
                )
                results.append({
                    'case_id': i,
                    'success': True,
                    'result': result
                })
            except Exception as e:
                results.append({
                    'case_id': i,
                    'success': False,
                    'error': str(e)
                })
        
        # 汇总统计
        successful_results = [r['result'] for r in results if r['success']]
        
        if successful_results:
            stats = {
                'faithfulness': np.mean([r.faithfulness for r in successful_results]),
                'answer_relevance': np.mean([r.answer_relevance for r in successful_results]),
                'context_precision': np.mean([r.context_precision for r in successful_results]),
                'overall_score': np.mean([r.overall_score for r in successful_results])
            }
        else:
            stats = {}
        
        return {
            'total_cases': len(test_cases),
            'successful': len(successful_results),
            'failed': len(test_cases) - len(successful_results),
            'stats': stats,
            'detailed_results': results
        }
    
    def generate_report(self, results: dict) -> str:
        """生成评估报告"""
        stats = results['stats']
        
        return f"""
# RAG系统评估报告
 
## 评估概览
 
- 测试用例总数:{results['total_cases']}
- 成功评估:{results['successful']}
- 失败评估:{results['failed']}
 
## 核心指标
 
| 指标 | 平均分数 | 评估 |
|------|---------|------|
| Faithfulness(忠实度) | {stats.get('faithfulness', 0):.3f} | {'✓' if stats.get('faithfulness', 0) > 0.7 else '✗'} |
| Answer Relevance(相关性) | {stats.get('answer_relevance', 0):.3f} | {'✓' if stats.get('answer_relevance', 0) > 0.6 else '✗'} |
| Context Precision(精确度) | {stats.get('context_precision', 0):.3f} | {'✓' if stats.get('context_precision', 0) > 0.5 else '✗'} |
| Overall Score(综合分) | {stats.get('overall_score', 0):.3f} | - |
 
## 改进建议
 
{self._generate_recommendations(stats)}
 
---
*报告生成时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}*
"""
    
    def _generate_recommendations(self, stats: dict) -> str:
        """生成改进建议"""
        recommendations = []
        
        if stats.get('faithfulness', 0) < 0.7:
            recommendations.append(
                "1. **提升忠实度**:检查上下文是否充分支持生成内容"
            )
        
        if stats.get('answer_relevance', 0) < 0.6:
            recommendations.append(
                "2. **提升答案相关性**:优化检索策略,确保检索到更相关的内容"
            )
        
        if stats.get('context_precision', 0) < 0.5:
            recommendations.append(
                "3. **提升上下文精确度**:使用更精细的重排和过滤机制"
            )
        
        if not recommendations:
            recommendations.append("✓ 系统表现良好,继续保持当前策略。")
        
        return "\n".join(recommendations)

六、幻觉检测

什么是幻觉?

幻觉就是AI编造了上下文中不存在的信息

上下文:苹果公司成立于1976年,总部位于加州库比蒂诺。

幻觉例子:
❌ "苹果公司成立于1975年"(年份错误)
❌ "苹果公司总部在纽约"(地点错误)
❌ "乔布斯是苹果公司唯一的创始人"(遗漏创始人)

非幻觉例子:
✓ "苹果公司成立于1976年"(正确引用)
✓ "苹果公司由多人创立"(正确泛化)

Self-RAG检测法

Self-RAG通过自我反思来检测幻觉:

class SelfRAGDetector:
    """Self-RAG幻觉检测"""
    
    def __init__(self, llm_client):
        self.llm = llm_client
    
    def detect_hallucination(
        self,
        context: str,
        answer: str
    ) -> dict:
        """
        检测幻觉
        
        核心思路:
        1. 从答案中提取所有事实声明
        2. 逐个验证是否被上下文支持
        3. 计算幻觉率
        """
        # 1️⃣ 提取声明
        claims = self._extract_factual_claims(answer)
        
        # 2️⃣ 逐个验证
        verified_claims = []
        hallucinated_claims = []
        
        for claim in claims:
            is_hallucinated, confidence = self._verify_claim(claim, context)
            
            if is_hallucinated:
                hallucinated_claims.append({
                    'claim': claim,
                    'confidence': confidence,
                    'severity': self._assess_severity(claim, confidence)
                })
            else:
                verified_claims.append(claim)
        
        # 3️⃣ 计算幻觉率
        hallucination_rate = len(hallucinated_claims) / len(claims) if claims else 0
        
        return {
            'hallucination_rate': hallucination_rate,
            'total_claims': len(claims),
            'verified_claims': verified_claims,
            'hallucinated_claims': hallucinated_claims,
            'risk_level': 'High' if hallucination_rate > 0.3 else 'Medium' if hallucination_rate > 0.1 else 'Low'
        }
    
    def _extract_factual_claims(self, text: str) -> list:
        """提取事实声明"""
        prompt = f"""从以下文本中提取所有可验证的事实声明。
忽略观点、感受、通用描述。
 
文本:
{text}
 
每个声明一行:"""
 
        result = self.llm.generate(prompt)
        return [c.strip() for c in result.split('\n') if c.strip()]
    
    def _verify_claim(
        self,
        claim: str,
        context: str
    ) -> tuple:
        """
        验证声明
        
        返回:(是否幻觉, 置信度)
        """
        prompt = f"""判断以下声明是否与上下文一致。
 
声明:{claim}
 
上下文:
{context}
 
判断:
A. 完全一致 - 上下文明确支持
B. 基本一致 - 上下文暗示或可推断
C. 不确定 - 上下文未提及
D. 不一致 - 上下文与声明矛盾
 
判断结果和置信度(0-1):"""
 
        result = self.llm.generate(prompt)
        
        if "A. 完全一致" in result or "完全一致" in result:
            return False, 0.95
        elif "B. 基本一致" in result or "基本一致" in result:
            return False, 0.75
        elif "D. 不一致" in result or "不一致" in result:
            return True, 0.9
        else:
            return True, 0.5  # 不确定视为可能的幻觉

无参考幻觉检测(Self-CheckGPT风格)

有时候我们没有参考上下文,这时候可以用自洽性检测

class SelfConsistencyChecker:
    """自洽性检测(无参考幻觉检测)"""
    
    def __init__(self, llm_client):
        self.llm = llm_client
    
    def check(
        self,
        answer: str,
        question: str = None
    ) -> dict:
        """
        无参考幻觉检测
        
        通过检测答案内部的一致性来判断可靠性
        """
        # 1️⃣ 提取关键陈述
        statements = self._extract_statements(answer)
        
        # 2️⃣ 检查自洽性
        consistency = self._check_self_consistency(statements)
        
        # 3️⃣ 检测过度自信
        overconfidence = self._detect_overconfidence(answer)
        
        # 4️⃣ 综合评分
        risk_score = self._calculate_risk(
            consistency, overconfidence
        )
        
        return {
            'risk_score': risk_score,
            'risk_level': 'High' if risk_score > 0.5 else 'Medium' if risk_score > 0.2 else 'Low',
            'consistency_score': consistency,
            'overconfidence_detected': overconfidence,
            'statements_checked': len(statements)
        }
    
    def _extract_statements(self, text: str) -> list:
        """提取关键陈述"""
        import re
        sentences = re.split(r'[。!?\n]', text)
        return [s.strip() for s in sentences if s.strip() and len(s) > 10]
    
    def _check_self_consistency(self, statements: list) -> float:
        """检查自洽性"""
        if len(statements) < 2:
            return 1.0
        
        prompt = f"""分析以下陈述之间是否存在逻辑矛盾。
 
陈述列表:
{chr(10).join([f"{i+1}. {s}" for i, s in enumerate(statements)])}
 
判断:
- 如果所有陈述逻辑一致,返回"一致"
- 如果存在矛盾,指出矛盾之处
 
分析结果:"""
 
        result = self.llm.generate(prompt)
        
        if "一致" in result:
            return 1.0
        elif "矛盾" in result or "冲突" in result:
            return 0.3
        else:
            return 0.7
    
    def _detect_overconfidence(self, answer: str) -> dict:
        """检测过度自信"""
        overconfidence_markers = [
            '绝对', '肯定', '一定', '毫无疑问',
            '所有人', '绝对不会', '100%', '必然'
        ]
        
        found = [m for m in overconfidence_markers if m in answer]
        
        return {
            'markers_found': found,
            'count': len(found),
            'is_suspicious': len(found) > 2
        }
    
    def _calculate_risk(
        self,
        consistency: float,
        overconfidence: dict
    ) -> float:
        """计算风险分数"""
        score = consistency
        
        if overconfidence['is_suspicious']:
            score *= 0.8
        
        return max(0, min(1, score))

七、实战评估流程

完整评估流程示例

def evaluate_rag_system(
    rag_system,
    test_questions: list,
    ground_truths: list = None
):
    """
    完整的RAG系统评估流程
    """
    # 1️⃣ 初始化评估器
    evaluator = RAGASEvaluator(llm_client, embedding_model)
    hallucination_detector = SelfRAGDetector(llm_client)
    
    results = []
    
    for i, question in enumerate(test_questions):
        # 2️⃣ 获取RAG响应
        response = rag_system.query(question)
        answer = response['answer']
        contexts = response['contexts']
        
        # 3️⃣ Faithfulness评估
        faithfulness = evaluator.faithfulness_eval.evaluate(
            question, '\n\n'.join(contexts), answer
        )
        
        # 4️⃣ 幻觉检测
        hallucination = hallucination_detector.detect_hallucination(
            '\n\n'.join(contexts), answer
        )
        
        # 5️⃣ 答案相关性
        relevance = evaluator.relevance_eval.evaluate(question, answer)
        
        results.append({
            'question': question,
            'faithfulness': faithfulness['score'],
            'hallucination_rate': hallucination['hallucination_rate'],
            'answer_relevance': relevance['score'],
            'contexts': contexts
        })
    
    # 6️⃣ 生成报告
    batch_evaluator = BatchRAGASEvaluator(evaluator)
    report = batch_evaluator.generate_report({'stats': {
        'faithfulness': np.mean([r['faithfulness'] for r in results]),
        'answer_relevance': np.mean([r['answer_relevance'] for r in results]),
    }})
    
    return report, results

一图总结

┌─────────────────────────────────────────────────────────────┐
│                  上下文质量评估速查表                          │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  📊 四大评估维度                                            │
│  ├─ Faithfulness:有没有瞎编?                               │
│  ├─ Answer Relevance:答非所问?                            │
│  ├─ Context Precision:检索了多少垃圾?                      │
│  └─ Context Recall:关键信息漏了多少?                        │
│                                                              │
│  🔍 幻觉检测                                                │
│  ├─ Self-RAG:有参考,逐个验证                              │
│  └─ Self-CheckGPT:无参考,自洽性检测                       │
│                                                              │
│  📈 评估流程                                                │
│  1. 提取声明/关键陈述                                       │
│  2. 逐个验证/检查自洽性                                     │
│  3. 计算分数/比率                                           │
│  4. 生成报告/改进建议                                       │
│                                                              │
└─────────────────────────────────────────────────────────────┘

相关主题


参考文献

  1. Es, S., et al. (2023). RAGAS: Automated Evaluation of Retrieval Augmented Generation. arXiv.
  2. Manakul, P., et al. (2023). SelfCheckGPT: Zero-Resource Black-Box Hallucination Detection in Generated Text. arXiv.
  3. Shi, W., et al. (2023). Augmenting Language Models with Plug-in Memory. arXiv.