课程0基础Agent开发课 / RAG与向量数据库 / RAG评估实战-用RAGAS量化检索与生成质量
— 18 min read

RAG评估实战-用RAGAS量化检索与生成质量

> **[进阶选读]** 本篇适合 RAG 系统已上线、需要系统评估和量化优化效果的读者。RAGAS 是目前最主流的 RAG 评估框架,学完本篇能对你的 RAG 系统做定量分析,而不是凭感觉判断好坏。

RAG 评估实战:用 RAGAS 量化检索与生成质量

[进阶选读] 本篇适合 RAG 系统已上线、需要系统评估和量化优化效果的读者。RAGAS 是目前最主流的 RAG 评估框架,学完本篇能对你的 RAG 系统做定量分析,而不是凭感觉判断好坏。


1.1 为什么需要专门的 RAG 评估

RAGAS评估指标体系
RAGAS 四大评估维度,全面量化 RAG 系统的检索与生成质量

传统软件工程的质量指标——响应延迟、吞吐量、错误率——对 RAG 系统同样重要,但远远不够。一个 RAG 系统可以在 200ms 内稳定返回答案,但这个答案可能是完全错误的、与文档不符的,或者根本没有回答用户的问题。

工程指标和质量指标之间存在根本性的差异:

指标类型 衡量什么 示例 局限
工程指标 系统运行效率 延迟、QPS、成本 无法判断答案质量
质量指标 答案内容好坏 准确性、相关性、完整性 需要专门的评估框架

评估 RAG 系统的质量困难之处在于:答案的"好坏"不是一个简单的布尔值。答案可能部分正确、过于冗长、包含幻觉内容、遗漏关键信息——每种问题都需要不同的度量方式。

RAGAS(Retrieval-Augmented Generation Assessment,检索增强生成评估)是目前最广泛使用的 RAG 评估框架,提供了四个针对性的指标,覆盖检索质量和生成质量两个维度。


1.2 RAGAS 四大核心指标详解

1.2.1 Faithfulness(忠实度)

衡量什么: 生成的答案中,每一个陈述是否都能从检索到的文档中找到依据。

评分方式: 将答案分解为独立的原子陈述,逐一验证每个陈述是否有文档支撑,最终得分 = 有支撑的陈述数 / 总陈述数。

低分意味着什么: 模型产生了幻觉(hallucination,指 AI 模型自信地生成了不实或未经文档支撑的内容,就像"凭空想象"出来的事实)——答案中包含文档未提及的内容。这通常是最严重的问题,会导致用户获得错误信息。

典型场景:

code
问题:公司退款政策的期限是多久?
检索文档:退款申请须在购买后 30 天内提交。
模型回答:您可以在 60 天内申请退款,无需提供购买凭证。
Faithfulness = 0("60天"和"无需凭证"均无文档支撑)

1.2.2 Answer Relevancy(答案相关性)

衡量什么: 生成的答案是否直接回答了用户的问题,而不是答非所问或过度冗长。

评分方式: 基于答案反向生成多个候选问题,计算这些候选问题与原始问题的语义相似度。高相关性意味着答案的内容与问题高度契合。

低分意味着什么: 答案虽然可能是事实正确的,但没有聚焦在用户真正关心的问题上。常见于答案过于宽泛,或者答案回答了相关但不同的问题。

典型场景:

code
问题:如何重置密码?
答案:我们的系统使用了行业领先的加密技术,支持多种登录方式,包括邮箱、手机号和第三方账号。安全是我们的首要考虑...
Answer Relevancy = 低(答案没有回答"如何重置密码")

1.2.3 Context Precision(上下文精确率)

衡量什么: 检索到的文档片段中,有多少是真正对回答问题有用的,而不是无关噪音。

评分方式: 对每个检索到的文档片段,判断它是否实际用于生成答案,计算有用片段占总检索片段的比例,并考虑排序(排名靠前的有用片段权重更高)。

低分意味着什么: 检索模块质量差,引入了太多噪音文档。噪音文档不仅浪费 token,还可能干扰模型生成准确答案。

1.2.4 Context Recall(上下文召回率)

