Chapter 09

RAG 评估与调优

用 RAGAS 框架系统评估 RAG 质量,诊断常见失败模式,建立持续改进的数据飞轮

为什么需要系统化评估

RAG 系统的质量很难凭直觉判断——你看到 LLM 给出了一段流畅的回答,但它可能是编造的,也可能是基于错误的文档片段。没有量化指标,优化就是在黑暗中摸索。

RAG 评估需要覆盖两个维度:

RAGAS 评估框架

RAGAS
RAG Assessment(检索增强生成评估)框架。用 LLM 自动评估 RAG 管道的各个维度,无需大量人工标注。核心四指标:忠实性、答案相关性、上下文精确率、上下文召回率。
忠实性(Faithfulness)
答案中每个陈述是否可以从检索到的上下文中推断出来。检测幻觉的核心指标。值域 [0, 1],越高越好,< 0.8 通常说明存在严重幻觉问题。
答案相关性(Answer Relevancy)
生成的答案与原始问题的相关程度。通过让 LLM 从答案反推多个可能的问题,计算这些问题与原始问题的语义相似度。
上下文精确率(Context Precision)
检索到的上下文中,有多少比例对生成正确答案是有用的。精确率低说明检索噪声多。
上下文召回率(Context Recall)
参考答案中的每个陈述,有多少比例可以在检索到的上下文中找到依据。召回率低说明重要信息没有被检索到。

RAGAS 快速上手

# pip install ragas langchain-openai
from ragas import evaluate
from ragas.metrics import (
    faithfulness,
    answer_relevancy,
    context_precision,
    context_recall,
    answer_correctness,
)
from datasets import Dataset
from langchain_openai import ChatOpenAI, OpenAIEmbeddings

# 准备评估数据集
# 格式:问题 + 检索到的上下文 + 生成答案 + 参考答案
eval_data = {
    "question": [
        "RAG 是什么?",
        "Qdrant 使用什么索引算法?",
        "文档分块的最佳实践是什么?",
    ],
    "contexts": [
        ["RAG(检索增强生成)由 Meta AI 于 2020 年提出,解决 LLM 的知识截止和幻觉问题..."],
        ["Qdrant 使用 HNSW(分层可导航小世界图)作为默认向量索引..."],
        ["推荐使用递归字符分块,chunk_size=1000,overlap=150..."],
    ],
    "answer": [
        "RAG 是检索增强生成,通过在生成前检索相关文档来减少幻觉。",
        "Qdrant 默认使用 HNSW 索引。",
        "最佳实践是使用递归分块,块大小 1000 字符,重叠 150 字符。",
    ],
    "ground_truth": [
        "RAG(检索增强生成)是 Meta AI 于 2020 年提出的技术,结合检索和生成来解决 LLM 幻觉问题。",
        "Qdrant 使用 HNSW(分层可导航小世界图)索引算法。",
        "推荐使用递归字符分块策略,通用起始参数是 chunk_size=1000, chunk_overlap=150。",
    ],
}

dataset = Dataset.from_dict(eval_data)

# 配置评估使用的模型
from ragas.llms import LangchainLLMWrapper
from ragas.embeddings import LangchainEmbeddingsWrapper

ragas_llm = LangchainLLMWrapper(ChatOpenAI(model="gpt-4o"))
ragas_emb = LangchainEmbeddingsWrapper(OpenAIEmbeddings())

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

print(result)
# 输出示例:
# {'faithfulness': 0.923,
#  'answer_relevancy': 0.951,
#  'context_precision': 0.875,
#  'context_recall': 0.912,
#  'answer_correctness': 0.887}

# 转 DataFrame 查看明细
df = result.to_pandas()
print(df[["question", "faithfulness", "answer_relevancy"]])

自动构建评估数据集

手工标注问答对成本高昂。RAGAS 提供了 Testset Generator,可以从文档自动生成高质量的评估集。

from ragas.testset import TestsetGenerator
from ragas.testset.evolutions import simple, reasoning, multi_context

generator = TestsetGenerator.with_openai(
    generator_llm="gpt-4o",
    critic_llm="gpt-4o",
    embeddings="text-embedding-3-small",
)

# 从文档生成测试集
testset = generator.generate_with_langchain_docs(
    documents=docs,
    test_size=50,          # 生成 50 个问答对
    distributions={
        simple: 0.5,       # 50% 简单问题
        reasoning: 0.25,   # 25% 推理型问题
        multi_context: 0.25 # 25% 需要多文档的问题
    }
)

