☰
RAG项目验收利器:20题评估集构建与落地实践
2026/10/1 6:31:04 网站建设 项目流程

1. 为什么你的RAG需要一个"验收标准"

1.1 被一句"它到底好不好用"问住的真实场景

先说个我自己经历的事。做RAG项目的人都懂,前期搭框架、选向量库、调prompt,忙活了两三周,终于跑通了一个像模像样的知识库问答demo。兴致勃勃拿去给业务方演示,对面问了一句:"那它到底好不好用?"我当场愣了一下,只能含糊地说"大部分问题都能答上来,效果还不错"。接着对方追问:"怎么证明?"——这句话直接把我问住了。

当时系统里确实有几个调试时候随手测的问题,但那些都是我自己挑出来的、答得好的问题,根本经不起推敲。业务方其实是想要一个"能拿出去给领导看、能说服自己上线"的依据。我意识到,RAG项目最缺的不是模型能力,不是检索精度,而是一套可量化的验收标准。后来我花了整整一周做了一件事:构造成一个20题的评估集,用它把RAG的表现从"我觉得还行"变成"这里有数据、有例子、有结论"。这个20题评估集后来成了我所有RAG项目的标配,每次改完代码、换完模型,都拿它跑一遍。

为什么要强调"评估集"而不是"测试一下"?因为一次性测试只能说明你当时运气好,而一个结构化的评估集能回答三个核心问题:它擅长什么、不擅长什么、改完之后有没有变好。这三个问题,恰恰是所有RAG项目从demo走向生产环境必须跨过的门槛。

1.2 评估集解决的三个核心问题

先说第一点:它擅长什么。RAG系统不是一个单一模型,它是"检索器 + 生成器 + 外部知识库"的组合,任何一个环节出问题,最终表现都会打折。比如你跟它聊产品手册里的常见问题,它可能答得很好;但你问跨章节的对比类问题,它可能就胡说了。评估集通过结构化的题目分布,能把这个"擅长边界"画出来。

第二点:它不擅长什么。这一步比第一步更重要。我见过很多团队上线RAG,用户的真实问题是五花八门的,但系统只在"FAQ式提问"上表现好,稍微换个问法就开始瞎编。评估集的作用就是主动把这些"盲区"暴露出来,而不是等用户骂上门。

第三点:改完之后有没有变好。这是评估集最大的价值。RAG是持续迭代的系统:今天换了embedding模型,明天调了chunk大小,后天给prompt加了引用格式要求。没有一套固定的题目,你根本说不清改动带来了"提升"还是"回退"。20题虽然不多,但足以作为回归测试的基线,让每次改动都有据可循。

关于评估集的数量,也有人问我:"为什么是20题,不是50题、100题?"我的回答是:20题是"最小有用集"。往上加题当然更严谨,但对一个起步阶段的RAG项目,20题已经能覆盖主要能力维度、能暴露出绝大多数常见问题,而且人工标注答案和理由的时间成本是可控的。先把20题跑熟,再扩展到真正的线上评测集,路径更顺。

2. 20题评估集的题目设计:从业务真实问题里长出来

2.1 题目从哪来:不能拍脑袋

我踩过最大的一个坑,就是自己坐在电脑前"编"评估题。当时我对着系统里已有的文档,脑子里凭空想了一些问题,比如"退货政策是什么""怎么联系客服"。这些题答起来确实漂亮,因为它们是冲着系统优点去的。但真实用户根本不会这么问,他们的问题是"我上周买的蓝牙耳机漏音,能退吗?"——带着具体场景、带着口语表达、还带情绪。

所以评估题的题源,第一优先级是真实用户问题。如果你有线上日志,从日志里扒高频问题、翻车问题;如果你没有线上日志,就去问售前、客服、运营同学,他们手上有一堆用户原话。我当时的做法是:找了5个业务同事,每个人给我5个"最常被问到的问题",再混入我观察到的"系统之前答错的问题",一共凑了40多个候选,从中筛选出20个。

第二优先级是业务文档里那些"必须答对"的关键知识点。比如产品价格、规格参数、合规红线、售后承诺——这些错了是要出事的。这种题不一定是用户高频问的,但必须纳入评估,因为它是底线。

