先把结论摆前头判断 RAG 好不好用别盯着「最终答案对不对」看那一步掺了大模型的发挥水太浑。真正该先量的是检索这一层——你召回的那几段到底有没有把答对问题需要的资料捞上来。量化它就两个最常用的指标recallk 和 MRR今天把怎么算、怎么落地讲清楚。我是怎么栽进这个坑的。前阵子做一个内部制度问答模型答错我第一反应是 prompt 没写好调了三天 prompt时好时坏。后来一个老同事问我一句你确定检索那步把对的段落捞回来了吗我当场愣住——压根没量过。扒了日志才发现三成的问题正确答案所在的那段根本没进 top5。prompt 调出花来也没用菜是从厨房就坏的。recallk该捞的捞回来了吗定义很白话对一个问题标准答案藏在某几段原文里这叫相关文档你检索回 top k 段里头命中了几段相关的比上总共该命中的就是 recallk。def recall_at_k(retrieved_ids, relevant_ids, k): hit len(set(retrieved_ids[:k]) set(relevant_ids)) return hit / len(relevant_ids) if relevant_ids else 0落地的麻烦不在公式在于你得先有一份「标注集」几十上百个真实问题每个标好它的答案出自哪几个 chunk。我第一版偷懒自己拍脑袋标了 30 条后来发现远不够覆盖不到长尾问法。现在我固定从线上真实对话里抽每周补一二十条标注这活儿确实烦没有捷径。recall5 我一般要求拉到 0.9 以上意思是九成的问题对的段落能进前五。低于 0.8 别急着动 prompt先回头查分块和 embedding。MRR对的那段排得够靠前吗recall 只看「在不在 top k 里」不看排第几。可第 1 段和第 5 段对模型差别很大——靠后的容易被它忽略。MRR平均倒数排名就补这个它取每个问题里第一个命中相关段落的排名取倒数再平均。命中在第 1 位记 1第 2 位记 0.5第 3 位记 0.33。def mrr(all_retrieved, all_relevant): total 0 for retrieved, relevant in zip(all_retrieved, all_relevant): for rank, rid in enumerate(retrieved, 1): if rid in relevant: total 1 / rank break return total / len(all_retrieved)指标看什么多少算及格recall5对的段有没有进前五≥0.9MRR对的段排得靠不靠前≥0.7两个一起看才完整。我遇到过 recall 挺高但 MRR 拉胯的情况——该捞的都捞回来了可全堆在第 4、5 位前面塞着一堆沾边没用的。后来加了重排MRR 从 0.6 抬到 0.82答案质量跟着稳了一截。把量化变成日常光算一次没意义得让它跑成流水线。我的做法标注集存成一个表每次改了分块策略、换了 embedding、加了重排就把整套标注集重跑一遍recallk 和 MRR 各出一个数跟上一版比。涨了留下跌了回滚全凭数据不靠手感。搭这套我没怎么写后端。智能体和知识库是在一个零代码就能配 RAG 的平台上拖出来的文档传上去自动切片向量化检索结果能导出带 chunk_id 的明细我拿这份明细跟标注集一比就出指标了。省了我自己搭向量库、写检索接口的活。当然平台也只管到「把段落捞回来」标注集、判分脚本还得自己写它替不了你定义什么叫「答对」。一点真实的代价最大的坑是标注集本身。它就是你的尺子尺子歪了量出来的全是自欺欺人。我前期标得潦草指标好看得很线上照样翻车。后来老老实实从真实日志抠问题、人工核对答案出处标注集才慢慢可信但那是实打实耗工时的体力活急不来。(模型和 RAG 我都走的讯飞星辰 MaaS现成大模型和托管知识库直接调没自己部署)你们评 RAG 是只看终答案还是也量检索层recall 和 MRR 你卡在多少评论区交流下我这套标注流程总觉得还能再省点人力。
RAG检索准不准怎么量化:recall@k和MRR实操
先把结论摆前头判断 RAG 好不好用别盯着「最终答案对不对」看那一步掺了大模型的发挥水太浑。真正该先量的是检索这一层——你召回的那几段到底有没有把答对问题需要的资料捞上来。量化它就两个最常用的指标recallk 和 MRR今天把怎么算、怎么落地讲清楚。我是怎么栽进这个坑的。前阵子做一个内部制度问答模型答错我第一反应是 prompt 没写好调了三天 prompt时好时坏。后来一个老同事问我一句你确定检索那步把对的段落捞回来了吗我当场愣住——压根没量过。扒了日志才发现三成的问题正确答案所在的那段根本没进 top5。prompt 调出花来也没用菜是从厨房就坏的。recallk该捞的捞回来了吗定义很白话对一个问题标准答案藏在某几段原文里这叫相关文档你检索回 top k 段里头命中了几段相关的比上总共该命中的就是 recallk。def recall_at_k(retrieved_ids, relevant_ids, k): hit len(set(retrieved_ids[:k]) set(relevant_ids)) return hit / len(relevant_ids) if relevant_ids else 0落地的麻烦不在公式在于你得先有一份「标注集」几十上百个真实问题每个标好它的答案出自哪几个 chunk。我第一版偷懒自己拍脑袋标了 30 条后来发现远不够覆盖不到长尾问法。现在我固定从线上真实对话里抽每周补一二十条标注这活儿确实烦没有捷径。recall5 我一般要求拉到 0.9 以上意思是九成的问题对的段落能进前五。低于 0.8 别急着动 prompt先回头查分块和 embedding。MRR对的那段排得够靠前吗recall 只看「在不在 top k 里」不看排第几。可第 1 段和第 5 段对模型差别很大——靠后的容易被它忽略。MRR平均倒数排名就补这个它取每个问题里第一个命中相关段落的排名取倒数再平均。命中在第 1 位记 1第 2 位记 0.5第 3 位记 0.33。def mrr(all_retrieved, all_relevant): total 0 for retrieved, relevant in zip(all_retrieved, all_relevant): for rank, rid in enumerate(retrieved, 1): if rid in relevant: total 1 / rank break return total / len(all_retrieved)指标看什么多少算及格recall5对的段有没有进前五≥0.9MRR对的段排得靠不靠前≥0.7两个一起看才完整。我遇到过 recall 挺高但 MRR 拉胯的情况——该捞的都捞回来了可全堆在第 4、5 位前面塞着一堆沾边没用的。后来加了重排MRR 从 0.6 抬到 0.82答案质量跟着稳了一截。把量化变成日常光算一次没意义得让它跑成流水线。我的做法标注集存成一个表每次改了分块策略、换了 embedding、加了重排就把整套标注集重跑一遍recallk 和 MRR 各出一个数跟上一版比。涨了留下跌了回滚全凭数据不靠手感。搭这套我没怎么写后端。智能体和知识库是在一个零代码就能配 RAG 的平台上拖出来的文档传上去自动切片向量化检索结果能导出带 chunk_id 的明细我拿这份明细跟标注集一比就出指标了。省了我自己搭向量库、写检索接口的活。当然平台也只管到「把段落捞回来」标注集、判分脚本还得自己写它替不了你定义什么叫「答对」。一点真实的代价最大的坑是标注集本身。它就是你的尺子尺子歪了量出来的全是自欺欺人。我前期标得潦草指标好看得很线上照样翻车。后来老老实实从真实日志抠问题、人工核对答案出处标注集才慢慢可信但那是实打实耗工时的体力活急不来。(模型和 RAG 我都走的讯飞星辰 MaaS现成大模型和托管知识库直接调没自己部署)你们评 RAG 是只看终答案还是也量检索层recall 和 MRR 你卡在多少评论区交流下我这套标注流程总觉得还能再省点人力。