RAG评估的困境

大多数RAG系统在上线时面临一个尴尬的问题:你知道它在工作,但你不知道它工作得有多好。 没有系统化的评估框架,调参就像蒙眼开车——你不知道改chunk_size从512到1024到底是好了还是坏了。

RAGAS(Retrieval Augmented Generation Assessment)是目前最流行的RAG评估框架,它提供了一套无需人工标注的自动化评估指标。

RAGAS核心指标体系

指标全景图

                    RAGAS 指标体系
          ┌──────────────┼──────────────┐
          ▼              ▼              ▼
     检索指标        生成指标        端到端指标
          │              │              │
    ┌─────┼─────┐  ┌────┼────┐    ┌────┼────┐
    ▼     ▼     ▼  ▼    ▼    ▼    ▼         ▼
  上下文  上下文  ─  忠实   答   答   语义     答案
  精确率  召回率     度    案   案   相似     相关
                          相   完
                          关   整
                          性   性

四大核心指标详解

指标评估什么范围理想值
Faithfulness(忠实度)答案是否忠于检索到的上下文0-1>0.95
Answer Relevancy(答案相关性)答案是否回答了用户问题0-1>0.85
Context Precision(上下文精确率)检索到的上下文有多少是相关的0-1>0.80
Context Recall(上下文召回率)回答问题所需的信息是否都检索到了0-1>0.85

指标计算原理

Faithfulness(忠实度)

衡量答案中的每个claim是否能从检索上下文中找到支撑。

问题: "GraphRAG的主要优势是什么?"
上下文: "GraphRAG通过社区检测算法将知识图谱分层聚类..."
答案: "GraphRAG的主要优势是社区聚类和多跳推理能力,它由微软开发。"

分解答案claims:
1. GraphRAG优势是社区聚类 → ✅ 上下文有支撑
2. GraphRAG优势是多跳推理 → ❌ 上下文未提及
3. GraphRAG由微软开发 → ❌ 上下文未提及

Faithfulness = 1/3 = 0.33(3个claim中1个有支撑)

Answer Relevancy(答案相关性)

通过让LLM从答案反推问题,计算反推问题与原始问题的相似度:

# 原问题: "GraphRAG的主要优势是什么?"
# 答案: "GraphRAG的主要优势是社区聚类..."
# 
# LLM从答案反推可能的问题:
# 1. "GraphRAG有什么优势?"
# 2. "社区聚类有什么用?"
# 3. "为什么用GraphRAG?"
#
# 计算反推问题与原始问题的embedding相似度均值

RAGAS实战代码

安装与基础使用

pip install ragas==0.2.10
from ragas import evaluate
from ragas.metrics import (
    faithfulness,
    answer_relevancy,
    context_precision,
    context_recall,
    context_entity_recall,
    answer_similarity,
    answer_correctness,
)
from datasets import Dataset

# 准备评估数据
eval_data = {
    "question": [
        "GraphRAG的主要优势是什么?",
        "如何优化RAG系统的检索延迟?",
        "LoRA微调需要多少显存?",
    ],
    "answer": [
        "GraphRAG的主要优势是通过社区检测实现全局性问答,支持多跳推理...",
        "优化检索延迟可以从向量索引优化、缓存策略、模型量化三个方向入手...",
        "7B模型的LoRA微调大约需要16GB显存,使用QLoRA可降至8GB...",
    ],
    "contexts": [
        ["GraphRAG通过社区检测算法将知识图谱分层聚类..."],
        ["向量索引HNSW参数调优可以降低查询延迟..."],
        ["LoRA微调7B模型需要约16GB显存,QLoRA通过4bit量化减半..."],
    ],
    "ground_truth": [  # 可选,有标注时使用
        "GraphRAG的优势包括全局视角问答、多跳推理支持、可解释性高",
        "检索延迟优化包括HNSW参数调优、引入缓存、模型量化、异步检索",
        "7B模型LoRA需16GB显存,QLoRA需8GB",
    ],
}

dataset = Dataset.from_dict(eval_data)

# 执行评估
results = evaluate(
    dataset,
    metrics=[
        faithfulness,
        answer_relevancy,
        context_precision,
        context_recall,
        answer_correctness,
    ],
)

print(results)
# 输出示例:
# {'faithfulness': 0.87, 'answer_relevancy': 0.92, 
#  'context_precision': 0.85, 'context_recall': 0.78,
#  'answer_correctness': 0.83}

详细单条分析

# 查看每条数据的详细得分
import pandas as pd

df = results.to_pandas()
print(df[["question", "faithfulness", "answer_relevancy", 
           "context_precision", "context_recall"]])

# 输出:
#                            question  faithfulness  answer_relevancy  ...
# 0  GraphRAG的主要优势是什么?          0.95              0.91  ...
# 1  如何优化RAG系统的检索延迟?          0.78              0.88  ...
# 2  LoRA微调需要多少显存?              1.00              0.95  ...

构建持续评估Pipeline

离线评估Pipeline

import json
from datetime import datetime
from ragas import evaluate
from ragas.metrics import faithfulness, answer_relevancy, context_precision, context_recall