筛选的时候,我坚持一个原则:每道题都要能从知识库中找到依据。找不到依据的题,要么是知识库里缺内容,要么是题目本身的答案就不在文档里。这类"超纲题"可以单独留两道,用来测试系统会不会"硬答"。硬答是一个严重问题:模型没有信息却编造答案,这在RAG场景里比答错更危险。

2.2 覆盖哪些能力维度:至少包含这六类

20道题不是均匀分布的,我是按照能力维度来配比的。每个维度代表一类典型的用户需求,也对应RAG系统的一个潜在弱点。

问题类型题量目的
单点事实问答4题考察最基础的"查得到、答得对"
多跳推理3题考察跨段落、跨文档的信息整合
限定条件问答3题考察"不同情况不同答案"的区分能力
否定/排除类2题考察"不是什么、不包含什么"的理解
时间敏感类2题考察文档版本和时效性处理
格式/操作类3题考察按指定格式输出的能力(如表格、步骤)
超纲题/拒答3题考察不知道时会不会诚实说不知道

这个配比不是拍脑袋定的。单点事实问答是RAG的基础能力,但很多系统连这个都做不好,尤其是知识库里有相似文档的时候,检索容易召回到错误片段;多跳推理是最容易暴露"检索碎片化"问题的类型,比如"A型号和B型号的保修政策有什么区别",答案散落在两段甚至两篇文档里,系统需要召回两个片段再整合;否定和排除类问题是传统向量检索的软肋,因为"不含""不适用"这类语义在embedding空间里往往表达得不清晰;时间敏感类问题对应的是"知识库里有多份版本,系统是否总是返回最新版"。

至于超纲题,我必须要说:大多数RAG项目没做这项测试,导致模型在不知道答案的时候一本正经地胡说。加3道"知识库里根本没有答案"的题,不是为了考倒系统,而是检验它的拒答机制。

2.3 难度配比与Golden Answer的写法

题目类型定完了,还需要给每道题配一个Golden Answer(标准答案)。这步很多人会偷懒,只写一句话正确答案。我建议一份完整的Golden Answer包含三部分:

第一,参考答案:不追求和模型输出逐字一致,但要点必须齐全。比如"A型号支持蓝牙5.3,B型号只支持5.2",后面那些解释性的话不要求必须出现。

第二,答案依据:注明这道题的知识点来自知识库的哪个文件、哪个章节。这是评估检索环节的关键——当模型答错时,你可以顺着依据去看检索到底有没有把对的片段召回来。

第三,评分要点:明确"答到什么程度算对"。比如"必须同时提到两个型号的蓝牙版本差异才算对""如果只答了其中一个型号,算部分得分"。

难度配比上我的建议是:6道基础题、8道中等题、6道难题(含超纲)。基础题用来确保系统没有"烂到底";中等题是主要区分度所在;难题用来暴露系统上限。20题的评估重点不是"满分率",而是看它在不同难度段位的表现曲线——如果难题全错、基础题也对不了几道,说明系统整体不可用;如果基础题全对、难题错一半,这个系统就具备上线的基本条件了。

Golden Answer这个环节有一个容易忽略的细节:答案要写给模型判分器看,也要写给人看。如果你后面用LLM-as-Judge(大模型当裁判)来批量打分,参考答案写得越具体,裁判模型的打分越稳定。

3. 评估指标体系:别只盯着命中率

3.1 检索层:Hit Rate和Recall的区别

很多人评估RAG,只报一个数:Hit Rate(命中率),也就是"检索结果里有没有包含正确答案的那个片段"。这确实是最直观的指标,但它有欺骗性。

打个比方:Hit Rate就是"你从图书馆借回的10本书里,有没有那本你需要的"。只要借回来了,就算命中了。但你借回来的另外9本书可能是完全无关的,这题照样算"命中"。所以Hit Rate高,不一定意味着检索"干净"。我见过一个系统Hit Rate能到0.9,但实际答案质量很差,因为检索回来的上下文里正确信息只占一小块,其他全是噪音,生成模型在噪音里迷失了方向。

