让裁判替你看答卷:RAGAS 实操因果链

本文是《让大模型"开卷考试":RAG 全链路因果链》的姊妹篇。上一篇第十二层说"评估是整条链的导航系统",留下一个问题:导航系统本身怎么搭------数据集从哪来、四个指标各自怎么算分、分数低了怎么归因、怎么接进 CI 防回退。本文把这条路从"跑通"讲到"上线"。
一句话总览:RAGAS 的本质是用 LLM 当裁判替代人工阅卷------因为人工评慢、贵、不一致,传统字面指标又量不出语义对错,所以让模型拆 claim、反向出题、逐块判定;而四个指标各防一种病:忠实度防编、相关性防跑题、上下文精度防噪音占位、上下文召回防漏检;评估的终点不是分数,是按分数归因回链路旋钮,并接进 CI 让每次改动都过检。


〇、愿景:我们要的到底是什么

上一篇的 RAG 链路有十三层,每层都有旋钮:切分大小、embedding 模型、召回路数、融合权重、reranker 阈值......改任何一个,都要回答同一个问题:

"这次改动,效果是变好了还是变坏了?"

我们要的是一个能重复执行、结果可比、能定位到坏在哪一层的评估系统------不是验收时跑一次,而是像单元测试一样,每次改动都跑。


第一层:三条土路都走不通,所以请 LLM 当裁判

先排除三个直觉方案,它们的死因各不相同:

  1. 人工评估 :最准,但改一次参数就要人看几百条问答------慢、贵,而且两个评审对同一条答案经常打分不一(一致性问题)。评估的意义是高频回归,人工撑不起这个频率;

  2. 传统 NLP 指标(BLEU/ROUGE):算的是字面重叠率,而生成式答案的表达千变万化。举个对比例子:

    • 参考答案:"D 级供应商将被暂停合作资格",模型答:"暂停与 D 级供应商的合作"------语义 100% 等价 ,但按字词重叠算(ROUGE),"将被""资格"等词缺失,得分只有 0.5 上下,冤杀
    • 参考答案同上,模型答:"D 级供应商将被暂停付款 资格"------只换了两个字,ROUGE 高达 0.9,但意思完全变了(不让你投标和冻结你货款是两回事),错放

    该低的不低、该高的不高------字面指标量不出语义对错

  3. 拍脑袋看几条 :没有固定数据集,这次看的和上次看的不是同一批 case,分数无从比较。典型翻车:把 chunk_size 从 400 调到 600,随手试了 3 条 query 都觉得"变好了"------但这 3 条恰好都是制度解读类长问题(大块天然占优),没覆盖到编号查询类(小块才捞得准),上线后第二类 query 召回率悄悄塌了;更糟的是下周又凭另外几条的"感觉"换了 embedding,两次"感觉不错"叠加,系统实际是在进步还是退步,没有固定基线永远无从发现------等于没评。

三条路的共同死因:缺一个既懂语义、又能高频执行、还标准统一的阅卷人 。这个角色的天然人选就是 LLM 自己------这就是 RAGAS 的核心思路:LLM-as-a-Judge(用 LLM 当裁判)

RAGAS 是一个开源 Python 评估框架(pip install ragas,Apache-2.0),把"裁判怎么阅卷"固化成了四个标准指标和一套数据集工具。

但请裁判是有代价的,先把丑话说在前面(后面每层都会回收):

  • 裁判本身有偏差:judge 模型的能力上限就是评分的可信度上限,换个裁判分数线会整体漂移;
  • 裁判按次收费:每条样本评四个指标 ≈ 十几次 LLM 调用,数据集一大就是真金白银。

因果要点:因为评估必须高频、统一、懂语义,而人供不起频率、字面指标供不起语义,所以 LLM 裁判是唯一解------但裁判的偏差和成本从此成为评估系统自身要管理的两个变量

第二层:裁判怎么打分------四个指标,各防一种病

RAG 的失败模式就两大类:检索错了生成错了。四个指标各盯一个具体的病,设计各不相同------理解了"为什么这样算",分数低了你才知道去哪修。

2.1 Faithfulness 忠实度:防"编"

问的是:答案的每句话,都能在检索到的资料里找到出处吗?

先解释一个关键词:claim(原子论断) ------一句"不能再拆、能独立判断真假"的最小陈述。自然语言的句子往往是好几个事实的打包,比如"评价等级分四级,D 级供应商暂停合作并被列入黑名单三年"这一句话里其实装了三个可以独立为真或为假的论断:①等级分四级;②D 级暂停合作;③D 级列入黑名单三年。资料里可能证实了①②却从没提过③------只有拆到这个粒度,才能发现"一句话里三分之二是真的、三分之一是编的"。拆 claims 这步也是 LLM 干的(prompt 要求把答案分解为原子陈述)。

