RAG 评测从入门到实践:一份手把手教学文档
适用对象:刚接触 RAG(检索增强生成)系统、需要搭建评测体系的算法/后端工程师 前置知识:了解 RAG 基本流程(检索 + 生成)即可,不要求有评测经验 学习目标:读完后能独立设计一套评测集、写出一个简单的裁判打分器,并看懂企业级评测系统的整体架构

一、先讲一个场景,搞懂"为什么需要评测"
假设你花了两周做了一个企业内部的客服 RAG 系统,接入了知识库,能回答问题了。你把 demo 演示给老板看,老板问了三个问题:
- "这系统准确率多少?"
- "上周你改了检索逻辑,效果是变好了还是变差了?"
- "如果它编造事实误导了客户,怎么保证不会发生?"
你发现自己答不上来——因为你只是"感觉好像还行",测试方式是自己在前端随便问几个问题看看回答对不对。这种方式有三个致命问题:
- 不可复现:今天你测了10个问题觉得不错,明天换个人测10个不同的问题可能觉得很差,没有统一标准。
- 改了这里、坏了那里,发现不了:优化了A类问题的回答,可能悄悄把B类问题的准确率搞低了,靠人工肉眼根本发现不了。
- 无法量化,没法向上汇报,也没法当作上线的门槛。
这就是评测体系存在的意义:把"感觉还行"变成"客观数字",把"随便测测"变成"可重复、可对比、可追溯"的工程流程。
二、基本概念:先搞清楚几个术语
在动手之前,先把几个反复会用到的概念讲清楚。
1. RAG 的两层结构
RAG 系统本质上分两层,评测也要按这两层来做,这是最重要的一个认知:
用户提问
│
▼
【检索层 Retrieval】:从知识库里找出相关的文档片段(chunk)
│
▼
【生成层 Generation】:大模型基于检索到的片段,生成最终回答
│
▼
返回给用户如果最终回答错了,可能是两种原因造成的:检索层没找对材料(巧妙做饭但食材不对),或者生成层没用好材料(食材是对的但做糊了)。评测体系必须能区分这两种情况,否则你永远不知道该优化检索还是优化生成。
2. 什么是 Golden Dataset(金标准测试集)
一份提前准备好的"标准答案集",每条包含:问题、标准答案、这个答案应该来自哪个文档。它是评测的"考卷",没有这个,后面所有指标都无从算起。最简单的一条大概长这样:
{
"question": "退货政策是几天内可以无理由退货?",
"ground_truth": "7天内无理由退货,需保持商品完好",
"source_file": "退换货政策.md"
}3. 什么是 LLM-as-a-Judge(大模型当裁判)
人工一条条看回答对不对,效率太低;用传统的字符串匹配(比如判断两句话像不像)又理解不了语义。所以业内的通行做法是:再找一个大模型,给它一套打分标准(Rubric),让它扮演"裁判",去比对系统的回答和标准答案,输出一个分数和理由。
打个比方:这就像批改作文,与其你自己一篇篇精读打分,不如请一位经验丰富的语文老师(裁判模型),按照统一的评分标准去批改,速度快很多,而且标准统一。
4. 什么是 Rubric(打分标尺)
Rubric 就是给裁判模型的"评分说明书"。没有它,裁判打分会很随意。一个好的 Rubric 长这样(以"忠实度"这个指标为例):
1.0 分:回答中的所有事实都能在参考资料里找到依据,没有编造
0.5 分:回答基本忠实,但补充了一些参考资料没提到的常识性内容
0.0 分:回答中包含参考资料没有依据的关键事实,属于编造/幻觉记住一句话:Rubric 越具体,裁判打分越稳定;Rubric 越模糊(比如只说"根据忠实度打0-1分"),裁判打分的随机性越大。
三、动手第一步:设计你的评测指标
不要一上来就想着做6个指标、8个指标,先理解每个指标"测的是什么问题",再决定要不要加。
检索层指标(客观计算,不需要大模型)
Hit@K:前K个召回的片段里,有没有包含标准答案所在的文档?有就是1,没有就是0。这是最基础的指标,理解起来像"抽奖有没有中"。
# 伪代码:判断 Hit@3
def hit_at_k(retrieved_docs, ground_truth_source, k=3):
top_k = retrieved_docs[:k]
return 1.0 if ground_truth_source in top_k else 0.0MRR(平均倒数排位):不只关心"有没有中",还关心"排第几"。排第1名给1分,第2名给0.5分,第3名给0.33分——排名越靠后,分数衰减越快。
# 伪代码:计算 MRR
def mrr(retrieved_docs, ground_truth_source):
for rank, doc in enumerate(retrieved_docs, start=1):
if doc == ground_truth_source:
return 1.0 / rank
return 0.0新手常踩的坑:知识库里存的文件名和检索返回的引用格式经常对不上(比如一个叫 01_退换货政策.md,检索返回的却是 退换货政策),直接用 == 判断相等会导致大量"假阴性"(明明中了却被判定没中)。解决办法是先把文件名统一做标准化处理(去掉编号前缀、去掉扩展名)再比较。
生成层指标(需要 LLM 裁判)
忠实度(Faithfulness):回答是不是"照着材料说话",有没有编造。这是最重要的一个指标,因为幻觉是 RAG 系统最大的风险。
准确度(Correctness):回答和标准答案对不对得上。
安全合规(Safety):有没有说出不该说的话(比如误导性的操作建议)。
初学者建议:一开始不要贪多,先把 Hit@K、忠实度、准确度这三个指标跑通,能看懂数字变化是怎么回事之后,再逐步加安全性、相关性等更多维度。
四、动手第二步:写一个最简单的裁判打分器
理解了 Rubric 之后,我们来写一个最简化版的忠实度裁判,帮助你理解整个链路是怎么跑起来的。
FAITHFULNESS_PROMPT = """
你是一个严格的质检员。请根据下面的【参考资料】判断【系统回答】是否忠实(没有编造事实)。
【参考资料】
{context}
【系统回答】
{answer}
打分规则:
1.0 分:回答完全基于参考资料,没有编造
0.5 分:基本忠实,但有轻微的常识性补充
0.0 分:包含参考资料中没有依据的关键事实
请只输出如下 JSON 格式,不要输出其他任何内容:
{{"score": 分数, "reasoning": "打分理由"}}
"""
def judge_faithfulness(context, answer, llm_client):
prompt = FAITHFULNESS_PROMPT.format(context=context, answer=answer)
response = llm_client.chat(prompt)
return parse_json(response) # 解析出 score 和 reasoning这里有个新手容易忽略的细节:大模型的输出不总是干净的 JSON,有时候会带 ```json 代码块标记,有时候会在前面加一句解释。所以实际生产代码里,parse_json 这一步通常要做"多级兜底":
import re, json
def parse_json(text):
# 第一步:尝试直接解析
try:
return json.loads(text)
except Exception:
pass
# 第二步:提取 ```json ... ``` 代码块
match = re.search(r"```(?:json)?\s*(\{.*?\})\s*```", text, re.DOTALL)
if match:
try:
return json.loads(match.group(1))
except Exception:
pass
# 第三步:提取第一个 { 到最后一个 } 之间的内容
start, end = text.find("{"), text.rfind("}")
if start != -1 and end > start:
try:
return json.loads(text[start:end + 1])
except Exception:
pass
return None # 都失败了,标记为解析失败,人工兜底这段代码看似不起眼,但在实际项目里极其重要——如果没有兜底逻辑,可能有5%~10%的裁判打分因为格式问题直接解析失败,白白浪费调用成本。
五、动手第三步:把单题评测串成完整流程
有了 Hit@K/MRR 的计算函数和裁判函数,接下来把它们串起来,对一整份测试集跑一遍:
async def evaluate_one_case(case, retriever, generator, judge):
# 1. 检索
contexts = retriever.search(case["question"], top_k=3)
hit = hit_at_k(contexts, case["source_file"])
rr = mrr(contexts, case["source_file"])
# 2. 生成
answer = generator.generate(case["question"], contexts)
# 3. 裁判打分(忠实度 + 准确度可以并行调用,节省时间)
faith_score = await judge.faithfulness(contexts, answer)
correct_score = await judge.correctness(case["ground_truth"], answer)
return {
"id": case["id"],
"hit@3": hit,
"mrr": rr,
"faithfulness": faith_score,
"correctness": correct_score,
}
async def run_evaluation(dataset, retriever, generator, judge):
results = []
for case in dataset:
result = await evaluate_one_case(case, retriever, generator, judge)
results.append(result)
return results新手常见的下一个问题:测试集有几十上百道题,一道道跑很慢。生产环境通常会用并发(比如 asyncio.gather + 信号量限流)来提速,但一开始学习阶段,建议先用最简单的串行循环把流程跑通,再考虑并发优化——先追求"跑得对",再追求"跑得快"。
六、看懂结果之后:怎么用评测结果指导优化
跑完一轮评测,你会拿到一堆分数。新手常犯的错误是:看到某个指标低,就直接开始瞎改(比如看到准确度低就疯狂加长 Prompt)。正确的做法是先做"归因",再动手:
归因的基本思路
拿到一条低分 case,按下面的顺序排查:
- 先看 Hit@K:是不是等于0?如果是,说明问题出在检索层,生成层再怎么优化都没用,得先去查检索逻辑(是不是切片切得不好、向量库配置有问题)。
- Hit@K 是1,但 MRR 很低:说明检索到了但排序靠后,可以考虑加 rerank 模型,或者调整检索的 top_k。
- Hit@K 和 MRR 都正常,但忠实度低:说明生成层出了问题,可能是 Prompt 没有明确要求"只依据参考资料回答"。
- 忠实度正常,但准确度低:可能是回答遗漏了关键细节(比如漏了一个数值、一个步骤),这种情况通常是 Prompt 里没有强调"完整保留关键信息"。
一个真实的调优例子(帮助理解这个排查过程)
假设有道题问"CAN总线故障如何排查",标准答案里包含"电阻60Ω,如果测得120Ω说明一端开路"这个关键判断依据。系统的回答只说了"电阻正常应为60Ω",漏掉了"120Ω代表一端开路"这个判断技巧。
- 排查:Hit@3=1(检索对了材料),忠实度=1.0(没有编造),但准确度裁判打了0.5分,理由是"遗漏关键判定依据"。
- 归因:属于上面第4种情况——生成层遗漏了细节,不是检索问题。
- 优化动作:在 Prompt 里加一条明确要求——"参考资料中的判定依据和具体数值必须完整保留,不能概括省略"。
- 复测:这道题的准确度分数从0.5涨到1.0。
这个例子想说明的核心道理是:优化动作永远要能对应到具体的归因结论,而不是凭感觉去改 Prompt。归因做得越细,优化就越有针对性,避免"改了这里、坏了那里"。
七、进阶一步:企业级场景要多想的几件事
当你把上面的流程跑通之后,如果要把这套评测体系用到真实企业项目里,还需要多考虑几点(这里先给概念,不展开细节,作为你后续学习的路标):
- 测试集不能只有一份,要分层:固定不变的"回归测试集"(每次改动必跑)、专门收集难题的"难例集"、专测安全底线的"红线集",以及定期从真实用户提问里抽样的"生产采样集"。
- 裁判模型自己也要被检验:定期抽样对比裁判打分和人工打分是否一致,防止裁判模型本身"看走眼"。
- 版本要能追溯:每次跑评测,要记录清楚当时用的是哪个版本的代码、Prompt、知识库、模型,分数变化了才知道是谁的锅。
- 要能接入上线流程:设定一个明确的门槛(比如"忠实度≥0.95 且安全违规=0 才允许上线"),让评测系统从"事后分析工具"变成"自动化质量关卡"。
八、动手实操清单(学完就做)
按顺序完成,每一步都能验证上一步的理解:
- [ ] 准备10道测试题,包含 question / ground_truth / source_file 三个字段
- [ ] 写一个
hit_at_k函数并手动验证3个 case - [ ] 写一个
mrr函数并手动验证3个 case - [ ] 写一个忠实度裁判 Prompt,用你的大模型 API 跑通一次调用
- [ ] 给裁判打分器加上 JSON 解析兜底逻辑,故意构造一个"脏输出"测试兜底是否生效
- [ ] 把上面所有函数串成一个完整的
run_evaluation循环,跑完10道题拿到一份分数表 - [ ] 挑一道低分题,按"检索→排序→忠实度→准确度"的顺序做一次归因,写出你认为的问题原因
- [ ] 针对归因结论修改一次 Prompt,重新跑这道题,验证分数是否变化
完成这份清单之后,你已经掌握了 RAG 评测体系里最核心的思维方式:先分层定位问题,再针对性优化,用可重复的数字代替"感觉"。这也是后续学习企业级评测平台架构(并发调度、可观测性、版本管理)的基础。
