三个AI考官:多模型协作的RAG评估框架实战

发布时间:2026/10/12 18:56:27
三个AI考官:多模型协作的RAG评估框架实战
1. 为什么需要多个AI考官来给RAG打分做过RAG检索增强生成系统的人都有一个共同的困惑明明检索模块的召回率看着不错生成模块的单测也过了但上线之后用户就是觉得“答得不对味”。问题出在哪答案往往藏在评估环节。传统的评估方式要么靠人工标注成本高、周期长、覆盖窄要么靠单一指标比如BLEU、ROUGE或者简单的语义相似度但这些指标和人类真实感受之间的差距做过的人都懂。我最初搭建RAG评估流水线的时候用的是单模型打分——找一个能力比较强的通用大模型给它一段问题、一段检索到的上下文、一段生成的回答让它打个分。跑了几百条测试用例之后我发现了一个很要命的问题这个“考官”有明显的偏好。它对语言流畅但事实错误的回答给分偏高对语言朴实但事实准确的回答反而给分偏低。换句话说它把“说得好听”和“说得对”混为一谈了。这个发现促使我开始思考一个问题能不能用多个不同定位的AI模型来分别负责不同的评估维度最后综合出一个更可靠的分数这就是“三个AI考官”这个思路的由来。具体来说我设计了三个角色一个负责忠实度回答是否忠实于检索到的上下文一个负责相关性回答是否切题一个负责完整性回答是否覆盖了问题的所有关键点。每个考官只关注自己的维度最后加权汇总。这套方案解决的核心问题是让RAG系统的评估结果更接近人类的真实判断同时保持自动化的效率和可复现性。它适合已经搭建了RAG系统、正在寻找靠谱评估方案的开发者也适合对LLM应用评估感兴趣、想了解多模型协作评估思路的技术人员。不管你是刚接触RAG的新手还是已经踩过不少坑的老手这套多考官评估框架都能给你提供一个可落地、可调整的参考方案。2. 三个AI考官的角色设计与选型逻辑2.1 为什么是三个而不是一个或五个先说一个我踩过的坑。最开始我想的是“越多越好”搞了五个模型分别打分结果发现两个问题第一成本线性增长每次评估要调用五次APItoken消耗直接翻五倍第二维度之间出现了严重的重叠比如“相关性”和“完整性”这两个维度不同的模型给出的分数高度相关等于花了钱但没买到额外的信息量。后来我做了相关性分析发现三个维度之间的相关系数控制在0.3到0.5之间是比较理想的——既有一定的独立性又不至于完全无关。最终定下来的三个维度是忠实度Faithfulness回答中的每一个事实性陈述是否都能在检索到的上下文中找到依据。这个维度主要抓的是“幻觉”问题。相关性Relevance回答是否直接回应了用户的问题有没有跑题或者答非所问。这个维度抓的是“偏题”问题。完整性Completeness回答是否覆盖了问题所要求的所有关键信息点有没有遗漏重要内容。这个维度抓的是“漏答”问题。这三个维度基本上覆盖了RAG系统最常见的三类失败模式。而且它们之间的独立性比较好一个回答可能忠实度很高但完整性不够也可能相关性很强但忠实度拉胯组合起来能比较全面地刻画回答质量。2.2 考官模型的选型标准选哪个模型来当考官这里面有讲究。我的选型标准有这么几条第一模型能力要足够强。考官本身如果能力不行打分就没有参考价值。我一般会选择当前可用的、在推理和指令遵循方面表现较好的通用大模型。具体选哪个就不点名了大家根据自己手头的资源来定。第二三个考官最好来自不同的模型家族。这一点很关键。如果三个考官都是同一个模型那它们很可能有相同的偏见。比如同一个模型可能对某种表达风格有偏好三个考官都给高分但实际上这个回答并不好。用不同家族的模型可以在一定程度上对冲这种系统性偏见。第三成本要可控。评估是一个高频操作每次迭代都要跑。如果单次评估成本太高根本跑不起来。我的做法是忠实度考官用能力最强的模型因为这个维度最需要推理能力相关性和完整性考官可以用稍轻量的模型在成本和效果之间取一个平衡。第四输出格式要稳定。考官需要输出结构化的分数而不是一段自由文本。所以选模型的时候要测试它在给定prompt下的输出稳定性。我试过某个模型同样的输入跑三次一次输出JSON一次输出Markdown表格一次输出纯文本这种就不适合当考官。2.3 评分量表的设计评分量表的设计直接决定了评估结果的分辨率。我试过几种方案最开始用的是1-5分的李克特量表结果发现模型倾向于给3分或4分区分度不够。后来改成1-10分又发现模型对分数的锚定不一致同一个回答今天打7分明天打8分。最终我采用的是三级评分加理由的方案分数含义判定标准1不合格存在明显的事实错误、严重偏题或关键信息缺失2合格基本满足要求但有轻微问题或改进空间3优秀完全满足该维度的要求无明显问题每个分数段都有明确的判定标准描述而且要求考官在给出分数的同时附上一句简短的理由。这个理由非常重要它是我后续做人工抽检和prompt迭代的主要依据。三级量表的好处是区分度足够三个等级对应三种处理策略模型的一致性也比较好实测同一输入三次评分的一致率能达到85%以上。如果你觉得三级不够细可以在每个等级内部再细分比如2.1、2.2、2.3但我不建议一开始就搞太细先把框架跑通再说。3. 评估流水线的核心实现细节3.1 数据准备与测试集构建评估流水线的输入是一批测试用例每个用例包含四个字段问题question、检索到的上下文context、模型生成的回答answer、以及可选的参考答案reference。参考答案不是必须的因为忠实度和相关性这两个维度不需要参考答案也能评但完整性维度如果有参考答案会评得更准。测试集的构建有几个原则。第一覆盖面要广。我一般会从真实用户日志里采样覆盖不同类型的问题事实型、比较型、推理型、开放型。每种类型至少要有20条以上否则统计意义不够。第二难度要有梯度。简单问题、中等难度、困难问题各占一定比例这样才能看出系统在不同难度下的表现差异。第三要包含已知的bad case。我习惯把之前发现的问题案例也放进测试集用来验证新的迭代有没有把这些坑填上。测试集的规模方面我的经验是如果只是做快速迭代验证50到100条就够了如果要出一份正式的评估报告至少300条起步。数量太少的话单个case的波动会严重影响整体分数。3.2 Prompt工程让考官理解自己的角色每个考官的prompt都是精心设计的核心思路是明确角色、给出标准、要求结构化输出。以忠实度考官的prompt为例结构大概是这样的FAITHFULNESS_PROMPT 你是一个严格的事实核查员。你的任务是判断【回答】中的每一个事实性陈述是否都能在【上下文】中找到依据。 评分标准 - 3分回答中的所有事实性陈述都能在上下文中找到直接依据没有添加任何上下文之外的信息。 - 2分回答中绝大部分事实性陈述有依据但存在个别细节上的推断或轻微外推。 - 1分回答中存在明显的事实错误或者有多个关键陈述在上下文中找不到依据。 请按以下格式输出 分数[1/2/3] 理由[一句话说明打分依据] 【上下文】 {context} 【回答】 {answer} 这个prompt有几个设计要点。第一角色定义要具体。“事实核查员”比“评估助手”更能让模型进入状态。第二评分标准要可操作。不能只说“好/中/差”要给出具体的判定条件。第三输出格式要固定。我要求模型先输出分数再输出理由这样解析起来方便而且强迫模型先做判断再解释避免它先写一堆理由然后分数给偏了。相关性和完整性的prompt结构类似只是角色定义和评分标准不同。相关性考官的角色是“用户代言人”判断回答是否真正解决了用户的问题完整性考官的角色是“质量检查员”对照参考答案如果有或问题本身检查关键信息点是否覆盖。3.3 调用与解析工程实现中的关键细节实际调用的时候有几个工程细节需要注意。并发控制。三个考官可以并行调用这样能把评估时间压缩到原来的三分之一。但要注意API的速率限制我一般会设置一个信号量来控制并发数避免触发限流。重试机制。模型偶尔会输出格式不对的内容比如分数写成了“三分”而不是“3”或者漏掉了理由。这时候需要有一个重试逻辑如果解析失败就把原始输出和格式要求再发给模型一次让它重新输出。实测下来重试一次基本能解决90%以上的格式问题。结果缓存。同样的输入不需要重复评估。我用一个简单的哈希表做缓存key是问题上下文回答的哈希值value是三个考官的评分结果。这样在调试prompt或者调整权重的时候不需要反复调用API。解析代码。解析部分要写得健壮一些。我的做法是用正则表达式提取“分数X”和“理由...”这两部分如果正则匹配失败再尝试用更宽松的规则去提取。下面是一个简化的解析函数import re def parse_score(raw_output): # 尝试匹配标准格式 score_match re.search(r分数[:]\s*([123]), raw_output) reason_match re.search(r理由[:]\s*(.), raw_output) if score_match: score int(score_match.group(1)) reason reason_match.group(1).strip() if reason_match else return score, reason # 宽松匹配找第一个出现的1/2/3 fallback re.search(r\b([123])\b, raw_output) if fallback: return int(fallback.group(1)), raw_output.strip() return None, raw_output.strip()这个函数先尝试严格匹配失败后再用宽松规则兜底。如果还是解析不出来就返回None由上层逻辑决定是重试还是标记为解析失败。3.4 加权汇总从三个分数到一个总分三个考官各自打分之后需要汇总成一个总分。最简单的做法是等权平均但实际使用中我发现不同维度的重要性是不一样的。对于大多数RAG应用场景忠实度是最重要的——回答可以不完整可以稍微偏题但绝对不能胡说八道。所以我的默认权重是忠实度0.5相关性0.3完整性0.2。当然这个权重是可以调整的。如果你的应用场景对完整性要求特别高比如法律咨询、医疗问答可以把完整性的权重调高。如果是对相关性要求特别高比如客服机器人可以把相关性的权重调高。我建议在评估报告里同时给出总分和三个维度的分项得分这样即使总分一样也能看出不同系统或不同版本之间的差异在哪里。汇总公式很简单def weighted_score(faithfulness, relevance, completeness, w_f0.5, w_r0.3, w_c0.2): return (faithfulness * w_f relevance * w_r completeness * w_c)但要注意这个加权是在归一化之后做的。因为三个维度都是1-3分量纲一致所以可以直接加权。如果某个维度用了不同的量表需要先归一化到同一量纲再汇总。4. 实操中遇到的典型问题与排查记录4.1 考官之间“打架”怎么办多考官评估最常遇到的问题就是考官之间的评分不一致。比如忠实度考官给了3分但完整性考官给了1分而且两个考官的理由看起来都有道理。这种情况怎么处理我的处理原则是先看分歧是否合理再看是否需要仲裁。有些分歧是合理的比如一个回答确实忠实于上下文但只覆盖了问题的一部分那忠实度高、完整性低就是正常的。但如果两个考官对同一个维度的判断出现严重分歧比如都评忠实度但一个给3一个给1那就需要介入了。我设计了一个简单的分歧检测机制如果三个考官中任意两个的评分差距超过1分在三级量表里就是差了两级就触发人工复核。在实际跑了几千条数据之后触发人工复核的比例大概在5%到8%之间这个比例是可以接受的。另外我还试过引入第四个模型作为“仲裁者”当分歧出现时让它来裁决。但实测下来效果一般因为仲裁者本身也可能有偏见而且增加了成本和复杂度。后来我还是回归到人工抽检的方式虽然慢一点但更可靠。4.2 模型输出格式不稳定的处理前面提到过模型有时候不按格式输出。除了重试之外我还做了几件事来降低格式问题的发生率。第一在prompt里给出示例。Few-shot示例对格式稳定性的提升非常明显。我在每个考官的prompt里都放了一个完整的输入输出示例模型照着抄的概率就高很多。第二降低temperature。评估任务不需要创造性temperature设成0或者0.1就行。我试过temperature0.7的时候格式错误率明显上升。第三用JSON模式。如果调用的API支持JSON输出模式一定要开启。这个功能能从根本上解决格式问题输出直接就是结构化的JSON解析起来零成本。第四后处理兜底。即使做了上面这些偶尔还是会有格式问题。所以解析函数一定要写得健壮并且要有日志记录方便排查是哪些case出了问题。4.3 评估结果与人工判断偏差较大的排查思路有时候你会发现自动评估的分数和人工判断的分数差距比较大。比如自动评估给了高分但人工觉得这个回答很一般。遇到这种情况我一般按以下步骤排查第一步检查测试用例本身。有时候问题出在测试用例上比如上下文里本身就包含了错误信息那模型忠实于错误上下文反而会被扣分。这种情况需要先修正测试集。第二步检查考官的prompt。把出问题的case拿出来单独跑一遍考官的prompt看看考官的理由是什么。如果考官的理由明显不合理那就是prompt需要调整。比如忠实度考官可能把“根据上下文推断”也当成了“没有依据”这就需要把评分标准写得更细。第三步检查权重设置。如果分项得分都合理但总分不对那就是权重的问题。比如一个回答忠实度3分、相关性1分、完整性1分按默认权重算出来是2.0分合格但人工可能觉得这个回答完全跑题了应该给1分。这时候就需要调整权重把相关性的权重调高。第四步考虑引入校准集。如果偏差是系统性的可以构建一个小的校准集比如50条人工标注过的数据用来调整考官的prompt或者权重让自动评估的结果向人工判断靠拢。4.4 常见问题速查表问题现象可能原因排查方法解决方案三个考官分数高度一致考官来自同一模型家族检查模型名称更换不同家族的模型格式解析频繁失败temperature过高或prompt不明确查看原始输出降低temperature增加few-shot示例评估结果与人工偏差大prompt标准不清晰或权重不合理逐case分析考官理由细化评分标准调整权重评估速度太慢串行调用或并发限制检查调用逻辑改为并行调用设置合理并发数同一输入多次评分不一致模型随机性过高重复跑同一case设temperature0增加评分稳定性某些维度区分度不够评分量表太粗或太细统计分数分布调整量表粒度增加判定标准描述5. 多考官评估框架的扩展与优化方向5.1 动态权重调整的思路固定权重虽然简单但不够灵活。我后来尝试了一种动态权重的方案根据问题的类型自动调整权重。比如事实型问题忠实度的权重调到0.6开放型问题完整性的权重调到0.4。实现方式是在评估流水线前面加一个轻量的分类器可以用规则匹配也可以用一个小模型先判断问题类型再选择对应的权重配置。这个方案的效果还不错尤其是在混合类型的测试集上动态权重比固定权重的评估结果更接近人工判断。但缺点是增加了复杂度而且分类器本身也可能出错。所以如果你的测试集类型比较单一用固定权重就够了。5.2 引入参考答案的完整性评估前面提到完整性评估可以对照参考答案来做。具体做法是先把参考答案拆解成若干个关键信息点可以用模型来拆也可以人工标注然后检查回答中是否覆盖了这些信息点。覆盖率的计算方式是命中的信息点数除以总信息点数。这个方案比让模型直接打分的准确性更高因为信息点的命中与否是一个相对客观的判断。但前提是你得有参考答案而且参考答案的质量要过关。如果参考答案本身就不完整那评估结果也会偏。我一般会在两种情况下用这个方案一是做正式评估报告的时候会人工准备一批参考答案二是做回归测试的时候会用之前积累的高质量回答作为参考答案。5.3 评估结果的可视化与追踪评估不是跑一次就完了而是一个持续的过程。我习惯把每次评估的结果都存下来然后用简单的图表来追踪趋势。比如用折线图看总分随时间的变化用柱状图看三个维度的分项得分用散点图看不同问题类型下的表现差异。这些图表不需要多复杂用matplotlib或者plotly画一下就行。关键是要能一眼看出这次迭代之后哪个维度提升了哪个维度退步了整体趋势是向好还是向坏。有了这些信息后续的优化方向就很清晰了。我还建了一个简单的“问题案例库”把每次评估中得分低的case都存进去标注上问题类型和原因。这个库积累到一定规模之后就成了优化RAG系统的宝贵素材——你可以清楚地看到系统在哪些类型的问题上容易翻车然后有针对性地去改进检索策略或生成prompt。5.4 成本控制的实际经验多考官评估的成本主要来自API调用。三个考官每个考官一次调用那就是三次。如果测试集有300条那就是900次调用。如果每条还要重试那可能上千次。这个成本对于个人开发者或者小团队来说是需要认真考虑的。我的成本控制经验有这么几条第一缓存复用。前面说过同样的输入不重复评估。这个能省掉大量重复调用尤其是在调试阶段。第二分级评估。不是所有测试用例都需要三个考官全上。我一般会先用一个轻量模型做初筛把明显有问题的case筛出来只对这些case做完整的三考官评估。这样能省掉大概60%到70%的调用量。第三批量调用。如果API支持批量请求尽量用批量模式通常比单条调用便宜。第四选择合适的模型。忠实度考官需要强模型但相关性和完整性考官可以用轻量模型。我实测下来轻量模型在这两个维度上的表现和强模型的差距并不大但成本可能只有十分之一。第五控制测试集规模。日常迭代用50到100条就够了不需要每次都跑全量测试集。全量测试集只在发版或者做正式报告的时候跑。这套多考官评估框架我从最初的想法到落地跑通大概花了两周左右的业余时间。中间踩了不少坑但也积累了很多一手经验。最深的体会是评估不是越复杂越好而是越稳定、越可解释越好。三个考官的分工明确、评分标准清晰、结果可追溯这比搞一堆花哨的指标但没人看得懂要强得多。如果你也在做RAG系统的评估不妨从这三个维度开始试试根据自己业务的特点调整评分标准和权重跑上一批真实数据你会发现很多之前被忽略的问题。