算法分三步:
#mermaid-svg-iUsAtUxbczPZ9ltf{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-iUsAtUxbczPZ9ltf .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-iUsAtUxbczPZ9ltf .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-iUsAtUxbczPZ9ltf .error-icon{fill:#552222;}#mermaid-svg-iUsAtUxbczPZ9ltf .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-iUsAtUxbczPZ9ltf .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-iUsAtUxbczPZ9ltf .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-iUsAtUxbczPZ9ltf .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-iUsAtUxbczPZ9ltf .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-iUsAtUxbczPZ9ltf .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-iUsAtUxbczPZ9ltf .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-iUsAtUxbczPZ9ltf .marker{fill:#333333;stroke:#333333;}#mermaid-svg-iUsAtUxbczPZ9ltf .marker.cross{stroke:#333333;}#mermaid-svg-iUsAtUxbczPZ9ltf svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-iUsAtUxbczPZ9ltf p{margin:0;}#mermaid-svg-iUsAtUxbczPZ9ltf .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-iUsAtUxbczPZ9ltf .cluster-label text{fill:#333;}#mermaid-svg-iUsAtUxbczPZ9ltf .cluster-label span{color:#333;}#mermaid-svg-iUsAtUxbczPZ9ltf .cluster-label span p{background-color:transparent;}#mermaid-svg-iUsAtUxbczPZ9ltf .label text,#mermaid-svg-iUsAtUxbczPZ9ltf span{fill:#333;color:#333;}#mermaid-svg-iUsAtUxbczPZ9ltf .node rect,#mermaid-svg-iUsAtUxbczPZ9ltf .node circle,#mermaid-svg-iUsAtUxbczPZ9ltf .node ellipse,#mermaid-svg-iUsAtUxbczPZ9ltf .node polygon,#mermaid-svg-iUsAtUxbczPZ9ltf .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-iUsAtUxbczPZ9ltf .rough-node .label text,#mermaid-svg-iUsAtUxbczPZ9ltf .node .label text,#mermaid-svg-iUsAtUxbczPZ9ltf .image-shape .label,#mermaid-svg-iUsAtUxbczPZ9ltf .icon-shape .label{text-anchor:middle;}#mermaid-svg-iUsAtUxbczPZ9ltf .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-iUsAtUxbczPZ9ltf .rough-node .label,#mermaid-svg-iUsAtUxbczPZ9ltf .node .label,#mermaid-svg-iUsAtUxbczPZ9ltf .image-shape .label,#mermaid-svg-iUsAtUxbczPZ9ltf .icon-shape .label{text-align:center;}#mermaid-svg-iUsAtUxbczPZ9ltf .node.clickable{cursor:pointer;}#mermaid-svg-iUsAtUxbczPZ9ltf .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-iUsAtUxbczPZ9ltf .arrowheadPath{fill:#333333;}#mermaid-svg-iUsAtUxbczPZ9ltf .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-iUsAtUxbczPZ9ltf .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-iUsAtUxbczPZ9ltf .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-iUsAtUxbczPZ9ltf .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-iUsAtUxbczPZ9ltf .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-iUsAtUxbczPZ9ltf .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-iUsAtUxbczPZ9ltf .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-iUsAtUxbczPZ9ltf .cluster text{fill:#333;}#mermaid-svg-iUsAtUxbczPZ9ltf .cluster span{color:#333;}#mermaid-svg-iUsAtUxbczPZ9ltf div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-iUsAtUxbczPZ9ltf .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-iUsAtUxbczPZ9ltf rect.text{fill:none;stroke-width:0;}#mermaid-svg-iUsAtUxbczPZ9ltf .icon-shape,#mermaid-svg-iUsAtUxbczPZ9ltf .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-iUsAtUxbczPZ9ltf .icon-shape p,#mermaid-svg-iUsAtUxbczPZ9ltf .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-iUsAtUxbczPZ9ltf .icon-shape .label rect,#mermaid-svg-iUsAtUxbczPZ9ltf .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-iUsAtUxbczPZ9ltf .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-iUsAtUxbczPZ9ltf .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-iUsAtUxbczPZ9ltf :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 生成的答案
① 拆 claims

把答案拆成原子论断
② 逐条验证

每条 claim 能否从

retrieved_contexts 推断出
③ 算比例

支持数 / 总 claims 数

沿用上一篇的供应商制度场景。检索到的资料是:"评价等级分为 A、B、C、D 四级,被评为 D 级的供应商将暂停合作资格。"模型却答:"评价等级分四级,D 级供应商将暂停合作并被列入黑名单三年。"

  • 拆 claims:①"等级分四级" ②"D 级暂停合作" ③"D 级列入黑名单三年";
  • 逐条验证:① 支持 ② 支持 ③ 不支持(资料没提黑名单);
  • Faithfulness = 2/3 ≈ 0.67。

为什么拆 claims 而不是整句判? 因为长答案常常部分正确------整句判定会让"两句对一句错"的答案直接归零,拆成原子论断才能精确定位到哪一句是编的,这个定位能力正是第五层归因的基础。

(工程选项:验证这一步默认用 LLM,也可以换 Vectara 开源的 HHEM-2.1-Open 幻觉检测小模型------免费、可本地跑,生产环境降成本的常规手段。)

2.2 Answer Relevancy 答案相关性:防"跑题"