因此在检索层,我至少看两个指标:Hit Rate@K(前K个结果里有没有正确答案)和上下文纯净度(召回的片段里有多少比例是真正有用的)。前者衡量"找没找到",后者衡量"找得准不准"。实践中,Hit Rate用来发现问题有没有暴露,纯净度用来判断问题出在embedding还是chunk设计上。如果你发现Hit Rate很高但纯净度很低,优先去优化chunk切分和重排;如果Hit Rate本身就低,问题多半在embedding模型或查询改写上。

还有一个容易搞混的概念:Recall(召回率)。在RAG评估里,Recall强调的是"知识库里所有相关片段被找回了多少"。一个多跳问题可能需要三个片段的信息才能完整回答,只召回两个也算不完整。20题这种小评估集没法严格计算Recall,但你可以在badcase分析时手动看:正确答案需要几个片段,系统实际召回了几个。这个信息能帮你判断要不要引入"查询分解"或"多路检索"。

3.2 生成层:忠实度、相关性、引用正确性

检索做得再好,最终用户看到的是生成的那段答案。生成层的评估,我拆成三个维度:

忠实度(Faithfulness):答案里的每句话是不是都能被检索到的上下文支持。这是RAG和纯LLM的核心区别——RAG模型没有被要求"记住"任何事,它只应该基于上下文说话,所以如果它说出了上下文里没有的内容,就是编造。忠实度是三条红线之一,一旦发现不忠实,这条答案直接判负,其他维度再好也没用。

相关性(Relevance):答案有没有真正回答用户的问题。有一种典型失败是"答非所问但内容正确"——用户问"怎么退货",模型回答了一大段退货政策原文,但没有给出操作步骤。从忠实度角度看它没错,但从相关性角度看它不及格。相关性评估稍微有点主观,所以我会在打分时要求评估者代入用户视角:这题如果我拿到这个回答,我会满意吗?

引用正确性(Citation Correctness):如果你的RAG带引用来源,那必须检查引用能不能对得上。我见过不少系统,引用标记是有了,但引用的段落和答案内容完全对不上,甚至引用的数字都不一致。这比不引用还糟糕,因为它给人一种"严谨"的错觉,其实是虚假严谨。引用正确性评估起来最简单粗暴:把答案里的关键信息,一条条顺着引用去原文里核对。

3.3 端到端打分:LLM-as-Judge怎么用更稳

20道题,如果纯人工打分,大概需要小半天时间;如果每次迭代都这么干,很快你就懒得跑了。所以我的做法是用LLM-as-Judge(大模型作裁判)做批量初评,再用人工抽检兜底。

用大模型给RAG打分这件事,很多人问"准不准"。说实话,如果你让它直接打一个总分,结论很飘;但如果你把评估拆细,让裁判模型按维度逐项打分,稳定性会好很多。我给裁判模型的打分指令里有这么几层要求:

  1. 先判断忠实度:请逐句判断回答中的每个陈述是否都能从提供的上下文片段中找到依据。只要有一句找不到依据,忠实度这一项就打0分,并指出是哪一句。
  2. 再判断相关性:忽略文字流畅度,只判断该回答是否直接解答了用户问题。如果有"绕开问题""答非所问"的倾向,相关性酌情扣分。
  3. 最后判断完整性:对照参考答案中的评分要点,检查是否所有要点都覆盖到了。

这里有个关键技巧:让裁判模型输出判决理由,而不仅仅是分数。哪怕理由写得稀烂,也比单纯一个数字有价值——因为它能告诉你"为什么扣分",这个信息足够你判断下一步优化方向。另外我提醒一句:不要用一个和你业务RAG相同的大模型来当裁判,尽量选用不同家的模型,降低"自说自话"的偏差。这是我实测后的经验,同一家模型的审美是连续的,很容易对相似风格的回答格外宽容。

4. 从0到1搭建评估脚本:跑出第一份报告

4.1 最小可用的评估管线设计

设计评估管线时,我的原则是能跑起来、结果能留痕、过程能复现。不需要一开始就上个完整的评测框架,一个Python脚本就够了。