衡量什么: 真正需要的信息是否都被检索到了,检索有没有遗漏关键文档。

评分方式: 将参考答案分解为多个陈述,判断每个陈述是否能从检索到的文档中找到支持,得分 = 有文档支持的陈述数 / 总陈述数。

低分意味着什么: 检索召回不足,向量数据库没有命中所需文档。可能是分块策略问题、嵌入模型不匹配,或者索引质量差。

需要 ground truth 答案:这个指标需要提供参考答案(ground truth),是唯一需要人工标注数据的指标。


1.3 快速上手:用 ragas 库跑评估

1.3.1 安装依赖

bash
pip install ragas langchain-openai datasets

1.3.2 准备评估数据集

RAGAS 需要三元组数据:问题(question)、上下文(contexts)、答案(answer),以及可选的参考答案(ground_truth)。

python
from datasets import Dataset

# 构建评估数据集
# 实际使用时,这些数据来自你的 RAG 系统的实际输出
eval_data = {
    "question": [
        "LangGraph 的 Send API 是什么?",
        "RAG 系统中的 Chunking 有什么作用?",
        "向量数据库和关系型数据库有什么区别?",
    ],
    "contexts": [
        # 每个问题对应一个列表,包含检索到的文档片段
        [
            "Send API 是 LangGraph 的动态并行机制,允许在运行时向节点派发任意数量的任务。",
            "Send 对象的第一个参数是目标节点名称,第二个参数是输入状态。",
        ],
        [
            "Chunking 是将长文档切分为较小片段的过程,直接影响检索的粒度和准确性。",
            "合理的 chunk 大小通常在 200-500 token 之间,需要在检索精度和上下文完整性之间取得平衡。",
        ],
        [
            "向量数据库专为高维向量的相似性搜索设计,支持 ANN(近似最近邻)算法。",
            "关系型数据库使用 SQL 进行精确查询,不支持语义相似性搜索。",
            "向量数据库适合语义搜索场景,关系型数据库适合结构化数据的精确查询。",
        ],
    ],
    "answer": [
        "Send API 是 LangGraph 的动态并行派发机制,允许在节点运行时生成任意数量的 Send 对象,每个 Send 对象触发目标节点的一次独立执行。",
        "Chunking 将长文档切分为小片段,使向量检索能够精确命中相关内容。chunk 大小影响检索粒度,过小会丢失上下文,过大会引入噪音。",
        "向量数据库专门为语义相似性搜索设计,使用 ANN 算法;关系型数据库用于结构化数据的精确查询。两者适用场景不同,不是竞争关系。",
    ],
    "ground_truth": [
        "Send API 允许在 LangGraph 节点内部动态创建 Send 对象列表,实现运行时的并行任务派发,是实现 Map-Reduce 模式的核心机制。",
        "Chunking 是 RAG 系统中将文档切分为可检索片段的过程,chunk 的大小和策略直接影响检索质量和最终答案的准确性。",
        "向量数据库针对高维向量相似性搜索优化,关系型数据库针对结构化数据精确查询优化,两者在 RAG 系统中各司其职。",
    ],
}

dataset = Dataset.from_dict(eval_data)

1.3.3 运行评估

python
from ragas import evaluate
from ragas.metrics import (
    faithfulness,
    answer_relevancy,
    context_precision,
    context_recall,
)
from langchain_openai import ChatOpenAI, OpenAIEmbeddings

# 配置评估使用的 LLM 和 Embedding 模型
# RAGAS 本身也需要 LLM 来进行指标计算
from ragas.llms import LangchainLLMWrapper
from ragas.embeddings import LangchainEmbeddingsWrapper

llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")

ragas_llm = LangchainLLMWrapper(llm)
ragas_embeddings = LangchainEmbeddingsWrapper(embeddings)

# 运行评估
results = evaluate(
    dataset=dataset,
    metrics=[
        faithfulness,
        answer_relevancy,
        context_precision,
        context_recall,
    ],
    llm=ragas_llm,
    embeddings=ragas_embeddings,
)

