欢迎光临
我们一直在努力

Day75-RAG评估体系:用RAGAS框架量化你的RAG质量

一、调参不能靠"看起来还行"

前几篇我们做了文档解析、切块、混合检索、重排序。但一个灵魂问题:你怎么知道改完之后变好了?

"我把 similarityThreshold 从 0.7 调到 0.75,感觉回答准了点。"——"感觉"不是证据。

没有量化评估,你今天改的"优化"可能明天就被另一个"感觉"推翻,RAG 系统在反复横跳中越来越烂。

RAGAS 就是把"感觉"变成分数的工具。 它用 LLM 当裁判,自动给每一轮问答打 4 个指标,你改任何参数都能看到曲线变化。


二、RAGAS 四大指标讲人话

指标衡量什么谁的责任高分意味着
Context Precision 召回的文档排得对不对 检索 相关文档排前面,噪声靠后
Context Recall 该召回的有没有漏 检索 答案需要的知识都在 context 里
Faithfulness 答案有没有编造 生成 每句话都能在 context 里找到依据
Answer Relevancy 答案对不对题 生成 直接回答用户问题,不跑题

关键点:Faithfulness 和 Context Recall 才是业务最在意的——一个回答再流畅,如果编造了合同条款(低 Faithfulness),就是事故。


三、评估数据集:你得先有"标准答案"

RAGAS 不是凭空打分,它需要一份评估集:每一条包含「问题 + 标准答案(ground truth)+ 上下文」。

package com.example.rag.eval;

import java.util.List;

/**
* 评估用例:最小可用结构
* 生产环境至少准备 50~100 条覆盖核心场景的用例
*/
public record EvalCase(
       String question,            // 用户问题
       String referenceAnswer,      // 人工标注的标准答案(用于算 Answer Relevancy / Recall)
       List<String> groundTruthCtx // 该问题"应该"召回的文档片段(用于算 Context Recall)
) {}
package com.example.rag.eval;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.core.io.ClassPathResource;

import java.io.InputStream;
import java.util.List;

/**
* 从 YAML/JSON 加载评估集(按 Day 69 的 Prompt 版本管理思路,评估集也要纳入版本控制)
*/
public class EvalDatasetLoader {

   private static final ObjectMapper MAPPER = new ObjectMapper();

   public static List<EvalCase> load(String path) throws Exception {
       try (InputStream is = new ClassPathResource(path).getInputStream()) {
           return MAPPER.readValue(is, new TypeReference<List<EvalCase>>() {});
      }
  }
}


四、评分器:用 LLM 当裁判

RAGAS 的核心是"LLM-as-Judge"——用一个判定模型,针对每条用例的「问题/回答/上下文」给出结构化评分。

package com.example.rag.eval;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.stereotype.Component;

import java.util.Map;

/**
* Faithfulness 评分器:判断答案是否忠于上下文(有没有幻觉)
* 用结构化输出把 AI 的"是/否/依据"变成可计算的数字
*/
@Component
public class FaithfulnessScorer {

   private final ChatClient judge;   // 建议用便宜的模型当裁判(如 qwen-plus),省钱

   public FaithfulnessScorer(ChatClient.Builder builder) {
       this.judge = builder.build();
  }

   // 提示词:让裁判逐句核对,输出"支撑句"列表
   private static final String PROMPT = """
       你是一个严格的RAG质量评审。
       给定【用户问题】【参考上下文】【模型回答】,请判断回答是否完全基于上下文、没有编造。
       逐句检查回答,列出每句话在上下文中能找到依据的支撑句。
       最后输出 JSON:{"faithful": true/false, "claims": [支撑句列表], "hallucinations": [编造句子列表]}
       【用户问题】{question}
       【参考上下文】{context}
       【模型回答】{answer}
       """;

   public double score(String question, String context, String answer) {
       String json = judge.prompt()
              .user(u -> u.text(PROMPT)
                      .param("question", question)
                      .param("context", context)
                      .param("answer", answer))
              .call().content();

       // 解析 JSON:faithful=true 给 1.0,否则 0.0(生产中可细分到"编造句占比")
       return json.contains("\\"faithful\\": true") ? 1.0 : 0.0;
  }
}

Faithfulness 的实现逻辑:让裁判把回答拆成"claim",逐个检查 context 里有没有依据。所有 claim 都有依据 → 1.0;有一条编造 → 0.0(或更细:有依据 claim 数 / 总 claim 数)。我建议生产用占比而非非黑即白,曲线更平滑。


五、Context Recall:该召回的有没有漏