管线分四段:

  1. 定义评估集:用一个JSON文件存20道题,每道题包含question(问题)、golden_answer(参考答案)、reference(答案依据)、difficulty(难度)、category(类型)。
  2. 循环执行RAG查询:对每道题调用你的RAG接口,拿到answer(回答)、contexts(召回片段)、sources(引用来源)。
  3. 调用裁判模型打分:把question、answer、contexts、golden_answer一起打包发给裁判模型,得到各维度分数和理由。
  4. 汇总输出报告:按题目维度汇总得分,计算整体指标,把badcase单独输出到文件。

这套管线的核心是第二步和第三步之间的"留痕"。我见过很多团队直接用RAG的最终答案去评分,中间过程全部丢弃,结果badcase出现时根本没法定位。每次评估跑完,必须留下每个问题的召回片段,这是后续分析的弹药。

4.2 评估脚本代码:批量跑题与打分

我用一个大概200行的Python脚本完成这件事,核心逻辑并不复杂。下面给出关键部分作为参考:

import json import pandas as pd # evaluation_set.json 结构见 4.1 with open("evaluation_set.json", "r", encoding="utf-8") as f: eval_set = json.load(f) def call_rag(question: str) -> dict: # 替换成你自己的RAG接口 result = your_rag.query( question=question, top_k=5, return_contexts=True, # 必须返回召回片段 return_sources=True, ) return { "answer": result.answer, "contexts": result.contexts, "sources": result.sources, } def judge(question, answer, contexts, golden_answer): prompt = f""" 你是一位严格的RAG系统评测员,请按以下维度打分(0-5分): 【忠实度】检查回答中每一句陈述是否都能从提供给模型的上下文中找到依据。 - 有编造即0分,并列出无法找到依据的句子。 【相关性】回答是否直接解答了用户问题,有无答非所问。 【完整性】对照参考答案的评分要点,检查是否全部覆盖。 用户问题:{question} 参考答案:{golden_answer} 模型召回上下文:{contexts} 模型回答:{answer} 请输出JSON: {{"faithfulness": 分数, "relevance": 分数, "completeness": 分数, "reason": "扣分理由", "unfaithful_sentences": []}} """ resp = judge_model.chat(prompt) return json.loads(resp) records = [] for item in eval_set: rag_result = call_rag(item["question"]) score = judge( question=item["question"], answer=rag_result["answer"], contexts=rag_result["contexts"], golden_answer=item["golden_answer"], ) records.append({ "question": item["question"], "category": item["category"], "difficulty": item["difficulty"], **score, "answer": rag_result["answer"], "contexts": rag_result["contexts"], "sources": rag_result["sources"], }) df = pd.DataFrame(records) df.to_csv("eval_report.csv", index=False, encoding="utf-8-sig")

一些细节说明:JSON输出要求很重要,一定要在prompt里让裁判模型给出结构化输出,不然你后面解析文本会疯掉;记录contexts这一行,是整个评估脚本信息量最大的资产;judge_model和业务RAG模型分开,上一章说过原因。

如果你用LangChain或LlamaIndex,也可以用它们自带的评估器模块,但说实话,对于20题这种小规模评估,自己写脚本反而更灵活,因为你可以完全控制记录格式和报告输出。

4.3 报告输出与badcase记录

跑完脚本后,我会生成一份评估报告,格式是一张表格加一个"坏例子清单"。表格的行是20道题,列分别是:类型、难度、忠实度、相关性、完整性、关键理由。然后再按照整体分、类型分、难度分三个维度做聚合。

我建议报告里一定要有类型维度的小计。只给一个总分35/60是没有意义的,你要看的是"多跳推理平均分2.0,单点问答平均分4.5"这种粒度,才能定位短板。比如我第一轮评估跑出来,20题平均分只有42分,但拆开看发现:单点事实问答几乎满血,多跳推理和超纲题全军覆没。这个信息直接告诉我,检索环节对于"跨文档整合"类型的支持不够,而不是整个系统都烂。

badcase清单我按严重程度标记:有编造的内容标红,答非所问标黄,只是不完整标蓝。标红的是必须立刻处理的,标黄的可以等下一轮,标蓝的属于可以接受的瑕疵。这样排优先级,优化的时候就不会眉毛胡子一把抓。

5. 我的踩坑记录:评估集常见的五个坑

5.1 坑一:评估题是从测试集里"抄"来的

