你的 RAG 到底好不好?评测指标全解析

周五下午六点,上线前的最后一轮测试。老李坐在小王工位旁边,看他敲完最后一条测试用例。

“行了,”老李拧开保温杯,语气里带着即将周末的松弛,“我随机抽了二十个问题,回答都挺靠谱的。系统没问题了,下周一上线。”

小王没接话,而是把屏幕往老李那边转了转:“老李,你看看这条。”

屏幕上一个问答记录赫然在目——用户问“公司现任 CEO 是谁”,系统回答“公司现任 CEO 是张伟先生,他于 2019 年接任”。老李的脸立刻僵了。

“张伟是三年前的 CEO!现在的 CEO 是刘总!”老李把保温杯往桌上一顿,“这怎么回事?我们知识库里不是有最新的组织架构文件吗?”

“文件有,也检索到了。但大模型读完之后,把文件里提到的‘张伟曾任 CEO’和‘刘洋现任 CEO’搞混了,自己编了一个看似合理的回答。”

小王叹了口气:“这就是‘看着好’和‘真的好’的区别。你抽的二十条测试,恰好没遇到这种踩坑的问题。但如果就这样上线,每条胡编乱造的回答都是一个潜在的线上事故。”

老李沉默了几秒:“那你有什么办法?总不能所有回答都人工看一遍吧,两千条文档,排列组合出几万种问题,我们团队就五个人。”

“不用人工,让评测框架来干。”小王打开一个新文件,“它叫 RAGAS——专门给 RAG 系统打分的工具。”


三个裁判,各管一摊

“评测 RAG 系统,就像给高考作文评分,”小王在白板上画了三个小人,“你不能只找一个语文老师看一遍就说多少分。得有人看内容是不是跑题,有人看论据是不是编的,有人看引用的材料对不对。RAGAS 正好有三个核心指标,对应这三个裁判。”

第一个裁判:忠实度。 “看的是 AI 有没有胡编乱造。回答里的每一个事实,能不能在检索到的文档里找到依据?如果找不到,就是在编。”

老李点头:“刚才 CEO 那个就是忠实度为零。”

第二个裁判:答案相关性。 “看的是有没有答非所问。你问‘年会几号’,它回答‘年会很热闹’,热闹跟几号有关系吗?没有。相关性就打低分。”

第三个裁判:上下文召回率。 “看的是检索环节有没有把需要的文档找出来。如果正确答案藏在第 5 篇文档里,而检索只取了前 3 篇,那召回率就丢分了。”

图:RAGAS 三个核心指标——召回率衡量检索,忠实度和相关性衡量生成

“这三个分数一跑,RAG 系统哪里掉链子,一目了然。”小王敲了敲屏幕,“比‘我看着挺好’靠谱多了。”

老李哼了一声:“那你这三个裁判,谁来打分?不会又是人工吧?”

“当然是自动的。RAGAS 底层也是用大模型来打分——但它用的是更强的模型,比如 GPT-4,来评估你的 RAG 输出。就像请一个专家来批改作业,比你自己的系统更客观。”


RAGAS 实战:十行代码见分晓

“别光说,”老李催促道,“把你刚才说的跑一遍。”

小王打开终端,调出了他们的评测数据集:

python
# 用 RAGAS 评测 RAG 系统的三个核心指标
from ragas import evaluate
from ragas.metrics import faithfulness, answer_relevancy, context_recall
from datasets import Dataset

# 准备评测样本:每个样本包含问题、回答、检索到的上下文
eval_data = Dataset.from_dict({
    "question": [
        "公司现任CEO是谁?",
        "年会是什么时候办的?",
        "数据库连接池怎么配?"
    ],
    "answer": [
        "现任CEO是张伟,2019年接任。",          # 这个会被打低分
        "2025年11月8日在杭州举办。",
        "max_connections建议设置为200。"
    ],
    "contexts": [
        ["张伟曾任CEO,刘洋2023年接任CEO。"],  # 上下文有正确答案
        ["2025年会于11月8日在杭州XX酒店举行。"],
        ["max_connections参数建议值200。"]
    ],
})