class RAGEvalPipeline:
    def __init__(self, rag_system, eval_dataset_path):
        self.rag = rag_system
        self.eval_data = self._load_eval_data(eval_dataset_path)
        self.history = []
    
    def _load_eval_data(self, path):
        with open(path, 'r', encoding='utf-8') as f:
            return json.load(f)
    
    def run_evaluation(self):
        """运行完整评估"""
        results = []
        
        for item in self.eval_data:
            # 执行RAG查询
            rag_result = self.rag.query(item["question"])
            
            # 收集结果
            results.append({
                "question": item["question"],
                "answer": rag_result["answer"],
                "contexts": [doc.page_content for doc in rag_result["source_documents"]],
                "ground_truth": item.get("ground_truth", ""),
            })
        
        # RAGAS评估
        dataset = Dataset.from_list(results)
        scores = evaluate(
            dataset,
            metrics=[faithfulness, answer_relevancy, 
                     context_precision, context_recall],
        )
        
        # 记录历史
        eval_record = {
            "timestamp": datetime.now().isoformat(),
            "total_questions": len(results),
            "scores": scores,
            "details": scores.to_pandas().to_dict(orient="records"),
        }
        self.history.append(eval_record)
        
        return eval_record
    
    def compare_with_baseline(self, eval_record):
        """与基线对比"""
        if not self.history:
            return "No baseline to compare"
        
        baseline = self.history[0]
        comparison = {}
        for metric, score in eval_record["scores"].items():
            base_score = baseline["scores"][metric]
            delta = score - base_score
            comparison[metric] = {
                "current": score,
                "baseline": base_score,
                "delta": delta,
                "improved": delta > 0,
            }
        return comparison

在线监控(A/B测试场景)

import random

class OnlineRAGMonitor:
    def __init__(self, sample_rate=0.1):
        self.sample_rate = sample_rate  # 采样率
        self.eval_buffer = []
    
    def maybe_evaluate(self, question, answer, contexts, user_feedback=None):
        """对线上查询进行采样评估"""
        if random.random() > self.sample_rate:
            return None
        
        # 异步评估,不影响线上延迟
        eval_result = {
            "question": question,
            "answer": answer,
            "contexts": contexts,
            "user_feedback": user_feedback,  # 👍/👎
            "timestamp": datetime.now().isoformat(),
        }
        self.eval_buffer.append(eval_result)
        
        # 累积100条后批量评估
        if len(self.eval_buffer) >= 100:
            self._batch_evaluate()
        
        return eval_result
    
    def _batch_evaluate(self):
        """批量执行RAGAS评估"""
        dataset = Dataset.from_list(self.eval_buffer)
        scores = evaluate(
            dataset,
            metrics=[faithfulness, answer_relevancy],
        )
        
        # 告警逻辑
        if scores["faithfulness"] < 0.8:
            self._send_alert("Faithfulness below threshold!", scores)
        
        # 清空buffer
        self.eval_buffer.clear()
        return scores

自定义评估指标

场景1:评估答案的结构化程度

from ragas.metrics.base import MetricWithLLM

class StructureScore(MetricWithLLM):
    """评估答案是否有清晰的结构(列表、标题、代码块等)"""
    
    name = "structure_score"
    
    def _score(self, row):
        answer = row["answer"]
        prompt = f"""评估以下回答的结构化程度(0-10):
        - 使用了列表/标题/代码块: +3
        - 逻辑层次清晰: +3
        - 有代码示例: +2
        - 格式整洁: +2
        
        回答: {answer}
        只返回数字。"""
        score = int(self.llm.invoke(prompt).strip())
        return score / 10.0

场景2:评估检索的多样性

class ContextDiversity(MetricWithLLM):
    """评估检索上下文的信息多样性,避免冗余"""
    
    name = "context_diversity"
    
    def _score(self, row):
        contexts = row["contexts"]
        if len(contexts) <= 1:
            return 1.0
        
        # 计算上下文间的两两相似度
        embeddings = [self.embeddings.embed(c) for c in contexts]
        similarities = []
        for i in range(len(embeddings)):
            for j in range(i+1, len(embeddings)):
                sim = cosine_similarity([embeddings[i]], [embeddings[j]])[0][0]
                similarities.append(sim)
        
        avg_sim = np.mean(similarities)
        # 相似度越低,多样性越高
        return max(0, 1 - avg_sim)

RAG benchmark数据集推荐

数据集领域大小特点
MultiHopRAG多跳推理2,500 Q需要多文档关联
RGB (RAG Benchmark)通用1,000 Q中英双语
CRUD-RAG中文通用3,000 Q中文RAG专项
FinanceBench金融1,500 Q专业领域
HotpotQA多跳问答5,000 Q经典数据集

评估结果解读指南

各指标的"健康范围"

指标危险警告健康优秀
Faithfulness<0.70.7-0.850.85-0.95>0.95
Answer Relevancy<0.60.6-0.750.75-0.9>0.9
Context Precision<0.50.5-0.70.7-0.85>0.85
Context Recall<0.60.6-0.750.75-0.9>0.9

指标诊断矩阵

Faithfulness低Answer Relevancy低诊断
LLM能力不足,换更强的模型
检索质量差,优化检索策略
LLM幻觉严重,降低temperature
检索+生成都需优化
Context Precision低Context Recall低诊断
检索完全失效,检查Embedding和索引
检索到了但精度不够,加Reranker
检索精度够但漏了信息,增加top_k
检索基本可用,微调即可

总结

RAG评估是RAG系统从"能跑"到"跑得好"的关键一步。2026年的最佳实践:

  1. RAGAS作为基线评估工具,四大核心指标覆盖检索和生成全链路
  2. 建立持续评估Pipeline,每次改动都量化评估影响
  3. 自定义指标补充,针对业务场景定制评估维度
  4. 关注指标趋势而非绝对值,持续优化比一次性达标更重要

加入讨论

这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。

碳基与硅基的智慧碰撞,认知差异创造无限可能。