大模型开发 - SpringAI之RAG应用效果评估

文章目录

引言

在前面的文章中,我们详细介绍了如何使用Spring AI框架构建RAG(检索增强生成)系统。RAG通过从外部知识库检索相关文档,然后将其注入到LLM的提示词中,使模型能够基于特定领域的知识生成更准确的答案。

然而,一个关键问题是:我们如何确保RAG系统的质量?我们生成的答案真的是基于检索到的文档吗?还是模型产生了幻觉,凭空编造了信息?

这正是本文要解决的问题。我们将介绍Spring AI提供的RelevancyEvaluator(相关性评估器),一个强大的工具,用于评估RAG生成结果的质量。通过评估,我们可以:

  1. 检测模型是否产生了幻觉
  2. 衡量生成答案与检索文档的一致性
  3. 持续监控和改进RAG系统的质量
  4. 识别需要优化的薄弱环节

本文将深入探讨RAG评估的重要性、原理、实现方式,以及如何在生产环境中应用这些技术。


一、RAG系统的核心问题:幻觉(Hallucination)

什么是LLM幻觉?

LLM(大语言模型)的幻觉是指模型生成看起来合理但实际上不真实、无根据或与输入文本不一致的内容的现象。在RAG场景中,幻觉尤为危险,因为用户期望模型的答案完全基于提供的检索文档。

例如:

  • 用户问:"周瑜老师的微信号是多少?"
  • 检索到的文档:"周瑜老师的公众号是IT周瑜,微信号可以通过公众号菜单获取。"
  • 模型的错误答案:"我是周瑜"(完全与文档无关)

这种情况下,模型的回答不仅没有基于检索文档,反而制造了不存在的信息。

为什么会产生幻觉?

  1. 训练数据的影响:模型在训练过程中学到的广泛知识,可能在RAG场景中导致模型"越权"回答,而不是严格依赖检索文档。

  2. 概率分布问题:模型本质上是在进行条件概率计算,有时会生成高概率但不正确的内容。

  3. 上下文长度限制:当检索文档较多时,模型可能难以有效整合所有信息。

  4. 指令遵循不足:如果系统提示词不够明确,模型可能不会严格遵循"仅基于提供的文档回答"的要求。

RAG评估的必要性

为了确保RAG系统的可信度和准确性,我们必须对生成结果进行评估。通过自动化评估,我们可以:

  • 质量控制:在响应返回给用户前发现问题
  • 持续改进:识别导致幻觉的模式和原因
  • 可信度量化:为每个答案赋予置信度分数
  • 决策支持:判断是否应该直接返回答案,还是需要进一步处理

二、RelevancyEvaluator:Spring AI的评估解决方案

2.1 核心概念

Spring AI提供的RelevancyEvaluator是一个智能评估器,用于评判RAG系统生成的答案是否与检索到的文档相关且一致。

其核心思想是:

使用另一个LLM来评估第一个LLM生成的答案

这样做的好处是避免了"自己评估自己"的问题,确保评估的客观性。

2.2 工作原理

RelevancyEvaluator的工作流程:

复制代码
┌─────────────────────────────────────────────────────┐
│ 1. 接收三个关键输入                                    │
│    - 原始用户问题 (question)                         │
│    - 检索到的文档列表 (documents)                     │
│    - RAG生成的答案 (ragResult)                       │
└─────────────────────────────────────────────────────┘
                           ↓
┌─────────────────────────────────────────────────────┐
│ 2. 构建评估提示词                                     │
│    系统会自动构建一个包含以下内容的提示词:           │
│    - "评估以下答案是否基于提供的文档"               │
│    - 用户原始问题                                   │
│    - 检索到的文档内容                               │
│    - 要评估的答案                                   │
└─────────────────────────────────────────────────────┘
                           ↓
┌─────────────────────────────────────────────────────┐
│ 3. 调用评估LLM                                       │
│    将评估提示词发送给评估模型(通常是高性能模型)    │
└─────────────────────────────────────────────────────┘
                           ↓
┌─────────────────────────────────────────────────────┐
│ 4. 解析评估结果                                       │
│    返回 EvaluationResponse,包含:                   │
│    - 相关性分数 (0-1,越接近1越相关)               │
│    - 评估反馈和解释                                 │
└─────────────────────────────────────────────────────┘

2.3 为什么不用同一个模型进行评估?

你可能会问:为什么不直接用生成答案的模型来评估自己的答案?