result = evaluate(eval_data, metrics=[faithfulness, answer_relevancy, context_recall])
print(result)
# 输出:
# faithfulness: 0.67     ← 第一条拉低了
# answer_relevancy: 0.91
# context_recall: 0.89

老李盯着那个 0.67 的忠实度分数:“CEO 那条回答把整组分数拉下来了。如果我只抽另外两条,忠实度可能接近 1.0——这就是你说的‘抽二十条看不出问题’。”

“对。二十条可能碰巧全简单,也可能正好绕开了所有坑。RAGAS 的价值就是帮你把坑找出来,而且不需要你一条一条看。”


评测数据集:不是随便拉几个问题就行

老李拧开保温杯喝了一口,若有所思:“那你的评测数据集是怎么来的?总不能从生产日志里随便扒几条吧。”

“这个问题问到点上了。”小王在白板上画了三个圈,“评测数据集的质量,直接决定了评测结果有没有参考价值。搭数据集的时候要注意三件事——”

覆盖边界案例。简单的‘年会是哪天’没问题,但你得放一些陷阱题进去——‘上个月的 CEO 是谁’、‘数据库迁移失败的根因是什么’、‘合同编号 XYZ-2024 的内容’。这些才是真实的用户查询,最能暴露系统的弱点。”

标注上下文依据。每一条评测数据,你得知道‘正确答案在哪篇文档里’。不然你怎么判断上下文召回率?就像考试不公布标准答案,分数就没法算。”

持续更新。知识库更新了,评测集也要跟着更新。不能拿去年的问题测今年的系统——那个叫刻舟求剑。”

python
# 一个结构化的评测样本
eval_sample = {
    "question": "数据库迁移的POC结果是什么?",
    "answer": "POC结果显示Postgres在写入性能上超过MySQL约30%。",
    "contexts": [  # 检索到的文档片段
        "POC测试:Postgres写入TPS 12000,MySQL写入TPS 9200。结论:迁移可行。",
        "迁移风险:老赵担心数据一致性,需进一步验证。"
    ],
    "ground_truth_doc": "doc_migration_poc_v2.pdf",  # 标注标准答案来源
}

“看到没有,”小王指着最后一行的 ground_truth_doc,“有了这个,评测框架才知道‘正确文档’是哪篇,才能算召回率。没有这行,你就只能靠猜。”

老李往小本本上记了几笔,头也不抬:“那这个评测,上线以后还要跑吗?”


把评测塞进 CI/CD

“当然要跑。”小王打开了项目部署流水线的配置页面,“你想想,我们每改一次分块策略、每换一次嵌入模型、每调整一次 top_k,都在改变 RAG 系统的行为。如果不跑一遍评测就上线,等于闭着眼睛踩油门。”

他在屏幕上画了一个流程:

图:RAG 系统的评测门禁——指标不达标,自动阻断上线

“每次提交代码,自动跑一组评测用例。忠实度低于 0.85,直接阻断部署,发告警给我。这个门禁不需要人工,跑完不到五分钟。”

老李盯着这个流程,沉默了好一会儿。然后他拧开保温杯,枸杞的香气飘了出来。

“行。评测数据集你来搭,RAGAS 集成到 CI 里。下周上线前,把忠实度给我拉到 0.9 以上。”他站起来,走了两步又转过身,“不过你小子是不是早就准备好了这套东西,就等着我拍板?”

小王嘿嘿一笑:“我上周就搭好了,就是怕你说‘不就跑个分吗’然后否掉。”

老李摆摆手,保温杯跟着晃了晃:“下不为例啊。还有——那个忠实度具体是怎么算的?大模型给大模型打分,会不会也有幻觉?你给我看看那个评分 prompt。”

“行,我把评分链路的日志调出来。”小王拉过椅子,“你看,RAGAS 内部是把回答拆成若干个独立陈述,然后逐句去跟上下文对账——就像会计做审计,一笔一笔核。”

窗外华灯初上,办公室只剩他们俩的屏幕亮着。老李的保温杯又续了一轮热水,枸杞在杯底打着转,像是评测框架里那些来回对账的裁判,一刻不停地在检查 AI 交上来的每一份答卷。

重排序:让你的检索结果从"还行"到"精准"的秘密武器
选 Embedding 模型就像相亲:合适的才是最好的