为什么"感觉不错"不是标准?
前面七篇文章,我们搭起了一整套 RAG 流程:分块、Embedding、向量库、检索策略。系统跑起来了,你问它几个问题,回答看起来"还不错"。
但问题接踵而至:
- 迭代后真的变好了吗? 你换了 Embedding 模型、调了 chunk_size、加了 MMR,但回答质量真的提升了吗?还是只是"感觉"变好了?
- 问题出在哪里? 某个问题回答得很差,是检索阶段 没召回相关文档,还是生成阶段模型在胡说八道?
- 怎么向老板汇报? "我觉得我们的 RAG 系统挺好的"------这句话在数据驱动的团队里毫无说服力。
RAG 系统的评估,不能靠感觉,必须靠数据。
本文会带你从零开始,用 RAGAS 框架建立一套可量化的 RAG 评估体系,让你清楚地知道系统好不好、哪里差、怎么改。
RAGAS 是什么?
RAGAS(Retrieval-Augmented Generation Assessment)是专为 RAG 系统设计的开源评估框架。它的核心思想很朴素:用 LLM 作为裁判,自动判断 RAG 系统的输出质量。
为什么用 LLM 当裁判?因为传统的 NLP 评估指标(如 BLEU、ROUGE)只适合做翻译或摘要任务,它们通过字符串匹配来判断相似度,完全无法理解语义。而 RAG 的评估需要理解"这个答案是否基于上下文"、"这个回答有没有答非所问"------这正是 LLM 擅长的。
RAGAS 提出了 4 个核心指标,覆盖了 RAG 系统的两个关键阶段(检索 + 生成):
四个核心指标详解
1. Faithfulness(忠实度)
问题:答案有没有在胡说八道?
Faithfulness 衡量生成答案是否忠实于检索到的上下文。如果模型在回答中加入了上下文里没有的信息,就是"幻觉",Faithfulness 就会低。
通俗理解:考试时Faithfulness高 = 答案全是根据提供的参考资料写的,没有自己瞎编。
计算方式:LLM 被prompt要求逐句检查答案中的每个陈述,判断是否能从上下文中推断出来。可推断的陈述数 / 总陈述数 = Faithfulness。
2. Answer Relevancy(答案相关性)
问题:答案是不是在答非所问?
Answer Relevancy 衡量答案与问题的相关程度。即使答案内容是对的,但如果偏离了问题的核心,这个指标也会低。
通俗理解:你问"怎么学 Python",对方却给你讲了一通 Java 的历史------虽然内容本身没错,但完全跑题了。
计算方式:LLM 根据答案生成若干个问题变体,然后计算这些问题与原始问题的 Embedding 相似度,取平均。
3. Context Precision(上下文精确度)
问题:检索回来的东西里,有多少是垃圾?
Context Precision 衡量检索结果中相关文档片段的比例。如果召回的 4 条上下文里有 2 条完全不相关,Context Precision 就是 0.5。
通俗理解:去图书馆找资料,借了 4 本书,只有 2 本有用------你的检索精确度就是 50%。
计算方式:LLM 逐条判断每个上下文片段是否与问题相关,相关条数 / 总条数。
4. Context Recall(上下文召回率)
问题:该找的东西找到了吗?
Context Recall 衡量所有与问题相关的信息,有多少被成功检索到了。这是检索阶段最核心的指标。
通俗理解:考试复习时,考卷上有 10 个知识点,你的复习资料只覆盖了 6 个------召回率就是 60%。
计算方式:LLM 将标准答案(ground_truth)拆分成多个关键陈述,然后逐条判断这些陈述是否能从检索到的上下文中推断出来。能推断的陈述数 / 总陈述数 = Context Recall。
四个指标的关系
bash
用户提问
├─→ Context Recall 低? → 检索阶段有问题(chunk/embedding/top-k)
├─→ Context Precision 低? → 检索混入了噪声
├─→ Faithfulness 低? → 生成阶段幻觉(上下文不足或模型不听话)
└─→ Answer Relevancy 低? → 答案跑题了
这四个指标互相独立、互相补充,一起构成了 RAG 系统的"体检报告"。
评估的第一步:构建测试集
评估需要"考题"和"参考答案"。测试集的质量直接决定评估结果的可信度。
测试集长什么样?
一个标准的 RAG 测试样本包含四个字段:
json
{
"question": "RAGAS 框架包含哪四个核心评估指标?",
"ground_truth": "RAGAS 四个核心指标是:Faithfulness、Answer Relevancy、Context Precision、Context Recall。",
"contexts": ["...", "..."],
"answer": "..."
}
question:用户问题ground_truth:标准答案(人工编写,不依赖模型)contexts:RAG 系统检索到的上下文(运行后自动填充)answer:RAG 系统生成的答案(运行后自动填充)
两种构建方式
| 方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 手工标注 | 质量高、边界清晰 | 成本高、耗时长 | 核心测试集、生产验收 |
| LLM 生成 | 效率高、可规模化 | 需人工抽检校正 | 快速构建、扩充覆盖面 |
本文的测试数据
我们用 8 篇 RAG 相关的技术文章作为知识库(涵盖向量数据库、Embedding、分块策略、混合检索等主题),并为每篇手工编写了 1 个 QA 对:
json
[
{"question": "什么是 RAG 技术,它主要解决什么问题?", "ground_truth": "RAG..."},
{"question": "企业级应用应该选择哪种向量数据库?", "ground_truth": "Qdrant 和 Weaviate..."},
{"question": "中文场景应该选择哪个 Embedding 模型?", "ground_truth": "BGE 系列..."},
...
]
完整数据见
data/knowledge_base.json和data/manual_testset.json
代码实战(一):搭建可评估的 RAG 系统
在评估之前,我们需要一个可配置的 RAG Pipeline。为什么要可配置?因为我们要对比不同参数(chunk_size、top_k)下的评估结果。
rag_pipeline.py 核心结构
python
class RAGPipeline:
def __init__(self, chunk_size=512, chunk_overlap=50, top_k=4):
self.chunk_size = chunk_size
self.chunk_overlap = chunk_overlap
self.top_k = top_k
def build_index(self, docs, force_rebuild=False):
# 1. 切分文档
splitter = RecursiveCharacterTextSplitter(
chunk_size=self.chunk_size,
chunk_overlap=self.chunk_overlap,
separators=["\n\n", "\n", "。", ";", " ", ""],
)
chunks = splitter.split_documents(docs)
# 2. 构建向量索引
self.vectorstore = Chroma.from_documents(
documents=chunks, embedding=embeddings
)
# 3. 组装 LCEL Chain
self.chain = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| prompt | llm | StrOutputParser()
)
def query(self, question):
contexts = self.retriever.invoke(question)
answer = self.chain.invoke(question)
return {"question": question, "answer": answer, "contexts": contexts}
关键设计:所有影响质量的参数都暴露为构造函数的参数,这样我们可以在诊断实验中轻松切换配置。
代码实战(二):运行 RAGAS 评估
evaluate.py 评估流程
python
def main():
# 1. 加载 8 条手工测试数据
testset = load_testset("./data/manual_testset.json")
# 2. 构建 RAG Pipeline(默认配置:chunk=512, overlap=50, top_k=4)
pipeline = RAGPipeline(chunk_size=512, chunk_overlap=50, top_k=4)
pipeline.build_index(force_rebuild=True)
# 3. 对每条测试数据执行 RAG,收集 answer 和 contexts
dataset = prepare_dataset(pipeline, testset)
# dataset 格式:{question, answer, contexts, ground_truth}
# 4. 运行 RAGAS 评估
result = evaluate(
dataset=dataset,
metrics=[faithfulness, answer_relevancy, context_precision, context_recall],
llm=ragas_llm, # 评估用的裁判 LLM
embeddings=ragas_emb, # 评估用的 Embedding
)
# 5. 打印并保存报告
print_report(result, dataset)
save_report(result, dataset, "./evaluation_report.json")
注意:评估用的 LLM 和 Embedding 可以与 RAG 系统本身使用的不同。RAG 系统可以用轻量级模型(如 glm-4-flash),而评估裁判可以用更强的模型(如 GPT-4)来获得更准确的判断。不过在实际项目中,为了节省成本,通常用同一个模型。
实际运行结果
bash
$ python evaluate.py
输出:
yaml
============================================================
RAGAS 评估报告
============================================================
📊 总体得分:
faithfulness : 0.792 ███████████████
answer_relevancy : 0.406 ████████
context_precision : 0.583 ███████████
context_recall : 0.625 ████████████
平均得分 : 0.602
📋 逐题明细:
# Question Faith AnsRel CtxPre CtxRec
---------------------------------------------------------------
1 什么是 RAG 技术... 1.00 0.68 1.00 1.00
2 企业级应用应该选择哪种... 0.33 0.09 0.33 0.00
3 中文场景应该选择哪个... 1.00 0.78 1.00 1.00
4 文档分块时 Chunk Size... 1.00 0.86 0.50 1.00
5 RAGAS 框架包含哪四个... 1.00 0.64 1.00 1.00
6 RRF 融合算法的公式是... 0.00 0.00 0.00 0.00
7 HyDE 查询优化技术的... 1.00 0.13 0.00 0.00
8 生产级 RAG 系统如何... 1.00 0.07 0.83 1.00
⚠️ 最差指标: answer_relevancy (0.406)
============================================================
结果解读
看到这个报告,我们能立即定位问题:
- Faithfulness 较高(0.792):说明模型基本能基于检索到的上下文回答,不太会凭空编造。
- Answer Relevancy 很低(0.406):这是最大的问题!说明很多答案虽然基于上下文,但没有准确回答问题的核心。
- Context Recall(0.625)和 Context Precision(0.583)中等:检索阶段有提升空间。
- 第 6 题全面崩溃(0/0/0/0):问的是 RRF 公式,但系统完全没有召回相关文档------这是典型的检索失败案例。
没有这份报告,你只会觉得"第 6 题回答得不太好"。有了报告,你精确知道是检索出了问题,而不是生成出了问题。
代码实战(三):诊断实验------好配置 vs 差配置
评估最大的价值不是打分,而是诊断。下面我们故意制造一个"差配置",看看 RAGAS 能不能发现问题。
diagnose.py 设计思路
| 参数 | 好配置 | 差配置 | 预期问题 |
|---|---|---|---|
| chunk_size | 512 | 128 | 上下文碎片化,语义断裂 |
| chunk_overlap | 50 | 0 | 边界信息丢失 |
| top_k | 4 | 2 | 召回不足,遗漏相关信息 |
python
# 好配置
good = RAGPipeline(chunk_size=512, chunk_overlap=50, top_k=4)
# 差配置:chunk 太小 + 无重叠 + 只召回 2 条
bad = RAGPipeline(chunk_size=128, chunk_overlap=0, top_k=2)
诊断对比结果
运行 python diagnose.py:
diff
============================================================
诊断对比:好配置 vs 差配置
============================================================
指标 好配置 差配置 差异 诊断
--------------------------------------------------------------------
faithfulness 0.830 0.750 +0.080 ✓ 正常
answer_relevancy 0.502 0.191 +0.312 ✗ 严重
context_precision 0.583 0.375 +0.208 ⚠ 警告
context_recall 0.625 0.250 +0.375 ✗ 严重
--------------------------------------------------------------------
平均得分 0.635 0.391 +0.244
============================================================
📋 诊断结论:
→ Context Recall 显著下降:检索阶段有问题(chunk 太小 / top-k 太少)
→ Context Precision 显著下降:检索结果中混入了噪声
→ Answer Relevancy 显著下降:答案偏离了问题
诊断结论分析
| 下降指标 | 差配置数值 | 诊断 |
|---|---|---|
| Context Recall ↓↓ | 0.625 → 0.250 | chunk 太小导致语义断裂,top_k=2 导致大量相关信息未被召回 |
| Context Precision ↓ | 0.583 → 0.375 | 碎片化后,低质量片段更容易被错误地匹配到查询 |
| Answer Relevancy ↓↓ | 0.502 → 0.191 | 上下文不足 → 模型只能基于有限信息回答 → 答案偏离问题 |
| Faithfulness ↓ | 0.830 → 0.750 | 上下文碎片化后,模型有时不得不"脑补"来填补信息缺口 |
这个实验完美展示了 RAGAS 的诊断能力 :不是笼统地说"差配置更差",而是精确告诉你哪个环节 出了问题、严重到什么程度。
完整代码
本文的完整代码已开源,包含:
rag_pipeline.py--- 可配置的 RAG Pipelineevaluate.py--- RAGAS 评估主脚本diagnose.py--- 好配置 vs 差配置诊断实验generate_qa.py--- LLM 自动生成测试集data/knowledge_base.json--- 8 篇知识库文档data/manual_testset.json--- 8 条手工测试数据
源码地址:
适用场景与注意事项
什么时候需要做 RAGAS 评估?
- 系统迭代后:更换 Embedding 模型、调整分块策略、升级 LLM 后,跑一遍评估看指标变化
- 上线前验收:建立 baseline,确保系统达到可接受的质量门槛
- 问题排查:用户投诉回答质量差时,用指标定位是检索问题还是生成问题
注意事项
- 评估成本不低:4 个指标 × N 条测试数据,每条都要调多次 LLM。8 条数据大约需要 3-5 分钟、几百次 API 调用。生产环境建议用异步批量处理。
- ground_truth 质量决定评估上限:如果参考答案本身写得不准确,Context Recall 等指标就会失真。
- 裁判模型与业务模型可以不同:评估可以用更强的模型(如 GPT-4),RAG 系统本身用轻量模型(如 glm-4-flash),这样评估更准确且成本可控。
- 定期更新测试集:知识库更新后,测试集也应同步更新,覆盖新增领域。
小结
本文带你从零建立了一套 RAG 评估体系:
- 为什么评估:"感觉不错"不可量化,迭代后不知道变好变坏
- RAGAS 四大指标:Faithfulness(防幻觉)、Answer Relevancy(防跑题)、Context Precision(减噪声)、Context Recall(保召回)
- 测试集构建:手工标注为主,LLM 生成为辅
- 代码实战:完整的 RAG Pipeline + RAGAS 评估 + 诊断对比实验
- 真实数据:好配置平均 0.635,差配置平均 0.391,差距一目了然
关键认知:RAG 优化不能拍脑袋。先用 RAGAS 跑一遍评估,找到最差的指标,再针对性优化------这是最高效的改进路径。