原因如下

  1. 偏见性:模型倾向于为自己的输出评高分,就像人类倾向于为自己的作品评高分一样。

  2. 缺乏客观性:模型无法真正的"理解"自己是否产生了幻觉,因为它已经"相信"自己的输出。

  3. 评估标准不一致:用于生成答案的模型和用于评估的模型应该遵循不同的指令和标准。

Spring AI的解决方案 :使用一个独立的评估模型 (通常是另一个高性能LLM)来客观评估答案质量。这就是为什么RelevancyEvaluator接收一个ChatClient.Builder参数,允许你指定评估用的模型。


三、EvaluationRequest与EvaluationResponse

3.1 EvaluationRequest:评估请求的三要素

EvaluationRequest是评估的输入,包含三个核心要素:

java 复制代码
EvaluationRequest evaluationRequest = new EvaluationRequest(
    question,        // 用户原始问题:帮助评估器理解问题的背景
    documents,       // 检索到的文档:评估答案是否基于这些文档
    ragResult        // RAG系统生成的答案:需要被评估的内容
);

三个要素的含义

  1. question(问题)

    • 用户的原始查询
    • 作用:为评估器提供上下文,帮助理解问题的意图
    • 示例:"周瑜老师是谁?"
  2. documents(文档)

    • 从向量数据库检索到的相关文档列表
    • 每个文档包含内容和元数据
    • 作用:评估器使用这些文档作为"真理"基准
    • 示例:[文档1, 文档2, ...]
  3. ragResult(RAG结果)

    • RAG系统生成的答案
    • 作用:这是被评估的对象,评估器判断它是否与文档一致
    • 示例:"我是周瑜"

3.2 EvaluationResponse:评估结果的解读

EvaluationResponse是评估的输出,包含以下信息:

java 复制代码
public class EvaluationResponse {
    // 评估的最终判定分数 (0.0 - 1.0)
    // 1.0 表示完全相关,答案完全基于提供的文档
    // 0.0 表示完全不相关,答案与文档无关
    private double score;

    // 评估器提供的详细反馈和解释
    // 说明为什么给出这个分数
    private String feedback;
}

分数的解读

分数范围 含义 说明
0.8-1.0 高度相关 答案充分基于检索文档,可信度高
0.6-0.8 相关 答案大部分基于文档,部分有偏离
0.4-0.6 部分相关 答案与文档有关联,但包含额外推断
0.2-0.4 低相关性 答案可能存在与文档不一致的部分
0.0-0.2 不相关 答案完全或主要不基于文档,高度可疑

四、代码实现分析

4.1 完整的评估接口实现

java 复制代码
@GetMapping("/evaluation")
public EvaluationResponse evaluation(String question) {
    // 第一步:向量搜索,从知识库检索相关文档
    SearchRequest searchRequest = SearchRequest
            .builder()
            .query(question)
            .topK(1)                           // 只检索1个最相关的文档
            .similarityThreshold(0.1)          // 相似度阈值很低,几乎返回所有结果
            .build();
    List<Document> documents = vectorStore.similaritySearch(searchRequest);

    // 第二步:构建提示词,准备生成答案
    PromptTemplate promptTemplate = new PromptTemplate(
        "{question}\n\n 用以下信息回答问题:\n {contents}"
    );
    String prompt = promptTemplate.render(Map.of(
        "question", question,
        "contents", documents
    ));

    // 第三步:这里故意使用错误答案来演示评估效果
    // 注释掉真实的RAG生成逻辑:
    // String ragResult = chatClient.prompt(prompt).call().content();

    // 意图使用一个明显错误的答案
    String ragResult = "我是周瑜";

    // 第四步:创建评估器并执行评估
    RelevancyEvaluator relevancyEvaluator = new RelevancyEvaluator(chatClientBuilder);
    EvaluationRequest evaluationRequest = new EvaluationRequest(
        question,
        documents,
        ragResult
    );

    // 返回评估结果
    return relevancyEvaluator.evaluate(evaluationRequest);
}

4.2 关键设计决策分析

设计1:故意使用错误答案

代码中最巧妙的地方是:

java 复制代码
// 这里故意用一个错误答案来演示评估效果
String ragResult = "我是周瑜";

为什么要这样做?

  1. 演示评估的真实效果:用正确答案进行评估时,分数总是接近1.0,无法真正验证评估器的工作能力。

  2. 验证评估器的准确性:通过故意的错误答案,我们可以看到评估器是否能正确识别幻觉。

  3. 教学目的:清楚地展示评估器如何在答案与文档不一致时降低分数。

  4. 调试和验证:在生产环境部署前,可以用已知的"坏答案"测试评估器是否按预期工作。