问的是:答案真的在回答用户的问题吗?(只管切不切题,不管对不对------对错是 Faithfulness 的事。)

算法设计很巧:
#mermaid-svg-LhRqDn7cToRxC8g1{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-LhRqDn7cToRxC8g1 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-LhRqDn7cToRxC8g1 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-LhRqDn7cToRxC8g1 .error-icon{fill:#552222;}#mermaid-svg-LhRqDn7cToRxC8g1 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-LhRqDn7cToRxC8g1 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-LhRqDn7cToRxC8g1 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-LhRqDn7cToRxC8g1 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-LhRqDn7cToRxC8g1 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-LhRqDn7cToRxC8g1 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-LhRqDn7cToRxC8g1 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-LhRqDn7cToRxC8g1 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-LhRqDn7cToRxC8g1 .marker.cross{stroke:#333333;}#mermaid-svg-LhRqDn7cToRxC8g1 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-LhRqDn7cToRxC8g1 p{margin:0;}#mermaid-svg-LhRqDn7cToRxC8g1 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-LhRqDn7cToRxC8g1 .cluster-label text{fill:#333;}#mermaid-svg-LhRqDn7cToRxC8g1 .cluster-label span{color:#333;}#mermaid-svg-LhRqDn7cToRxC8g1 .cluster-label span p{background-color:transparent;}#mermaid-svg-LhRqDn7cToRxC8g1 .label text,#mermaid-svg-LhRqDn7cToRxC8g1 span{fill:#333;color:#333;}#mermaid-svg-LhRqDn7cToRxC8g1 .node rect,#mermaid-svg-LhRqDn7cToRxC8g1 .node circle,#mermaid-svg-LhRqDn7cToRxC8g1 .node ellipse,#mermaid-svg-LhRqDn7cToRxC8g1 .node polygon,#mermaid-svg-LhRqDn7cToRxC8g1 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-LhRqDn7cToRxC8g1 .rough-node .label text,#mermaid-svg-LhRqDn7cToRxC8g1 .node .label text,#mermaid-svg-LhRqDn7cToRxC8g1 .image-shape .label,#mermaid-svg-LhRqDn7cToRxC8g1 .icon-shape .label{text-anchor:middle;}#mermaid-svg-LhRqDn7cToRxC8g1 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-LhRqDn7cToRxC8g1 .rough-node .label,#mermaid-svg-LhRqDn7cToRxC8g1 .node .label,#mermaid-svg-LhRqDn7cToRxC8g1 .image-shape .label,#mermaid-svg-LhRqDn7cToRxC8g1 .icon-shape .label{text-align:center;}#mermaid-svg-LhRqDn7cToRxC8g1 .node.clickable{cursor:pointer;}#mermaid-svg-LhRqDn7cToRxC8g1 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-LhRqDn7cToRxC8g1 .arrowheadPath{fill:#333333;}#mermaid-svg-LhRqDn7cToRxC8g1 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-LhRqDn7cToRxC8g1 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-LhRqDn7cToRxC8g1 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-LhRqDn7cToRxC8g1 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-LhRqDn7cToRxC8g1 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-LhRqDn7cToRxC8g1 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-LhRqDn7cToRxC8g1 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-LhRqDn7cToRxC8g1 .cluster text{fill:#333;}#mermaid-svg-LhRqDn7cToRxC8g1 .cluster span{color:#333;}#mermaid-svg-LhRqDn7cToRxC8g1 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-LhRqDn7cToRxC8g1 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-LhRqDn7cToRxC8g1 rect.text{fill:none;stroke-width:0;}#mermaid-svg-LhRqDn7cToRxC8g1 .icon-shape,#mermaid-svg-LhRqDn7cToRxC8g1 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-LhRqDn7cToRxC8g1 .icon-shape p,#mermaid-svg-LhRqDn7cToRxC8g1 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-LhRqDn7cToRxC8g1 .icon-shape .label rect,#mermaid-svg-LhRqDn7cToRxC8g1 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-LhRqDn7cToRxC8g1 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-LhRqDn7cToRxC8g1 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-LhRqDn7cToRxC8g1 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 生成的答案
① 反向出题

LLM 从答案反推出

n=3 个可能的问题
② 算余弦

每个反推问题与

原问题的 embedding 余弦
③ 取平均

背后的洞察一句话:如果答案真的切题,那从答案大概率能反推出原问题 ------就像你听别人打电话,从这一头的回答能猜出那一头问的是什么。答案跑题了,反推出的问题就会和原问题越离越远,余弦自然低。反向出题的数量默认 3 个(strictness 参数可调)。

2.3 Context Precision 上下文精度:防"噪音占位"

问的是:检索回来的块里,相关的排在前面了吗? 这是检索侧的指标,但它不只算"有没有噪音",更算噪音站在哪个位置------算法是 AP(Average Precision)风格的加权:

复制代码
Context Precision@K = Σ (Precision@k × vk) / 相关块总数
vk ∈ {0,1}:第 k 位的块是否相关(LLM 逐块判定)