我第一版评估题,有将近半数是从之前用过的开源测试集里挑的,或者是从网上找的RAG benchmark问题。看起来省事,但后果很严重:开源测试集的知识领域和你的业务文档八竿子打不着,你测出来的分数再高,业务方也不会买账。道理很简单——评估集的本质是"你的业务验收标准",不是"通用能力测试"。

后来我的做法是:第一版的20题,至少15题来自真实业务场景,剩下5题可以借鉴通用测试集的出题思路,比如故意构造一个需要多步推理的问题,但底层知识点换成你自己的文档内容。一张"抄来的"评估集也许能说服技术领导,但绝对说服不了业务方,因为人家看一眼题目就知道"这不是我们用户会问的"。

5.2 坑二:Golden Answer写得太死

有一版我的参考答案写得很机械化,比如"退货政策是7天无理由",然后裁判模型给所有提到"7天"的回答都打了高分。结果系统学精了,开始大量堆砌关键词,回答读起来像关键词拼凑,但裁判模型看不出来。这就是答案写得太死、没有覆盖关键维度带来的问题。

后来我调整了Golden Answer的写法,不追求一个标准句子,而是写清楚"必须包含3个要点:时效、条件、操作入口,缺一不可"。这样裁判模型是在核对要点,而不是做字面匹配。注意:Golden Answer不是让你放弃精确性,而是把精确性从"字面"提升到"语义"层面。

5.3 坑三:评分模型被你的RAG"带偏"

上一章我提过裁判模型尽量别和业务RAG用同一家。这里我要再说深一点:裁判模型很容易被"高质量但错误"的回答带偏。什么意思?你的RAG生成了一段文笔很流畅、结构很完整的错误答案,裁判模型看到它像模像样,就给忠实度打了高分。我遇到过一次:模型编造了一个产品参数,但这个参数在召回上下文里根本不存在,裁判模型却因为"回答很自然"给了4.5分。

解决办法有两个:一是把忠实度判断从"整体判断"改成"逐句判断",让裁判模型对照上下文,一句一句标出处;二是人工抽检,每轮评估至少抽5道题自己看一遍。LLM-as-Judge只能当筛子,不能当法官。

5.4 坑四:只测平均数,不看分布

只报一个平均分,是最偷懒也最误导人的做法。举个例子:某次迭代我把prompt调整了一下,平均分从4.0升到4.3,看着是进步了。但当我翻明细时发现,多跳推理类的三道题从4.0掉到了2.5——平均分被其他题型的提升掩盖了。这就是辛普森悖论在日常评估里的体现。平均数会掩盖系统性退化。

所以我在每一轮评估报告里都强制要求:按类型、难度两个维度做交叉表,任何一个子类别出现分数下降,都必须有解释,否则视为回归。一个评估集的价值不在于算出一个总分,而在于帮你抓住"哪一类能力在退化"。

5.5 坑五:题目写完之后就再也不更新

我见过最多的场景是:团队辛苦做了一套评估集,然后把它当成了"圣旨",半年都不变。问题在于,业务文档在更新,用户问题在变化,你的RAG系统也在进化——评估集必须跟着演进。否则会出现两种情况:一是系统在这20题上分数越来越高,但真实用户满意度没提升,说明评估集已经过拟合;二是系统为了过评估,开始"背答案",但遇到新问题依然拉胯。

我现在每个季度会做一次评估集"体检":删除已经答烂的题,加入近期真实用户的翻车问题,调整难题配比。评估集是活文档,不是纪念碑。

6. 评估结果出来之后:别急着发PPT,先做这三件事

6.1 按题号逐条过badcase,定位是检索还是生成问题

评估报告出来,很多人的第一反应是"赶紧把PPT做出来,展示我们提升了多少"。我的建议是:先花半小时逐条过badcase,把每个错题归因到环节。归因方法不复杂:看那个问题的召回片段。如果召回片段里压根没有正确答案,就是检索问题;如果召回片段里有正确答案但生成答案没用上或编造了,就是生成问题;如果两者都有,那就是串联问题。

这个归因决定了你的优化方向完全不一样。检索问题,你调embedding、调chunk、加重排;生成问题,你调prompt、调引用机制、调模型参数。不分青红皂白乱调,很可能调了半天分数没动。我自己做过一次完整的badcase归因统计,发现20题里12个错误,7个是检索层面没召回正确片段,4个是召回了但生成时没用上,1个是Golden Answer都写错了。这个分布让我非常清楚接下来该把力气花在哪。