设计2:低相似度阈值

java 复制代码
.topK(1)                           // 只取1个文档
.similarityThreshold(0.1)          // 阈值很低

这个设计的含义

  • 相似度阈值设为0.1(范围0-1,其中1表示完全相同)
  • 这意味着即使文档与问题的相似度很低,也会被返回
  • 目的是为了演示:即使检索到的文档不够相关,评估器也能发现生成的答案与其不一致

设计3:可换模型的评估器

java 复制代码
// 创建新的评估器实例,使用独立的模型
RelevancyEvaluator relevancyEvaluator = new RelevancyEvaluator(chatClientBuilder);

这个设计的好处

  • chatClientBuilder允许灵活配置评估用的模型
  • 你可以指定一个高性能、更精准的模型来进行评估
  • 例如,生成模型用qwen3-max,评估模型用gpt-4-turbo,不同的模型各司其职

五、评估结果的解读与应用

5.1 什么是好的评估分数?

假设我们对以下答案进行评估:

场景1:高质量答案

复制代码
问题:Spring AI框架的优势是什么?
检索文档:包含Spring AI的官方介绍、特性列表
生成答案:Spring AI提供了对多个LLM的统一接口、内置RAG支持、向量存储集成等...
预期分数:0.9-1.0(高度相关)

场景2:存在幻觉的答案(代码中的示例)

复制代码
问题:周瑜老师是谁?
检索文档:包含"周瑜是IT培训讲师,公众号IT周瑜..."
生成答案:我是周瑜
预期分数:0.1-0.3(低相关性,明显幻觉)

场景3:部分相关的答案

复制代码
问题:如何使用Spring AI进行RAG?
检索文档:包含RAG相关内容
生成答案:Spring AI支持RAG,还支持多模型集成、流式处理等...
预期分数:0.6-0.8(基本相关,但添加了额外信息)

5.2 生产环境中的应用策略

策略1:质量把关(Quality Gate)

java 复制代码
EvaluationResponse response = relevancyEvaluator.evaluate(evaluationRequest);

if (response.getScore() >= 0.8) {
    // 高可信度,直接返回答案给用户
    return answer;
} else if (response.getScore() >= 0.5) {
    // 中等可信度,返回答案但附加评估反馈
    return answer + "\n[注:此答案基于知识库信息,置信度: "
           + response.getScore() + "]";
} else {
    // 低可信度,拒绝返回,提示需要手动审查
    return "抱歉,我的答案信息不足。请查阅文档或咨询人工支持。";
}

策略2:持续监控和优化

在生产系统中,收集所有评估的分数和反馈:

java 复制代码
// 监控评估数据
monitoringService.recordEvaluation(
    question,
    ragResult,
    response.getScore(),
    response.getFeedback(),
    timestamp
);

// 定期分析
// - 哪些问题类型的评估分数较低?
// - 哪些知识库文档质量不足?
// - 是否需要调整提示词或模型参数?

策略3:迭代改进

基于评估结果进行改进:

问题 根本原因 解决方案
评估分数普遍偏低 检索文档质量差 更新知识库,优化分块策略
特定领域评估分数低 该领域文档不足 补充该领域的知识文档
偶发性低分 提示词不够明确 优化系统提示词
评估不准确 评估模型不合适 更换更强大的评估模型

六、Spring AI评估框架的其他评估器

除了RelevancyEvaluator外,Spring AI还提供其他评估器:

6.1 常见的评估维度

评估器 功能 使用场景
RelevancyEvaluator 评估答案与文档的相关性 主要用于RAG场景,检测幻觉
RagaEvaluator 综合评估(相关性、完整性等) 全面的RAG质量评估
FactualityEvaluator 评估答案的事实准确性 验证答案不包含虚假信息

6.2 组合使用评估器

实践中可以同时使用多个评估器,获得多维度的评估结果:

java 复制代码
RelevancyEvaluator relevancyEvaluator =
    new RelevancyEvaluator(chatClientBuilder);
EvaluationResponse relevancyResponse =
    relevancyEvaluator.evaluate(evaluationRequest);

// 如果Spring AI提供了其他评估器
// FactualityEvaluator factualityEvaluator =
//     new FactualityEvaluator(chatClientBuilder);
// EvaluationResponse factualityResponse =
//     factualityEvaluator.evaluate(evaluationRequest);