官方示例一组数字把这个设计讲透了:检索回来两块,一块"埃菲尔铁塔在巴黎"(相关)、一块"勃兰登堡门在柏林"(无关)------

  • 相关块排第 1、无关块排第 2:得分 1.0(噪音在队尾,无害);
  • 无关块排第 1、相关块排第 2:得分 0.5(噪音占了首位,重罚)。

为什么罚位置? 回收上一篇的伏笔:LLM 有 Lost in the Middle、top 块先进 prompt------噪音块的位置越靠前,对生成的毒害越大。所以这个指标低,矛头直指召回排序和 reranker,而不是召回数量。

2.4 Context Recall 上下文召回:防"漏检"

问的是:回答问题需要的信息,检索都捞齐了吗?

算法和 Faithfulness 镜像对称:把参考答案(reference)拆成 claims,逐条判定能否归因到检索回来的上下文,算归因比例:

复制代码
Context Recall = reference 中能被检索上下文支撑的 claims 数 / reference 总 claims 数

镜像设计带来一个重要的使用约束:它是四个指标里唯一必须有 reference(标准答案)的------"漏没漏"是相对于"该有什么"而言的,没有标准答案就没有分母。怎么搞到 reference,是第四层的主题。

2.5 两个"答案"别搞混:response 与 reference

读到这里容易绕晕:Faithfulness 拆"答案"的 claims,Context Recall 拆"参考答案"的 claims------这是两个不同的东西

  • response(生成的答案) :你的 RAG 系统这次实际吐出来的回答。它是考生的答卷------可能编、可能漏、每次改动系统后再跑都可能不一样,是被考核的对象;
  • reference(参考答案) :评估数据集里事先标好的标准答案 ,人工写或 TestsetGenerator 合成(第四层),评估开始前就冻结在 golden set 里。它是阅卷用的标准答卷,不随系统改动变化。

同一个例子看两者各自怎么拆:问题是"D 级供应商会受到什么处理?",系统检索到的资料是"评价等级分为 A、B、C、D 四级,被评为 D 级的供应商将暂停合作资格"。

  • 系统生成的 response :"评价等级分四级,D 级供应商将暂停合作并被列入黑名单三年。" → 拆出 3 个 claims(①分四级 ②暂停合作 ③黑名单三年)→ Faithfulness 拿这 3 条对资料核"有没有出处":①② 有、③ 没有 → 0.67(生成的病:编);
  • 人工标注的 reference :"D 级供应商将被暂停合作资格。" → 拆出 2 个 claims(①D 级是评价等级之一 ②D 级暂停合作)→ Context Recall 拿这 2 条对资料核"有没有被捞到":两条都能在检索资料里找到 → 1.0(检索没漏)。