package com.example.rag.eval;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.stereotype.Component;

/**
* Context Recall:判断"标准答案所依赖的知识"是否都在召回的 context 里
* 这一步用 groundTruth + 召回context 比对,不依赖模型回答
*/
@Component
public class ContextRecallScorer {

   private final ChatClient judge;

   public ContextRecallScorer(ChatClient.Builder builder) { this.judge = builder.build(); }

   public double score(String question, String referenceAnswer, String retrievedContext) {
       String prompt = """
           给定【用户问题】【标准答案】【召回的上下文】,判断标准答案中需要的事实,
           有多少能在召回上下文里找到。输出 JSON:{"tp": 命中事实数, "fn": 漏掉事实数}
           """;
       String json = judge.prompt().user(u -> u.text(prompt)
              .param("question", question)
              .param("referenceAnswer", referenceAnswer)
              .param("retrievedContext", retrievedContext)).call().content();

       // Recall = TP / (TP + FN)
       int tp = extract(json, "tp"), fn = extract(json, "fn");
       return (tp + fn) == 0 ? 0.0 : (double) tp / (tp + fn);
  }

   private int extract(String json, String key) {
       // 简化解析:生产用 JSON 库
       int i = json.indexOf("\\"" + key + "\\":");
       if (i < 0) return 0;
       return Integer.parseInt(json.substring(i + key.length() + 3).trim().split("[,\\\\s}]")[0]);
  }
}


六、一键跑出评估报告

把上面四个评分器串起来,对每个用例跑一遍,汇总成一张表:

package com.example.rag.eval;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class RagEvaluator {

   private final FaithfulnessScorer faithfulness;
   private final ContextRecallScorer recall;
   // … Precision / AnswerRelevancy 类似

   public EvalReport run(List<EvalCase> cases, RagPipeline pipeline) {
       List<Row> rows = cases.stream().map(c -> {
           // 1. 跑你的 RAG 管线,拿到回答和召回的 context
           RagResult r = pipeline.answer(c.question());
           // 2. 四项打分
           double f = faithfulness.score(c.question(), r.context(), r.answer());
           double rec = recall.score(c.question(), c.referenceAnswer(), r.context());
           return new Row(c.question(), f, rec, r.answer());
      }).toList();

       // 3. 汇总均值
       return new EvalReport(
               avg(rows, Row::faithfulness),
               avg(rows, Row::contextRecall)
      );
  }

   private double avg(List<Row> rows, java.util.function.ToDoubleFunction<Row> f) {
       return rows.stream().mapToDouble(f).average().orElse(0.0);
  }

   public record Row(String question, double faithfulness, double contextRecall, String answer) {}
   public record EvalReport(double faithfulness, double contextRecall) {}
}

用法:改一次 similarityThreshold,跑一遍 RagEvaluator.run(),记下分数;再改一次,再跑。两次分数对比,就知道这次调参是"真优化"还是"自嗨"。

我自己的经验阈值(技术文档问答):

指标及格线优秀线
Faithfulness ≥ 0.85 ≥ 0.95
Context Recall ≥ 0.75 ≥ 0.90
Context Precision ≥ 0.70 ≥ 0.85
Answer Relevancy ≥ 0.80 ≥ 0.92

七、建议

  • 评估集最小 50 条,且要覆盖"难例"。光放简单问题,分数全是 0.95 没意义。把线上真实翻车的 query 沉淀进评估集,分数才反映真实质量。

  • 裁判模型用便宜的。Faithfulness 这种判定不需要 GPT-4o,qwen-plus / deepseek 足够,评估跑 100 条能省几十块。

  • 把评估接进 CI。每次改检索参数/换 Embedding 模型,自动跑评估并对比基线,低于阈值就阻断合并——把"凭感觉"变成"门禁"。


  • 没有评估的 RAG 优化,都是盲人摸象。RAGAS 给你一双眼睛。

    你无法优化你无法测量的东西。

    下篇预告:Day 76 我们跳出"问答",进入 AI Agent——用 LangChain4j 这个 Java 生态首选框架,把"工具调用 + 记忆 + 多步推理"串起来,让 AI 不只是答,而是"做"。

    往期回顾:

    • 数据层 × 中间件AI化篇:MySQL主从复制与读写分离:延迟排查/故障切换
    • 分布式事务解决方案演进
    • JDBC、Hibernate、MyBatis、JPA持久层技术的四次进化
    赞(0)
    未经允许不得转载:171主机测评 » Day75-RAG评估体系:用RAGAS框架量化你的RAG质量
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址