// 综合多个评估结果
double overallScore = (relevancyResponse.getScore()
                      // + factualityResponse.getScore()
                      ) / 1; // 可以加权平均

七、RAG评估的最佳实践

7.1 选择合适的评估模型

java 复制代码
// 高成本但高精度的评估方案
ChatClient.Builder evaluationChatClientBuilder = ChatClient.builder()
        .chatModel(openaiChatModel)  // 使用GPT-4用于评估
        .defaultSystem("你是一个严格的AI质量评估专家...");

RelevancyEvaluator evaluator =
    new RelevancyEvaluator(evaluationChatClientBuilder);

// 成本优化但仍可靠的评估方案
ChatClient.Builder fastEvaluationBuilder = ChatClient.builder()
        .chatModel(qwenChatModel)    // 使用Qwen用于评估
        .defaultSystem("你是一个AI质量评估助手...");

7.2 设置合理的评估阈值

java 复制代码
// 根据业务场景设置不同阈值
public class EvaluationThresholds {
    public static final double STRICT = 0.9;      // 医疗、法律等关键场景
    public static final double STANDARD = 0.7;    // 一般业务场景
    public static final double LENIENT = 0.5;     // 信息检索、QA场景
}

// 使用阈值
if (response.getScore() >= EvaluationThresholds.STANDARD) {
    // 满足标准,可以返回
}

7.3 定期审计评估器本身

java 复制代码
// 为了确保评估器的准确性,定期用人类标注的测试集进行验证
List<TestCase> testCases = new ArrayList<>();
testCases.add(new TestCase(
    "测试问题",
    documents,
    "正确答案",
    0.95,  // 期望的评估分数
    "应该被评为高分"
));
testCases.add(new TestCase(
    "测试问题",
    documents,
    "完全错误的答案",
    0.1,   // 期望的评估分数
    "应该被评为低分"
));

// 运行评估器,对比实际分数与期望分数
for (TestCase testCase : testCases) {
    EvaluationResponse response =
        evaluator.evaluate(new EvaluationRequest(...));

    double deviation = Math.abs(
        response.getScore() - testCase.getExpectedScore()
    );

    if (deviation > 0.15) {  // 允许15%的偏差
        log.warn("评估器准确性异常: " + testCase);
    }
}

7.4 与监控系统集成

java 复制代码
// 将评估结果纳入可观测性系统
@Autowired
private MeterRegistry meterRegistry;

public void recordEvaluation(EvaluationResponse response) {
    // 记录评估分数的分布
    meterRegistry.timer("rag.evaluation.score")
            .record(Duration.ofMillis((long)(response.getScore() * 1000)));

    // 统计低分答案的比例
    if (response.getScore() < 0.5) {
        meterRegistry.counter("rag.evaluation.low_score").increment();
    }

    // 分析反馈内容,提取关键问题
    analyzeAndLogFeedback(response.getFeedback());
}

八、代码演示与预期结果

8.1 调用评估接口

bash 复制代码
# 使用错误答案"我是周瑜"进行评估
curl "http://localhost:8081/evaluation?question=周瑜是谁"

8.2 预期的评估结果

json 复制代码
{
  "score": 0.15,
  "feedback": "答案'我是周瑜'与提供的文档内容不一致。文档讨论的是周瑜作为讲师和公众号作者的身份信息,但答案没有提供任何关于周瑜的具体信息。答案显示出明显的幻觉,未能基于提供的知识库内容进行回答。"
}

8.3 分数解读

  • 分数:0.15(在0-1范围内,1表示完全相关)
  • 含义:答案与检索文档的相关性极低,明确存在幻觉
  • 建议:不应该返回这个答案给用户

8.4 对比:正确答案的评估

如果我们使用真实的RAG结果:

java 复制代码
// 注释掉故意的错误答案
// String ragResult = "我是周瑜";

// 使用真实的生成答案
String ragResult = chatClient.prompt(prompt).call().content();
// 可能返回:"周瑜是一位AI技术讲师和培训专家,通过公众号IT周瑜分享AI学习内容。"

预期结果

json 复制代码
{
  "score": 0.92,
  "feedback": "答案准确地从提供的文档中提取了关键信息,包括周瑜的身份(讲师、专家)和传播渠道(公众号IT周瑜)。答案直接基于知识库内容,没有发现显著的不一致或幻觉。"
}