print(results)
# 输出示例:
# {'faithfulness': 0.92, 'answer_relevancy': 0.88,
#  'context_precision': 0.85, 'context_recall': 0.79}

1.3.4 查看详细结果

python
import pandas as pd

# 转换为 DataFrame,查看每个样本的详细得分
df = results.to_pandas()
print(df[["question", "faithfulness", "answer_relevancy",
          "context_precision", "context_recall"]].to_string())

1.4 如何构建评估数据集

评估数据集的质量直接决定评估结论的可信度。以下是三种构建方式:

1.4.1 方式一:从生产日志采样

python
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage

def collect_rag_outputs(rag_pipeline, questions: list[str]) -> list[dict]:
    """从 RAG 管道收集输入输出,构建评估样本"""
    samples = []
    for question in questions:
        # 假设 rag_pipeline 返回 (answer, source_documents)
        answer, source_docs = rag_pipeline.invoke(question)

        samples.append({
            "question": question,
            "answer": answer,
            "contexts": [doc.page_content for doc in source_docs],
            # ground_truth 需要人工标注,此处留空
            "ground_truth": None,
        })
    return samples

1.4.2 方式二:用 LLM 自动生成评估问题

python
def generate_eval_questions(documents: list[str], n_questions: int = 50) -> list[str]:
    """从文档库自动生成评估问题"""
    llm = ChatOpenAI(model="gpt-4o-mini")
    questions = []

    for doc in documents[:n_questions]:
        response = llm.invoke([
            HumanMessage(content=f"""基于以下文档内容,生成2个高质量的评估问题:
1. 问题应该能用文档中的信息直接回答
2. 问题应该是用户可能真实提出的
3. 只输出问题,每行一个,不加序号

文档内容:
{doc[:500]}""")
        ])
        new_questions = response.content.strip().split("\n")
        questions.extend([q.strip() for q in new_questions if q.strip()])

    return questions[:n_questions]

1.4.3 方式三:使用 RAGAS 内置的测试集生成

python
from ragas.testset import TestsetGenerator
from langchain_community.document_loaders import DirectoryLoader
from langchain_openai import ChatOpenAI, OpenAIEmbeddings

# 加载文档
loader = DirectoryLoader("./docs", glob="**/*.md")
documents = loader.load()

# 自动生成测试集(RAGAS 会自动生成问题和 ground truth)
generator = TestsetGenerator.from_langchain(
    generator_llm=ChatOpenAI(model="gpt-4o"),
    critic_llm=ChatOpenAI(model="gpt-4o"),
    embeddings=OpenAIEmbeddings(),
)

testset = generator.generate_with_langchain_docs(
    documents,
    test_size=50,
    distributions={"simple": 0.5, "reasoning": 0.25, "multi_context": 0.25},
)

eval_dataset = testset.to_dataset()

1.5 如何解读评估结果

以下是各指标得分区间的参考解读:

指标 得分区间 含义 优化方向
Faithfulness < 0.7 严重幻觉问题 检查 prompt,增加"只根据文档回答"的约束
Faithfulness 0.7-0.9 基本可用,有改进空间 优化 prompt 模板,考虑更强的模型
Faithfulness > 0.9 良好 保持
Answer Relevancy < 0.7 答案经常跑题 优化 prompt,增加"直接回答问题"的指令
Context Precision < 0.6 检索噪音过多 增加重排序(re-ranking)步骤,调整 Top-K
Context Recall < 0.7 检索遗漏关键信息 优化分块策略,调整嵌入模型,增大 Top-K

联合诊断:

code
高 Faithfulness + 低 Context Recall
→ 模型只用检索到的文档回答,但检索不完整
→ 优先优化检索模块(分块、嵌入模型、检索策略)

低 Faithfulness + 高 Context Recall
→ 所需信息都检索到了,但模型没有严格遵循文档
→ 优先优化 prompt,增加忠实度约束,考虑换更强的 LLM

低 Context Precision + 低 Answer Relevancy
→ 检索引入大量噪音,导致模型产生了不相关的答案
→ 优先优化检索质量,引入重排序

1.6 RAGAS 评估流程图