response(生成的答案) reference(参考答案)
谁写的 你的 RAG 系统(被考核的考生) 人工 / 合成器(出题人附的标准答卷)
何时产生 评估时现场生成 评估前冻结在 golden set
会不会变 改一次系统就变 冻结后不变
谁拆它 Faithfulness Context Recall
拆完跟谁核、核什么 对检索资料核"有没有出处"(防 对检索资料核"有没有被捞到"(防

一句话:两个指标拆 claims 的方向正好镜像------Faithfulness 是"答卷 → 资料"(生成是否忠于资料),Context Recall 是"标准答案 → 资料"(资料是否覆盖了标准答案)。一个管生成科,一个管检索科。

四指标全景:

指标 输入依赖 防的病 病灶在哪层
Faithfulness 答案 + 检索上下文 生成编造 生成/prompt(链路十一层)
Answer Relevancy 答案 + 问题 + embedding 答案跑题 生成或整体错位
Context Precision 检索上下文 + reference/答案 噪音块占位靠前 召回排序/reranker(八、九层)
Context Recall 检索上下文 + reference(必需) 该捞的没捞到 切分/embedding/query 处理(三、四、六层)

因果要点:因为 RAG 的病分"检索"和"生成"两科,所以四个指标不是四个分数,是四张不同科室的化验单------Faithfulness/Context Recall 拆 claims 是为了定位到句,Answer Relevancy 反向出题是因为正着没法算"切题",Context Precision 加权位置是因为噪音的毒性随位置递增。

第三层:跑通最小闭环------四行配置,一次打分

先立版本旗帜:本文代码按 ragas 0.4.x(当前最新 0.4.3,2026-01 发布)的 collections API 编写。网上大量教程(包括官方旧文档截图)是 0.1/0.2 的老 API,照抄必踩坑,版本对照见文末专节。

python 复制代码
pip install ragas   # 0.4.3
python 复制代码
import os
from openai import AsyncOpenAI
from ragas.llms import llm_factory
from ragas.embeddings.base import embedding_factory
from ragas.metrics.collections import (
    Faithfulness, AnswerRelevancy, ContextPrecision, ContextRecall,
)

os.environ["OPENAI_API_KEY"] = "sk-..."          # 裁判模型的钥匙
client = AsyncOpenAI()
judge_llm = llm_factory("gpt-4o-mini", client=client)          # 统一工厂,按模型名推断 provider
judge_emb = embedding_factory("openai", model="text-embedding-3-small", client=client)

# 每个指标是一个类实例,构造时注入裁判
faith      = Faithfulness(llm=judge_llm)
relevancy  = AnswerRelevancy(llm=judge_llm, embeddings=judge_emb)  # 反向出题要比余弦,所以多吃一个 embedding
precision  = ContextPrecision(llm=judge_llm)
recall     = ContextRecall(llm=judge_llm)

# 一条真实问答流水账:问题、检索到的块、生成的答案、人工标注的参考答案
sample = dict(
    user_input="D 级供应商会受到什么处理?",
    retrieved_contexts=["评价等级分为 A、B、C、D 四级,被评为 D 级的供应商将暂停合作资格。"],
    response="评价等级分四级,D 级供应商将暂停合作并被列入黑名单三年。",
    reference="D 级供应商将被暂停合作资格。",
)

r1 = faith.score(user_input=sample["user_input"], response=sample["response"],
                 retrieved_contexts=sample["retrieved_contexts"])
r2 = recall.score(user_input=sample["user_input"], reference=sample["reference"],
                  retrieved_contexts=sample["retrieved_contexts"])

print(r1.value)   # ≈0.67 ------ 分数
print(r1.reason)  # "第 3 条 claim「列入黑名单三年」无法从上下文推断" ------ 打分的理由

两个细节值得记住:

  1. score() 同步 / ascore() 异步 ,每个指标返回的不是裸分数,是 MetricResult------.reason 字段是裁判的判卷理由。分数告诉你"有病",理由直接告诉你"病灶在哪",批量跑完先把低分样本的 reason 捞出来看,比盯分布图高效得多;
  2. 各指标吃的输入不一样(对照 2.4 的全景表):Answer Relevancy 不需要上下文、Context Recall 必须要 reference------传参前先想清楚"这个指标在跟谁比"。

因果要点:因为裁判是 LLM,所以评估代码的骨架就是"把流水账(问题/上下文/答案/参考答案)逐条递给裁判"------真正的工程量不在调用,而在流水账从哪来,这就是下一层。

第四层:没有标注数据怎么办------让题库自己长出来

第三层的 sample 里,reference 是人工写的。100~200 条 golden set 是评估的入场券,但人工标注是体力活。两条路:

路一:人工标注(最准,兜底)。从真实用户 query 日志里抽样,人工写参考答案。覆盖了真实分布,但慢。

路二:TestsetGenerator 合成(快,冷启动)。RAGAS 内置的合成器直接从你的文档自动造出 (问题, 上下文, 参考答案) 三元组。原理不是"让 LLM 随便编题",而是两步结构化生成:
#mermaid-svg-nzuYcibAqfQUKaBq{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-nzuYcibAqfQUKaBq .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-nzuYcibAqfQUKaBq .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-nzuYcibAqfQUKaBq .error-icon{fill:#552222;}#mermaid-svg-nzuYcibAqfQUKaBq .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-nzuYcibAqfQUKaBq .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-nzuYcibAqfQUKaBq .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-nzuYcibAqfQUKaBq .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-nzuYcibAqfQUKaBq .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-nzuYcibAqfQUKaBq .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-nzuYcibAqfQUKaBq .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-nzuYcibAqfQUKaBq .marker{fill:#333333;stroke:#333333;}#mermaid-svg-nzuYcibAqfQUKaBq .marker.cross{stroke:#333333;}#mermaid-svg-nzuYcibAqfQUKaBq svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-nzuYcibAqfQUKaBq p{margin:0;}#mermaid-svg-nzuYcibAqfQUKaBq .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-nzuYcibAqfQUKaBq .cluster-label text{fill:#333;}#mermaid-svg-nzuYcibAqfQUKaBq .cluster-label span{color:#333;}#mermaid-svg-nzuYcibAqfQUKaBq .cluster-label span p{background-color:transparent;}#mermaid-svg-nzuYcibAqfQUKaBq .label text,#mermaid-svg-nzuYcibAqfQUKaBq span{fill:#333;color:#333;}#mermaid-svg-nzuYcibAqfQUKaBq .node rect,#mermaid-svg-nzuYcibAqfQUKaBq .node circle,#mermaid-svg-nzuYcibAqfQUKaBq .node ellipse,#mermaid-svg-nzuYcibAqfQUKaBq .node polygon,#mermaid-svg-nzuYcibAqfQUKaBq .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-nzuYcibAqfQUKaBq .rough-node .label text,#mermaid-svg-nzuYcibAqfQUKaBq .node .label text,#mermaid-svg-nzuYcibAqfQUKaBq .image-shape .label,#mermaid-svg-nzuYcibAqfQUKaBq .icon-shape .label{text-anchor:middle;}#mermaid-svg-nzuYcibAqfQUKaBq .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-nzuYcibAqfQUKaBq .rough-node .label,#mermaid-svg-nzuYcibAqfQUKaBq .node .label,#mermaid-svg-nzuYcibAqfQUKaBq .image-shape .label,#mermaid-svg-nzuYcibAqfQUKaBq .icon-shape .label{text-align:center;}#mermaid-svg-nzuYcibAqfQUKaBq .node.clickable{cursor:pointer;}#mermaid-svg-nzuYcibAqfQUKaBq .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-nzuYcibAqfQUKaBq .arrowheadPath{fill:#333333;}#mermaid-svg-nzuYcibAqfQUKaBq .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-nzuYcibAqfQUKaBq .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-nzuYcibAqfQUKaBq .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-nzuYcibAqfQUKaBq .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-nzuYcibAqfQUKaBq .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-nzuYcibAqfQUKaBq .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-nzuYcibAqfQUKaBq .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-nzuYcibAqfQUKaBq .cluster text{fill:#333;}#mermaid-svg-nzuYcibAqfQUKaBq .cluster span{color:#333;}#mermaid-svg-nzuYcibAqfQUKaBq div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-nzuYcibAqfQUKaBq .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-nzuYcibAqfQUKaBq rect.text{fill:none;stroke-width:0;}#mermaid-svg-nzuYcibAqfQUKaBq .icon-shape,#mermaid-svg-nzuYcibAqfQUKaBq .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-nzuYcibAqfQUKaBq .icon-shape p,#mermaid-svg-nzuYcibAqfQUKaBq .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-nzuYcibAqfQUKaBq .icon-shape .label rect,#mermaid-svg-nzuYcibAqfQUKaBq .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-nzuYcibAqfQUKaBq .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-nzuYcibAqfQUKaBq .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-nzuYcibAqfQUKaBq :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 文档集合
① 建知识图谱

切块成节点 → NER/关键词抽取

→ 按实体相似度连边
② 按场景合成

Scenario = 节点组合 × 题型

× 问句长度 × 风格
(问题, 相关上下文, 参考答案)

测试集

第一步建图,是为了让"多跳题"有据可依------两块都提到"黑名单"的 chunk 被实体边连起来,合成器才能造出"串标行为和黑名单处罚有什么关系"这类需要跨块推理 的题。第二步按 Scenario 组合,是为了题型分布可控

python 复制代码
from ragas.testset import TestsetGenerator
from ragas.testset.synthesizers import (
    SingleHopSpecificQuerySynthesizer, SingleHopAbstractQuerySynthesizer,
    MultiHopSpecificQuerySynthesizer,  MultiHopAbstractQuerySynthesizer,
)

generator = TestsetGenerator(llm=judge_llm, embedding_model=judge_emb)
testset = generator.generate_with_langchain_docs(docs, testset_size=100)
# 进阶:用 query_distribution 控制四类题的比例(单跳/多跳 × 具体/抽象)

合成数据的铁律:参考答案是 LLM 生成的,本身必须抽查。 建议流程:先合成 100 条 → 人工过一遍、删掉答案离谱的 → 剩下的冻结成 golden set → 之后每次改动都跑这同一套题。题不换,分才可比。

因果要点:因为 Context Recall 必须有 reference,而人工标注慢,所以冷启动靠合成、长期靠真实 query 沉淀------golden set 一旦冻结就是资产,它的质量上限就是你能发现的坏case的上限

第五层:分数低了然后呢------按指标组合归因到链路旋钮

单个分数只报病,指标组合才是诊断书。对照上一篇的链路层号:

化验单组合 诊断 回哪层拧旋钮
Faithfulness 低,Context Recall 高 捞对了但生成在编 十一层:prompt 溯源约束、拒答机制
Faithfulness 高,Context Recall 低 生成诚实但检索漏了("忠于残缺资料的不完整答案") 三层切分 / 四层 embedding / 六层 query 改写
Context Precision 低 噪音块占位靠前 八层融合策略 / 九层 reranker
Answer Relevancy 低,其余正常 答非所问 六层 query 理解 / 十一层 prompt 指令
四项全低 系统性错位,先查数据 索引是否建在错误版本的文档上

第一种组合最常见也最危险的是它的镜像------高 Recall 低 Faithfulness :系统找到了对的文档,然后在上面添油加醋,答案还挂着引用编号,因为看起来可信所以更危险

归因完顺手做一件事:把低分样本的 .reason 汇总归类。Faithfulness 低分样本的 reason 集中出现"资料未提及",是生成问题;集中出现"上下文与结论矛盾",先怀疑是不是检索把两个相似条款都捞进来互相干扰------病在召回不在生成。这就是为什么第三层说 reason 比分数值钱。

因果要点:因为四个指标盯的是链路的不同段,所以分数矩阵 × reason 文本 = 从症状到旋钮的映射表------评估的产出物不是报告,是下一轮的改动清单。

第六层:防回退------把评估焊进 CI

评估只在发版前跑一次,等于没评估。正确的姿势是像单元测试一样:每次改动(换 embedding、调切分、改 prompt)都自动跑 golden set,分数跌破阈值就拦下

python 复制代码
# eval_gate.py ------ CI 质量门禁(精简骨架)
import sys, statistics

THRESHOLDS = {
    "faithfulness":      0.85,
    "answer_relevancy":  0.80,
    "context_precision": 0.70,
    "context_recall":    0.75,
}

scores = {k: [] for k in THRESHOLDS}
for sample in golden_set:                      # 冻结的 golden set
    result = run_my_rag_pipeline(sample["user_input"])   # 你的系统跑一遍
    scores["faithfulness"].append(
        faith.score(user_input=sample["user_input"], response=result.answer,
                    retrieved_contexts=result.contexts).value)
    # ... 其余三个指标同理

failed = [k for k, th in THRESHOLDS.items() if statistics.mean(scores[k]) < th]
if failed:
    print("质量门禁未通过:", {k: statistics.mean(scores[k]) for k in failed})
    sys.exit(1)                                # 非零退出码 → CI 标红、阻断合并

生产化三个注意(每一条都是第一层埋的"裁判变量"的回收):

  1. 锁定裁判模型版本 :judge 从 gpt-4o-mini 换成新版,分数线会整体漂移------阈值瞬间失真。裁判版本、prompt、数据集三者必须一起冻结,换裁判要重新校准阈值;
  2. 评估作业慢且贵:每条样本四个指标 ≈ 十几次 LLM 调用,100 条就是上千次。CI 里给评估作业设独立超时(15~20 分钟)、大数据集分批跑;日常 PR 可以只跑抽样子集,全量留给 nightly;
  3. 别用 deprecated 的 evaluate() :0.4.x 里整表跑的 evaluate() 已标记弃用(官方推荐 @experiment 装饰器做实验追踪),逐指标 score() 的 collections 写法既不受影响也更适合门禁脚本。

#mermaid-svg-vB3zIp0BvvwlIo0t{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-vB3zIp0BvvwlIo0t .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-vB3zIp0BvvwlIo0t .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-vB3zIp0BvvwlIo0t .error-icon{fill:#552222;}#mermaid-svg-vB3zIp0BvvwlIo0t .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-vB3zIp0BvvwlIo0t .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-vB3zIp0BvvwlIo0t .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-vB3zIp0BvvwlIo0t .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-vB3zIp0BvvwlIo0t .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-vB3zIp0BvvwlIo0t .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-vB3zIp0BvvwlIo0t .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-vB3zIp0BvvwlIo0t .marker{fill:#333333;stroke:#333333;}#mermaid-svg-vB3zIp0BvvwlIo0t .marker.cross{stroke:#333333;}#mermaid-svg-vB3zIp0BvvwlIo0t svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-vB3zIp0BvvwlIo0t p{margin:0;}#mermaid-svg-vB3zIp0BvvwlIo0t .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-vB3zIp0BvvwlIo0t .cluster-label text{fill:#333;}#mermaid-svg-vB3zIp0BvvwlIo0t .cluster-label span{color:#333;}#mermaid-svg-vB3zIp0BvvwlIo0t .cluster-label span p{background-color:transparent;}#mermaid-svg-vB3zIp0BvvwlIo0t .label text,#mermaid-svg-vB3zIp0BvvwlIo0t span{fill:#333;color:#333;}#mermaid-svg-vB3zIp0BvvwlIo0t .node rect,#mermaid-svg-vB3zIp0BvvwlIo0t .node circle,#mermaid-svg-vB3zIp0BvvwlIo0t .node ellipse,#mermaid-svg-vB3zIp0BvvwlIo0t .node polygon,#mermaid-svg-vB3zIp0BvvwlIo0t .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-vB3zIp0BvvwlIo0t .rough-node .label text,#mermaid-svg-vB3zIp0BvvwlIo0t .node .label text,#mermaid-svg-vB3zIp0BvvwlIo0t .image-shape .label,#mermaid-svg-vB3zIp0BvvwlIo0t .icon-shape .label{text-anchor:middle;}#mermaid-svg-vB3zIp0BvvwlIo0t .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-vB3zIp0BvvwlIo0t .rough-node .label,#mermaid-svg-vB3zIp0BvvwlIo0t .node .label,#mermaid-svg-vB3zIp0BvvwlIo0t .image-shape .label,#mermaid-svg-vB3zIp0BvvwlIo0t .icon-shape .label{text-align:center;}#mermaid-svg-vB3zIp0BvvwlIo0t .node.clickable{cursor:pointer;}#mermaid-svg-vB3zIp0BvvwlIo0t .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-vB3zIp0BvvwlIo0t .arrowheadPath{fill:#333333;}#mermaid-svg-vB3zIp0BvvwlIo0t .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-vB3zIp0BvvwlIo0t .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-vB3zIp0BvvwlIo0t .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vB3zIp0BvvwlIo0t .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-vB3zIp0BvvwlIo0t .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vB3zIp0BvvwlIo0t .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-vB3zIp0BvvwlIo0t .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-vB3zIp0BvvwlIo0t .cluster text{fill:#333;}#mermaid-svg-vB3zIp0BvvwlIo0t .cluster span{color:#333;}#mermaid-svg-vB3zIp0BvvwlIo0t div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-vB3zIp0BvvwlIo0t .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-vB3zIp0BvvwlIo0t rect.text{fill:none;stroke-width:0;}#mermaid-svg-vB3zIp0BvvwlIo0t .icon-shape,#mermaid-svg-vB3zIp0BvvwlIo0t .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vB3zIp0BvvwlIo0t .icon-shape p,#mermaid-svg-vB3zIp0BvvwlIo0t .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-vB3zIp0BvvwlIo0t .icon-shape .label rect,#mermaid-svg-vB3zIp0BvvwlIo0t .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vB3zIp0BvvwlIo0t .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-vB3zIp0BvvwlIo0t .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-vB3zIp0BvvwlIo0t :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 通过
跌破
改动:换embedding /

调切分 / 改prompt
CI 触发

golden set 全量回归
四指标均值

≥ 阈值?
合并,记录分数存档
阻断合并

输出低分样本 + reason
按第五层矩阵归因

修完重跑

因果要点:因为评估的价值在"回归"而不在"验收",所以评估系统必须满足三条件才能进 CI------数据集冻结、裁判冻结、阈值门禁。不满足这三条的评估分数,只是看起来很像数据的噪音。


版本边界:RAGAS 的三张脸(照抄网上教程前必读)

RAGAS 迭代极快,API 已经换过两次脸,网上教程绝大多数是老版本:

版本 时间 长什么样
0.1.x ~2024 evaluate() 直接吃 HuggingFace Dataset;字段名 question/answer/contexts;指标是小写单例 faithfulness;TestsetGenerator 用 from_langchain(critic_llm) + evolutions.simple/reasoning
0.2.x 2025 Q1 字段全改名question→user_inputanswer→responsecontexts→retrieved_contextsanswer_relevancy→ResponseRelevancyevaluate() 改吃 EvaluationDataset;指标必须类实例化并显式传 llm
0.4.x(当前) 2025-12 起 collections APIragas.metrics.collections.Faithfulnessllm_factory() 统一裁判配置(弃 LangchainLLMWrapper);score/ascore(**kwargs) 返回 MetricResult.value + .reason);evaluate() 标记弃用、推荐 @experiment;TestsetGenerator 走 KG + Scenario(query_distribution 用 Synthesizer 类比例)

两个实用提醒:

  • 旧 API 在 0.4 里已弃用、1.0 将移除------新项目直接上 collections API;
  • 仓库已从 explodinggradients/ragas 改名 vibrantlabsai/ragas(旧地址会跳转),搜 issue 和 PR 别找错地方。

取舍总览

环节 核心矛盾 主流取舍 代价
裁判选择 评得准 vs 评得起 强模型(GPT-4o 级)当裁判,小模型 HHEM 做 Faithfulness 验证平替 裁判能力即分数可信度上限
数据集 真实分布 vs 冷启动速度 TestsetGenerator 合成起步 + 人工抽查 + 真实 query 沉淀替换 合成 reference 需人工过滤
指标 全面 vs 调用成本 四指标全跑做诊断,日常 CI 可裁剪(如只守 Faithfulness + Recall) 裁剪会漏掉对应科室的病
CI 门禁 严格防回退 vs 流水线时长 阈值门禁 + PR 抽样跑 + nightly 全量 抽样有漏检概率
阈值 防倒退 vs 防误伤 裁判/数据/阈值三者绑定冻结,换裁判重新校准 校准本身是体力活

后记

回头看这条链:因为人工和字面指标都供不起"高频、统一、懂语义"的阅卷,所以 LLM 当裁判;因为 RAG 的病分检索和生成两科,所以四个指标各防一种病、算法各为其病设计;因为裁判按次收费且自身有偏差,所以数据集要合成起步、裁判要锁定版本;因为评估的价值在回归不在验收,所以分数要归因成改动清单、门禁要焊进 CI。

RAGAS 给的不是四个分数,是一整套"让 RAG 调优从玄学变工程"的操作系统------分数是表象,归因是本体,CI 是落地。

相关推荐
SLD_Allen3 小时前
RAG for Agent:Agent 什么时候需要查知识库?
agent·rag
一只小bit1 天前
LlamaIndex框架:简单RAG框架的全过程实现手册
llm·milvus·rag·llamaindex·deepseek
云烟成雨TD1 天前
LlamaIndex 系列【24】检索增强策略:交叉编码器(Cross‑Encoder)
ai·agent·rag·llamaindex
码农哈丁1 天前
用 Trait 给系统留出后路:RAG 存储层如何做到 5 种后端可插拔
人工智能·知识图谱·rag
2601_962077712 天前
Spring AI企业级RAG落地实战:从基础架构到生产级高可用方案
知识库问答·rag·springai·企业级架构·高可用方案
AI-Frontiers2 天前
面向AIGC的检索增强生成综述
rag
东莞市云毅网络有限公司2 天前
企业知识库问答的自动化评测:构建 golden set 与回归脚本
python·rag·检索·企业知识库·评测集
东莞市云毅网络有限公司3 天前
企业非结构化资料拆分成可引用片段:一套能跑通的预处理流水线
python·数据清洗·rag·企业知识库·文档解析
像风一样自由20204 天前
23.OCR在知识库中的作用扫描件和图片文字如何进入RAG
postgresql·大模型·ocr·rag