6.2 检索侧优化:chunk、embedding、重排的优先级

如果你归因发现大部分问题出在检索,优化顺序我建议是这样的:

第一步,检查chunk大小和重叠。我遇到过太多案例,问题不是embedding不好,而是chunk切得太碎,导致一段完整的产品参数说明被切成了两半,检索时只召回了一半。把chunk从256token增大到512token,加上一点重叠,多跳推理的命中率能明显上升。但注意,chunk变大意味着噪音变多,需要配合下面的机制来兜底。

第二步,审视embedding模型。业务文档如果是中文为主,建议用针对中文优化的向量模型,或者至少用它跑一遍你的评估集,和通用模型对比Hit Rate。embedding模型的更换,是RAG优化里"收益可能巨大但成本也直观"的一步,评估集的价值就在于你能立刻看到Hit Rate的变化,而不是凭感觉判断"好像变好了"。

第三步,加重排(Rerank)。如果你想进一步提升检索质量,在向量召回Top 20的基础上用交叉编码器重排取Top 5。重排对"检索结果里有正确答案但位置靠后"的场景特别有效。但重排会增加延迟,20题评估时你关心的不是延迟,而是它能把Hit Rate@1和纯净度提升多少。实测中重排一般能带来5到10个百分点的提升,前提是Base检索别太差。

6.3 生成侧优化:prompt、引用、拒答

如果问题出在生成侧,常见三类:没读懂上下文里的关键信息、过度发挥编造内容、该拒答时不拒答。

prompt层面的优化很简单但有效:明确告诉模型"你只能使用上下文中的信息作答,如果上下文不足以回答问题,请直接说'知识库中未找到相关信息',不要自行推断"。这句话朴实无华,但能显著降低编造率。把这句话加进系统prompt之后,我第一版的超纲题拒答率从0提升到了2/3。

引用机制方面,如果你的框架支持,我会要求生成时"在每句话末尾标注它依据的片段编号"。强制性和引用标注有个额外好处:大模型为了标注正确,会更加克制地使用上下文,附带的编造率也会下降。这是我在实测中的一个意外收获:加引用之后,即便不看引用正确性,单纯忠实度也涨了。

拒答策略上,核心是给模型一个"体面的退路"。很多模型不爱说"不知道",因为它觉得这样不专业。你要在prompt里把"不知道"定义为专业行为,比如"如果上下文信息不足,直接说明缺少哪些信息,这比给出可能错误的推测更好"。

6.4 用评估集做回归:每次改动都跑一遍

最后一个建议,也是我认为最重要的:把评估集变成你的CI/CD一环。每次改动RAG的任何一个环节——不管是换模型、调chunk、改prompt,还是更新了知识库——都要跑一遍20题评估,把分数对比前一次记录。

我的习惯是每次跑完都把这个版本的数字归档,命名规则是"日期 + 改动内容"。一个月后回看,整条优化路径就非常清晰:哪天调了embedding,Hit Rate涨了多少;哪天加了重排,纯净度变化多少;哪天改了prompt,编造率降了多少。这份"改动-指标"的对照记录,比任何PPT都更能说服业务方——因为你展示的不是"我们做了很多工作",而是"每一步改动都对应数字的变化"。

如果哪天你发现跑评估集本身变得很痛苦(比如人工抽检环节开始糊弄),那就是评估集的题目该更新了。一个让团队愿意跑的评估集,比一个理论上完美的评估集有价值得多。


最后再分享一个真实体会:第一版20题评估集做出来的时候,我的RAG得分很惨,当时心里挺没底的。但恰恰是那些难看的badcase,逼着我一步步把检索、生成、拒答这些环节全部优化了一遍。后来项目上线,业务方再问"好不好用",我直接拿出评估报告,一五一十讲清楚哪些场景强、哪些场景弱、下一步打算怎么补。对方反而更信任了。我现在做任何RAG项目,第一周就把评估集立起来——不是因为它能证明你强,而是因为它能让你知道,你到底哪里还不够强。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询