结果分析

RAGAS 指标计算

数据准备

answer + contexts

Faithfulness 低

Context Recall 低

Context Precision 低

Answer Relevancy 低

用户问题集

RAG 系统执行

评估样本
question / answer
contexts / ground_truth

Faithfulness
答案 → 拆分陈述 → 核查文档支撑

Answer Relevancy
答案 → 反推问题 → 语义相似度

Context Precision
每个 chunk 是否有用 → 加权精确率

Context Recall
ground_truth → 核查文档覆盖

得分汇总

诊断

优化 Prompt
增加忠实度约束

优化检索
分块/嵌入/Top-K

引入重排序
过滤噪音 chunk

调整 Prompt
聚焦问题回答


1.7 持续评估:集成到 CI/CD 流程

CI/CD(持续集成/持续交付)是软件开发中自动化测试和部署的工程实践,每次代码变更时自动运行测试,确保质量达标才能上线。

评估不应该是一次性的工作,而应该作为 RAG 系统迭代的质量门禁。

python
# eval_pipeline.py
import json
import sys
from datetime import datetime
from ragas import evaluate
from ragas.metrics import faithfulness, answer_relevancy, context_precision, context_recall
from datasets import Dataset

# 质量阈值配置
QUALITY_THRESHOLDS = {
    "faithfulness": 0.85,
    "answer_relevancy": 0.80,
    "context_precision": 0.75,
    "context_recall": 0.70,
}

def load_eval_dataset(path: str) -> Dataset:
    with open(path) as f:
        data = json.load(f)
    return Dataset.from_dict(data)

def run_evaluation(dataset_path: str) -> dict:
    dataset = load_eval_dataset(dataset_path)

    results = evaluate(
        dataset=dataset,
        metrics=[faithfulness, answer_relevancy, context_precision, context_recall],
    )

    scores = dict(results)
    return scores

def check_quality_gates(scores: dict) -> tuple[bool, list[str]]:
    """检查是否通过质量门禁"""
    failures = []
    for metric, threshold in QUALITY_THRESHOLDS.items():
        if metric in scores and scores[metric] < threshold:
            failures.append(
                f"{metric}: {scores[metric]:.3f} < threshold {threshold}"
            )
    return len(failures) == 0, failures

def save_results(scores: dict, output_path: str):
    """保存评估结果用于趋势追踪"""
    record = {
        "timestamp": datetime.now().isoformat(),
        "scores": scores,
    }
    with open(output_path, "a") as f:
        f.write(json.dumps(record, ensure_ascii=False) + "\n")

if __name__ == "__main__":
    dataset_path = sys.argv[1] if len(sys.argv) > 1 else "eval_dataset.json"

    print("Running RAGAS evaluation...")
    scores = run_evaluation(dataset_path)

    print("\nEvaluation Results:")
    for metric, score in scores.items():
        status = "PASS" if score >= QUALITY_THRESHOLDS.get(metric, 0) else "FAIL"
        print(f"  {metric}: {score:.3f} [{status}]")

    passed, failures = check_quality_gates(scores)
    save_results(scores, "eval_history.jsonl")

    if not passed:
        print("\nQuality gate FAILED:")
        for failure in failures:
            print(f"  - {failure}")
        sys.exit(1)  # 非零退出码,触发 CI 失败
    else:
        print("\nAll quality gates PASSED.")
        sys.exit(0)

在 GitHub Actions(GitHub 提供的 CI/CD 自动化工具,可以在代码推送时自动触发脚本执行)中集成:

yaml
# .github/workflows/rag-eval.yml
name: RAG Quality Evaluation

on:
  pull_request:
    paths:
      - 'rag/**'
      - 'prompts/**'
      - 'indexing/**'

jobs:
  evaluate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Setup Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.11'
      - name: Install dependencies
        run: pip install ragas langchain-openai datasets
      - name: Run RAG evaluation
        env:
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
        run: python eval_pipeline.py eval_dataset.json

这样每次修改检索逻辑或 prompt 模板时,CI 流程会自动运行评估,只有所有质量指标达标才允许合并代码。

本页目录