testset_df = testset.to_pandas()
print(testset_df.head())
testset_df.to_csv("rag_testset.csv", index=False)

常见失败模式与诊断

失败模式 症状(指标) 根本原因 解决方案
低忠实性 Faithfulness < 0.7 LLM 未遵循上下文,自行编造 强化 Prompt 指令;降低 temperature;减少上下文噪声
低上下文精确率 Context Precision < 0.7 检索噪声多,无关文档被纳入上下文 提高相似度阈值;加入 Reranker;改进分块策略
低上下文召回率 Context Recall < 0.7 相关文档未被检索到 增大 top_k;启用混合检索;查询改写扩展覆盖
低答案相关性 Answer Relevancy < 0.8 答案偏离问题,答非所问 改进 Prompt 模板;检查问题理解逻辑
空检索 No context found 查询与文档语义距离远;文档未正确索引 降低相似度阈值;检查文档加载;启用查询改写

端到端评估流水线

import pandas as pd
from tqdm import tqdm

def run_rag_pipeline(question: str) -> dict:
    """运行 RAG 系统并收集中间结果"""
    # 检索
    retrieved_docs = retriever.invoke(question)
    contexts = [doc.page_content for doc in retrieved_docs]

    # 生成
    answer = rag_chain.invoke(question)

    return {
        "question": question,
        "contexts": contexts,
        "answer": answer,
    }

# 批量运行评估集
testset_df = pd.read_csv("rag_testset.csv")
results = []

for _, row in tqdm(testset_df.iterrows(), total=len(testset_df)):
    result = run_rag_pipeline(row["question"])
    result["ground_truth"] = row["ground_truth"]
    results.append(result)

# 用 RAGAS 评估
eval_dataset = Dataset.from_list(results)
scores = evaluate(
    eval_dataset,
    metrics=[faithfulness, answer_relevancy, context_precision, context_recall]
)

# 保存报告
report_df = scores.to_pandas()
report_df.to_csv("rag_evaluation_report.csv", index=False)

# 打印摘要
print("\n=== RAG 评估报告 ===")
print(f"忠实性:      {scores['faithfulness']:.3f}")
print(f"答案相关性:  {scores['answer_relevancy']:.3f}")
print(f"上下文精确率:{scores['context_precision']:.3f}")
print(f"上下文召回率:{scores['context_recall']:.3f}")

RAG 调优策略矩阵

调优目标 优先尝试 进阶方案
提升召回率 增大 top_k、降低相似度阈值 混合检索(BM25+向量)、查询改写、多查询融合
提升精确率 提高相似度阈值、加 Reranker 父子分块、改进 Embedding 模型、元数据过滤
减少幻觉 强化 Prompt("只基于上下文")、降低 temperature 引用追踪验证、使用更强 LLM、减少上下文噪声
提升速度 向量量化、减小 top_k 异步并发检索、缓存高频查询结果
降低成本 换小 LLM(GPT-4o-mini)、减少 top_k 本地 Embedding 模型、语义缓存(Semantic Cache)

语义缓存(Semantic Cache)

对相似的查询缓存结果,避免重复调用 LLM,大幅降低成本和延迟。

from langchain.globals import set_llm_cache
from langchain_community.cache import RedisSemanticCache

# 语义缓存:相似问题命中缓存(不需要完全一致)
set_llm_cache(RedisSemanticCache(
    redis_url="redis://localhost:6379",
    embedding=OpenAIEmbeddings(),
    score_threshold=0.95,  # 相似度超过 0.95 才命中缓存
))

# 第一次查询:调用 LLM,存入缓存
r1 = rag_chain.invoke("HNSW 索引是什么?")

# 第二次相似查询:直接命中缓存,不调用 LLM
r2 = rag_chain.invoke("HNSW 是什么索引?")  # 命中缓存!

print("缓存命中,节省一次 LLM 调用")
持续改进飞轮

建立 RAG 系统的数据飞轮:用户反馈(踩/赞) → 识别失败案例 → RAGAS 专项评估 → 定位问题组件 → 针对性优化 → A/B 测试验证 → 上线新版本 → 继续收集反馈。每一轮迭代都应该有量化指标支撑,避免凭感觉改。

本章总结