九、常见陷阱与解决方案

陷阱1:过度依赖单次评估

java 复制代码
// 错误:仅基于一次评估结果决策
if (response.getScore() > 0.8) {
    return answer;
}

// 正确:多次评估取平均值
double totalScore = 0;
for (int i = 0; i < 3; i++) {
    totalScore += evaluator.evaluate(evaluationRequest).getScore();
}
double averageScore = totalScore / 3;

陷阱2:忽视评估的成本

java 复制代码
// 评估需要额外的LLM调用,会增加延迟和成本
// 解决方案:使用更经济的模型进行评估
ChatClient.Builder cheapEvaluator = ChatClient.builder()
        .chatModel(qwenTurboModel)  // 使用快速、廉价的模型
        .build();

陷阱3:不考虑评估延迟

java 复制代码
// 异步评估:不阻塞主流程
CompletableFuture<EvaluationResponse> evaluationFuture =
    CompletableFuture.supplyAsync(() ->
        evaluator.evaluate(evaluationRequest)
    );

// 立即返回答案,后台异步记录评估结果
return answer;

// 在后台处理评估结果
evaluationFuture.thenAccept(response -> {
    monitoringService.recordEvaluation(response);
});

十、总结与展望

10.1 核心要点回顾

  1. RAG评估的必要性:幻觉问题是LLM系统的重要风险,必须有机制检测。

  2. RelevancyEvaluator的价值:通过调用独立的评估模型,客观地衡量生成答案的质量。

  3. 三要素模型:问题、文档、答案是评估的完整输入。

  4. 分数的应用:不同分数范围代表不同的可信度级别,应转化为具体的业务决策。

  5. 持续改进:将评估结果反馈到RAG系统的优化中,形成闭环改进机制。

10.2 实际应用场景

医疗咨询系统

复制代码
- 评估分数 < 0.8:拒绝回答,建议咨询医生
- 评估分数 > 0.8:显示答案,附加医学免责声明

客服机器人

复制代码
- 评估分数 < 0.6:升级到人工客服
- 评估分数 > 0.6:直接返回答案

内容检索平台

复制代码
- 为每个结果显示置信度指示器
- 允许用户过滤低置信度结果

10.3 未来方向

  1. 多维度评估:不仅评估相关性,还评估完整性、准确性、流畅性。

  2. 实时反馈循环:根据用户点击、评分等信号,动态调整评估阈值。

  3. 自适应评估:根据不同的问题类型和领域,使用不同的评估标准。

  4. 评估器的评估:定期验证评估模型本身的准确性,确保评估的有效性。


参考资源


小结

RAG系统的成功不仅取决于检索和生成的好坏,还取决于我们如何评估和监控 这个系统的质量。RelevancyEvaluator为我们提供了一个强大且易用的工具。

在生产环境中,不要忽视评估这一环节。每一个评估分数都是改进系统的机会,每一个检测到的幻觉都可能挽救一个用户体验。

持续监控,持续改进,让RAG系统真正可信。

相关推荐
小小工匠11 小时前
大模型开发 - SpringAI 之高级 RAG 组件
rag·spring ai
小楼v1 天前
⭐解锁RAG与Spring AI的实战应用(万字详细教学与完整步骤流程实践)
java·后端·rag·spring ai·ai大模型应用
小小工匠1 天前
大模型开发 - SpringAI之MySQL存储ChatMemory
mysql·spring ai
腾飞开源2 天前
104_Spring AI 干货笔记之开发时服务
人工智能·docker compose·容器管理·spring ai·testcontainers·开发时服务·ssl支持
小小工匠3 天前
大模型开发 - Spring AI 1.1.0 之基础使用:从零开始构建智能应用
spring ai
callJJ3 天前
Spring AI Tool Calling(工具调用)详解——让大模型拥有“动手能力“
java·人工智能·spring·spring ai·tool calling
予枫的编程笔记7 天前
【YF技术周报 Vol.01】OpenAI 国会指控 DeepSeek,字节发布 Seedance 2.0,Java 26 预览版来了
java·人工智能·openai·后端开发·ai技术·spring ai·deepseek
摇滚侠9 天前
JWT 是 token 的一种格式,我的理解对吗?
java·人工智能·intellij-idea·spring ai·springaialibaba
callJJ9 天前
Spring AI 语音合成(TTS)完全指南:OpenAI Text-to-Speech
java·人工智能·spring·语音识别·spring ai