Spring AI RAG 模型评估详解

📋 目录
为什么需要模型评估
RAG 系统的局限性
虽然 RAG(检索增强生成)能够基于真实资料减少幻觉,但并不能完全消除幻觉。RAG 系统仍然可能出现以下问题:
| 问题类型 | 描述 | 影响 |
|---|---|---|
| 事实性幻觉 | 上下文提供了明确事实,但模型未读取或匹配,凭常识胡乱生成 | 回答不准确,误导用户 |
| 虚构细节 | 模型"看"到的背景信息有限,但仍然自信地"虚构"细节回答问题 | 产生错误信息,降低可信度 |
| 相关性不足 | 模型回答与用户问题不相关,或与检索到的文档不匹配 | 用户体验差,系统价值低 |
现实中的常见问题场景
场景 1:上下文提供了明确事实,但模型未读取
上下文文档:
"取消费用:经济舱 75 美元,豪华经济舱 50 美元,商务舱 25 美元。"
模型回答:
"经济舱取消费用是 100 美元。" ❌ 错误!未读取上下文中的 75 美元
场景 2:模型虚构细节
用户问题:"我叫什么名字?"
检索到的文档:
"预订航班时需要确保个人信息(姓名、ID 等)的准确性..."
模型回答:
"根据文档,您的名字是张三。" ❌ 错误!文档中并未提及用户姓名
评估的必要性
| 原因 | 说明 | 业务影响 |
|---|---|---|
| 质量保证 | 确保 RAG 系统回答的准确性和可靠性 | 避免用户收到错误信息 |
| 持续改进 | 通过评估发现系统问题,指导优化方向 | 提升系统整体质量 |
| 风险控制 | 及时发现和修复事实性错误 | 降低法律和声誉风险 |
| 成本优化 | 评估不同模型和配置的效果,选择最优方案 | 降低运营成本 |
评估器类型与对比
评估器分类
Spring AI 提供了两种主要的评估器:
| 评估器 | 英文名称 | 评估维度 | 核心功能 |
|---|---|---|---|
| 事实性评估器 | FactCheckingEvaluator |
事实准确性 | 检查 AI 回答是否与提供的上下文文档一致 |
| 相关性评估器 | RelevancyEvaluator |
相关性 | 检查 AI 回答是否与用户问题相关,是否基于检索到的文档 |
详细对比
| 特性 | FactCheckingEvaluator | RelevancyEvaluator |
|---|---|---|
| 评估目标 | 事实准确性 | 回答相关性 |
| 输入参数 | List<Document>(上下文)+ String(AI 回答) |
String(用户问题)+ List<Document>(上下文)+ String(AI 回答) |
| 评估逻辑 | 检查回答中的事实是否能在上下文中找到依据 | 检查回答是否与问题相关,是否基于上下文 |
| 适用场景 | 事实性检查、知识库问答 | 相关性检查、RAG 系统整体评估 |
| 评估粒度 | 细粒度(逐条事实检查) | 粗粒度(整体相关性) |
| LLM 调用 | 需要调用 LLM 进行事实对比 | 需要调用 LLM 进行相关性判断 |
| 返回结果 | EvaluationResponse(包含评分和解释) |
EvaluationResponse(包含评分和解释) |
评估维度对比
| 维度 | FactCheckingEvaluator | RelevancyEvaluator |
|---|---|---|
| 事实准确性 | ✅ 主要评估维度 | ⚠️ 间接评估 |
| 回答相关性 | ⚠️ 不直接评估 | ✅ 主要评估维度 |
| 上下文利用 | ✅ 严格检查 | ✅ 检查是否基于上下文 |
| 幻觉检测 | ✅ 能检测事实性幻觉 | ✅ 能检测无关回答 |
使用方式详解
1. FactCheckingEvaluator(事实性评估器)
基本用法
java
// 1. 创建评估器
var factCheckingEvaluator = new FactCheckingEvaluator(
ChatClient.builder(chatModel)
);
// 2. 准备上下文文档
List<Document> documents = List.of(
Document.builder()
.text("""
取消预订:
- 最晚在航班起飞前 48 小时取消。
- 取消费用:经济舱 75 美元,豪华经济舱 50 美元,商务舱 25 美元。
- 退款将在 7 个工作日内处理。
""")
.build()
);
// 3. AI 回答
String response = "经济舱取消费用75 美元";
// 4. 创建评估请求
EvaluationRequest evaluationRequest = new EvaluationRequest(
documents, // 上下文文档
response // AI 回答
);
// 5. 执行评估
EvaluationResponse evaluationResponse = factCheckingEvaluator.evaluate(
evaluationRequest
);
// 6. 查看结果
System.out.println(evaluationResponse);
参数说明
| 参数 | 类型 | 说明 | 示例 |
|---|---|---|---|
documents |
List<Document> |
检索到的上下文文档,包含事实依据 | 知识库文档列表 |
response |
String |
AI 模型生成的回答 | "经济舱取消费用75 美元" |
工作原理
1. 将上下文文档和 AI 回答发送给 LLM
2. LLM 分析回答中的每个事实声明
3. 检查每个事实是否能在上下文中找到依据
4. 返回评估结果:
- 评分(0.0 - 1.0)
- 解释(哪些事实正确,哪些错误)
2. RelevancyEvaluator(相关性评估器)
基本用法
java
// 1. 创建评估器
var evaluator = new RelevancyEvaluator(
ChatClient.builder(chatModel)
);
// 2. 准备评估数据
String query = "我叫什么名字"; // 用户问题
List<Document> context = chatResponse.getMetadata()
.get(RetrievalAugmentationAdvisor.DOCUMENT_CONTEXT); // 检索到的上下文
String response = chatResponse.getResult()
.getOutput().getText(); // AI 回答
// 3. 创建评估请求
EvaluationRequest evaluationRequest = new EvaluationRequest(
query, // 用户问题
context, // 检索到的上下文
response // AI 回答
);
// 4. 执行评估
EvaluationResponse evaluationResponse = evaluator.evaluate(
evaluationRequest
);
// 5. 查看结果
System.out.println(evaluationResponse);
参数说明
| 参数 | 类型 | 说明 | 示例 |
|---|---|---|---|
query |
String |
用户的实际查询问题 | "我叫什么名字" |
context |
List<Document> |
从向量数据库检索到的相关文档 | RAG 检索结果 |
response |
String |
AI 模型生成的答案 | "根据文档,您的名字是..." |
工作原理
1. 将用户问题、上下文文档和 AI 回答发送给 LLM
2. LLM 分析:
- 回答是否与用户问题相关?
- 回答是否基于提供的上下文?
- 回答是否回答了用户的问题?
3. 返回评估结果:
- 评分(0.0 - 1.0)
- 解释(相关性分析)
3. 在完整 RAG 流程中使用评估
完整示例(基于 RagEvalTest)
java
@Test
public void testRag(
@Autowired VectorStore vectorStore,
@Autowired DashScopeChatModel dashScopeChatModel) {
// 1. 准备知识库
List<Document> documents = List.of(...);
vectorStore.add(documents);
// 2. 构建 RAG Advisor
RetrievalAugmentationAdvisor advisor = RetrievalAugmentationAdvisor.builder()
.documentRetriever(VectorStoreDocumentRetriever.builder()
.vectorStore(vectorStore)
.build())
.build();
// 3. 执行 RAG 查询
String query = "我叫什么名字";
ChatResponse chatResponse = ChatClient.builder(dashScopeChatModel)
.build()
.prompt(query)
.advisors(advisor)
.call()
.chatResponse();
// 4. 提取评估所需的数据
String userQuery = query; // 用户问题
List<Document> retrievedContext = chatResponse.getMetadata()
.get(RetrievalAugmentationAdvisor.DOCUMENT_CONTEXT); // 检索到的上下文
String aiResponse = chatResponse.getResult()
.getOutput().getText(); // AI 回答
// 5. 创建评估请求
EvaluationRequest evaluationRequest = new EvaluationRequest(
userQuery, // 用户问题
retrievedContext, // 检索到的上下文
aiResponse // AI 回答
);
// 6. 执行相关性评估
RelevancyEvaluator evaluator = new RelevancyEvaluator(
ChatClient.builder(dashScopeChatModel)
);
EvaluationResponse evaluationResponse = evaluator.evaluate(evaluationRequest);
// 7. 查看评估结果
System.out.println(evaluationResponse);
System.out.println("AI 回答: " + aiResponse);
}
使用场景分析
场景 1:开发和测试阶段
目的:在集成测试中验证 RAG 系统的质量
实现方式:
java
@Test
public void testRagQuality() {
// 执行 RAG 查询
ChatResponse response = executeRagQuery("退票需要多少费用?");
// 评估回答质量
EvaluationRequest request = new EvaluationRequest(
"退票需要多少费用?",
getRetrievedContext(response),
getAiResponse(response)
);
RelevancyEvaluator evaluator = new RelevancyEvaluator(chatClient);
EvaluationResponse evalResult = evaluator.evaluate(request);
// 断言:相关性评分应该 >= 0.8
assertTrue(evalResult.getScore() >= 0.8,
"回答相关性不足: " + evalResult.getExplanation());
}
价值:
- ✅ 自动化质量检查
- ✅ 防止回归问题
- ✅ 确保系统稳定性
场景 2:批量质量检查
目的:对一批历史对话进行离线评估
实现方式:
java
public void batchEvaluate(List<Conversation> conversations) {
FactCheckingEvaluator factChecker = new FactCheckingEvaluator(chatClient);
RelevancyEvaluator relevancyChecker = new RelevancyEvaluator(chatClient);
List<EvaluationResult> results = new ArrayList<>();
for (Conversation conv : conversations) {
// 事实性评估
EvaluationRequest factRequest = new EvaluationRequest(
conv.getContext(),
conv.getResponse()
);
EvaluationResponse factResult = factChecker.evaluate(factRequest);
// 相关性评估
EvaluationRequest relevancyRequest = new EvaluationRequest(
conv.getQuery(),
conv.getContext(),
conv.getResponse()
);
EvaluationResponse relevancyResult = relevancyChecker.evaluate(relevancyRequest);
results.add(new EvaluationResult(factResult, relevancyResult));
}
// 生成评估报告
generateReport(results);
}
价值:
- ✅ 发现系统性问题
- ✅ 识别低质量回答模式
- ✅ 指导系统优化方向
场景 3:系统监控
目的:定期抽样评估生产环境中的对话质量
实现方式:
java
@Component
public class RAGQualityMonitor {
private final RelevancyEvaluator evaluator;
private int requestCount = 0;
private static final int SAMPLE_RATE = 100; // 每100次评估1次
@EventListener
public void onRagResponse(RagResponseEvent event) {
requestCount++;
// 抽样评估
if (requestCount % SAMPLE_RATE == 0) {
EvaluationRequest request = new EvaluationRequest(
event.getQuery(),
event.getContext(),
event.getResponse()
);
EvaluationResponse result = evaluator.evaluate(request);
// 记录评估结果
logEvaluationResult(result);
// 如果评分过低,发送告警
if (result.getScore() < 0.6) {
sendAlert("RAG 质量下降", result);
}
}
}
}
价值:
- ✅ 实时监控系统质量
- ✅ 及时发现质量问题
- ✅ 支持 SLA 保障
场景 4:模型验证
目的:当更换 AI 模型或调整 RAG 配置时,验证新配置的效果
实现方式:
java
public class ModelComparison {
public ComparisonResult compareModels(
ChatModel oldModel,
ChatModel newModel,
List<TestQuery> testQueries) {
List<EvaluationResult> oldResults = new ArrayList<>();
List<EvaluationResult> newResults = new ArrayList<>();
RelevancyEvaluator evaluator = new RelevancyEvaluator(chatClient);
for (TestQuery testQuery : testQueries) {
// 测试旧模型
ChatResponse oldResponse = executeRag(oldModel, testQuery);
EvaluationResponse oldEval = evaluator.evaluate(
new EvaluationRequest(testQuery, oldResponse)
);
oldResults.add(oldEval);
// 测试新模型
ChatResponse newResponse = executeRag(newModel, testQuery);
EvaluationResponse newEval = evaluator.evaluate(
new EvaluationRequest(testQuery, newResponse)
);
newResults.add(newEval);
}
// 对比分析
return analyzeComparison(oldResults, newResults);
}
}
价值:
- ✅ 客观对比不同模型效果
- ✅ 数据驱动的决策支持
- ✅ 降低模型切换风险
场景对比表
| 场景 | 评估频率 | 评估范围 | 主要评估器 | 业务价值 |
|---|---|---|---|---|
| 开发测试 | 每次构建 | 测试用例 | RelevancyEvaluator |
质量保证 |
| 批量检查 | 定期(如每周) | 历史对话 | FactCheckingEvaluator RelevancyEvaluator |
问题发现 |
| 系统监控 | 实时抽样(如1%) | 生产对话 | RelevancyEvaluator |
质量监控 |
| 模型验证 | 模型切换时 | 测试集 | RelevancyEvaluator |
决策支持 |
评估的意义与价值
1. 质量保证
| 维度 | 说明 | 价值 |
|---|---|---|
| 事实准确性 | 确保回答基于真实资料,避免幻觉 | 提高用户信任度 |
| 回答相关性 | 确保回答与用户问题相关 | 提升用户体验 |
| 上下文利用 | 确保回答充分利用检索到的文档 | 发挥 RAG 系统价值 |
2. 风险控制
| 风险类型 | 评估作用 | 业务影响 |
|---|---|---|
| 法律风险 | 及时发现错误的法律信息 | 避免法律纠纷 |
| 声誉风险 | 防止发布错误信息 | 保护品牌形象 |
| 财务风险 | 避免错误的业务决策 | 降低经济损失 |
3. 持续改进
| 改进方向 | 评估指导 | 效果 |
|---|---|---|
| 模型优化 | 评估不同模型效果,选择最优模型 | 提升回答质量 |
| 配置调优 | 评估不同配置参数的效果 | 优化系统性能 |
| 知识库优化 | 识别知识库不足,指导文档补充 | 完善知识体系 |
4. 成本优化
| 优化点 | 评估价值 | 节省成本 |
|---|---|---|
| 模型选择 | 选择性价比最高的模型 | 降低 API 调用成本 |
| 配置优化 | 找到最优配置参数 | 减少资源浪费 |
| 质量提升 | 减少错误回答导致的客服成本 | 降低运营成本 |
5. 业务价值总结
| 业务目标 | 评估贡献 | 量化指标 |
|---|---|---|
| 用户满意度 | 提高回答质量和相关性 | 满意度评分提升 X% |
| 系统可靠性 | 及时发现和修复问题 | 错误率降低 X% |
| 运营效率 | 减少人工审核和修正 | 人工成本降低 X% |
| 决策支持 | 数据驱动的模型和配置选择 | 决策准确率提升 X% |
代码示例分析
示例 1:FactCheckingTest.testFactChecking
代码分析
java
@Test
void testFactChecking(@Autowired DashScopeChatModel chatModel) {
// 1. 创建事实性评估器
var factCheckingEvaluator = new FactCheckingEvaluator(
ChatClient.builder(chatModel)
);
// 2. 准备上下文文档(知识库中的真实资料)
Document doc = Document.builder()
.text("""
取消预订:
- 最晚在航班起飞前 48 小时取消。
- 取消费用:经济舱 75 美元,豪华经济舱 50 美元,商务舱 25 美元。
- 退款将在 7 个工作日内处理。
""")
.build();
List<Document> documents = List.of(doc);
// 3. AI 回答(需要评估的回答)
String response = "经济舱取消费用75 美元";
// 4. 创建评估请求
EvaluationRequest evaluationRequest = new EvaluationRequest(
documents, // 上下文:事实依据
response // 回答:需要检查的事实
);
// 5. 执行评估
EvaluationResponse evaluationResponse = factCheckingEvaluator.evaluate(
evaluationRequest
);
System.out.println(evaluationResponse);
}
执行流程
1. 创建 FactCheckingEvaluator
↓
2. 准备上下文文档(包含事实:经济舱 75 美元)
↓
3. 准备 AI 回答(声明:经济舱取消费用75 美元)
↓
4. 创建 EvaluationRequest(documents + response)
↓
5. 调用 evaluate() 方法
↓
6. LLM 内部处理:
- 提取回答中的事实声明:"经济舱取消费用75 美元"
- 在上下文中查找依据:找到"取消费用:经济舱 75 美元"
- 对比验证:事实一致 ✅
↓
7. 返回 EvaluationResponse:
- score: 1.0(完全正确)
- explanation: "回答中的事实与上下文一致"
评估结果分析
| 评估项 | 结果 | 说明 |
|---|---|---|
| 事实准确性 | ✅ 通过 | 回答中的"75 美元"与上下文中的"75 美元"一致 |
| 上下文依据 | ✅ 存在 | 在提供的文档中找到了明确的事实依据 |
| 幻觉检测 | ✅ 无幻觉 | 回答没有虚构或错误的事实 |
如果回答错误会怎样?
假设 AI 回答是:"经济舱取消费用100 美元"
评估结果:
- score: 0.0(完全错误)
- explanation: "回答中的事实(100 美元)与上下文不符(上下文为 75 美元)"
示例 2:FactCheckingTest.testRelevancyEvaluator
代码分析
java
@Test
void testRelevancyEvaluator(@Autowired DashScopeChatModel chatModel) {
// 1. 创建相关性评估器
var evaluator = new RelevancyEvaluator(
ChatClient.builder(chatModel)
);
// 2. 准备评估数据
String context = "地球是距离太阳的第三颗行星,也是已知唯一孕育生命的天文物体。";
String claim = "地球是距离太阳的第四颗行星,也是已知唯一孕育生命的天文物体。";
// 3. 创建评估请求
EvaluationRequest evaluationRequest = new EvaluationRequest(
context, // 上下文文档
Collections.emptyList(), // 用户问题(本例中为空)
claim // AI 回答(声明)
);
// 4. 执行评估
EvaluationResponse evaluationResponse = evaluator.evaluate(
evaluationRequest
);
System.out.println(evaluationResponse);
}
执行流程
1. 创建 RelevancyEvaluator
↓
2. 准备上下文:"地球是距离太阳的第三颗行星..."
↓
3. 准备声明(AI 回答):"地球是距离太阳的第四颗行星..."
↓
4. 创建 EvaluationRequest(context + query + claim)
↓
5. 调用 evaluate() 方法
↓
6. LLM 内部处理:
- 分析声明:"地球是距离太阳的第四颗行星"
- 对比上下文:"地球是距离太阳的第三颗行星"
- 发现不一致:第三颗 vs 第四颗 ❌
↓
7. 返回 EvaluationResponse:
- score: 0.0(完全不相关/错误)
- explanation: "声明与上下文不符,存在事实错误"
评估结果分析
| 评估项 | 结果 | 说明 |
|---|---|---|
| 相关性 | ❌ 不相关 | 回答与上下文存在矛盾 |
| 事实准确性 | ❌ 错误 | "第四颗"与上下文中的"第三颗"不符 |
| 上下文利用 | ❌ 未正确利用 | 模型未正确理解上下文信息 |
示例 3:RagEvalTest.testRag(完整 RAG 流程评估)
代码分析
java
@Test
public void testRag(
@Autowired VectorStore vectorStore,
@Autowired DashScopeChatModel dashScopeChatModel) {
// 1. 准备知识库
List<Document> documents = List.of(
new Document("1. 预订航班\n- 通过我们的网站或移动应用程序预订..."),
new Document("2. 更改预订\n- 允许在航班起飞前 24 小时更改..."),
new Document("3. 取消预订\n- 最晚在航班起飞前 48 小时取消...")
);
vectorStore.add(documents);
// 2. 构建 RAG Advisor
RetrievalAugmentationAdvisor advisor = RetrievalAugmentationAdvisor.builder()
.documentRetriever(VectorStoreDocumentRetriever.builder()
.vectorStore(vectorStore)
.build())
.build();
// 3. 执行 RAG 查询
String query = "我叫什么名字";
ChatResponse chatResponse = ChatClient.builder(dashScopeChatModel)
.build()
.prompt(query)
.advisors(advisor)
.call()
.chatResponse();
// 4. 提取评估所需的数据
EvaluationRequest evaluationRequest = new EvaluationRequest(
query, // 用户问题
chatResponse.getMetadata().get(
RetrievalAugmentationAdvisor.DOCUMENT_CONTEXT
), // 检索到的上下文
chatResponse.getResult().getOutput().getText() // AI 回答
);
// 5. 执行相关性评估
RelevancyEvaluator evaluator = new RelevancyEvaluator(
ChatClient.builder(dashScopeChatModel)
);
EvaluationResponse evaluationResponse = evaluator.evaluate(evaluationRequest);
System.out.println(evaluationResponse);
System.out.println(chatResponse.getResult().getOutput().getText());
}
执行流程
1. 准备知识库(向量存储)
↓
2. 构建 RAG Advisor(检索增强生成)
↓
3. 用户查询:"我叫什么名字"
↓
4. RAG 流程:
- 向量检索:在知识库中搜索相关文档
- 检索结果:可能找到"预订时需要确保个人信息(姓名、ID 等)的准确性..."
- 生成回答:基于检索到的文档生成回答
↓
5. 提取评估数据:
- query: "我叫什么名字"
- context: 检索到的文档列表
- response: AI 生成的回答
↓
6. 创建评估请求
↓
7. 执行相关性评估:
- 检查回答是否与问题相关?
- 检查回答是否基于检索到的上下文?
- 检查回答是否回答了用户的问题?
↓
8. 返回评估结果
评估结果分析
| 评估维度 | 预期结果 | 说明 |
|---|---|---|
| 问题相关性 | ⚠️ 可能不相关 | 用户问"我叫什么名字",但知识库中没有用户个人信息 |
| 上下文利用 | ✅ 正确利用 | 回答应该基于检索到的文档 |
| 回答合理性 | ⚠️ 可能不合理 | 如果回答"您的名字是..."则不合理,因为文档中没有此信息 |
典型评估结果
场景 A:回答合理
用户问题:"我叫什么名字"
检索上下文:"预订时需要确保个人信息(姓名、ID 等)的准确性..."
AI 回答:"根据文档,我没有找到您的姓名信息。请提供您的姓名以便预订。"
评估结果:
- score: 0.8(高度相关)
- explanation: "回答正确指出文档中没有用户姓名信息,并提供了合理的建议"
场景 B:回答不合理(幻觉)
用户问题:"我叫什么名字"
检索上下文:"预订时需要确保个人信息(姓名、ID 等)的准确性..."
AI 回答:"根据文档,您的名字是张三。"
评估结果:
- score: 0.2(低相关性)
- explanation: "回答虚构了用户姓名,文档中并未包含此信息,存在幻觉"
最佳实践
1. 评估器选择策略
| 场景 | 推荐评估器 | 原因 |
|---|---|---|
| 知识库问答 | FactCheckingEvaluator |
重点检查事实准确性 |
| RAG 系统整体评估 | RelevancyEvaluator |
评估回答与问题的相关性 |
| 综合评估 | 两者结合 | 同时检查事实性和相关性 |
2. 评估阈值设置
| 评估类型 | 推荐阈值 | 说明 |
|---|---|---|
| 事实性评估 | ≥ 0.9 | 事实准确性要求高,容错率低 |
| 相关性评估 | ≥ 0.7 | 相关性可以有一定灵活性 |
| 生产环境监控 | ≥ 0.6 | 低于此值需要告警 |
3. 评估频率建议
| 场景 | 评估频率 | 说明 |
|---|---|---|
| 开发测试 | 每次构建 | 确保代码变更不影响质量 |
| 批量检查 | 每周/每月 | 定期回顾历史对话质量 |
| 生产监控 | 1%-5% 抽样 | 平衡监控成本和覆盖度 |
| 模型切换 | 100% 测试集 | 全面评估新模型效果 |
4. 评估数据准备
✅ 好的实践
java
// 1. 使用真实的上下文文档
List<Document> context = vectorStore.similaritySearch(
SearchRequest.builder()
.query(userQuery)
.topK(5)
.build()
);
// 2. 使用完整的用户问题
String query = "退票需要多少费用?";
// 3. 使用实际的 AI 回答
String response = chatResponse.getResult().getOutput().getText();
❌ 避免的做法
java
// ❌ 不要使用模拟数据
List<Document> context = List.of(
new Document("模拟文档内容") // 不真实
);
// ❌ 不要截断用户问题
String query = "退票"; // 信息不完整
// ❌ 不要使用期望的回答
String response = "经济舱取消费用75 美元"; // 这是期望值,不是实际回答
5. 评估结果处理
结果记录
java
public class EvaluationLogger {
public void logEvaluation(EvaluationResponse result,
EvaluationRequest request) {
// 记录评估结果
EvaluationLog log = EvaluationLog.builder()
.timestamp(LocalDateTime.now())
.score(result.getScore())
.explanation(result.getExplanation())
.query(request.getQuery())
.response(request.getResponse())
.build();
// 保存到数据库
evaluationLogRepository.save(log);
// 如果评分过低,发送告警
if (result.getScore() < 0.6) {
alertService.sendAlert("RAG 质量下降", log);
}
}
}
结果分析
java
public class EvaluationAnalyzer {
public AnalysisResult analyze(List<EvaluationResponse> results) {
// 计算平均分
double avgScore = results.stream()
.mapToDouble(EvaluationResponse::getScore)
.average()
.orElse(0.0);
// 统计低分比例
long lowScoreCount = results.stream()
.filter(r -> r.getScore() < 0.6)
.count();
double lowScoreRate = (double) lowScoreCount / results.size();
// 提取常见问题
List<String> commonIssues = extractCommonIssues(results);
return AnalysisResult.builder()
.averageScore(avgScore)
.lowScoreRate(lowScoreRate)
.commonIssues(commonIssues)
.build();
}
}
6. 性能优化
| 优化策略 | 实现方式 | 效果 |
|---|---|---|
| 异步评估 | 使用 @Async 异步执行评估 |
不阻塞主流程 |
| 批量评估 | 批量调用评估 API | 减少网络开销 |
| 缓存结果 | 缓存相同问题的评估结果 | 避免重复计算 |
| 抽样评估 | 只评估部分请求 | 降低评估成本 |
java
@Service
public class AsyncEvaluationService {
@Async
public CompletableFuture<EvaluationResponse> evaluateAsync(
EvaluationRequest request) {
RelevancyEvaluator evaluator = new RelevancyEvaluator(chatClient);
EvaluationResponse result = evaluator.evaluate(request);
return CompletableFuture.completedFuture(result);
}
}
常见问题
Q1: 评估器会消耗额外的 LLM 调用吗?
A: 是的。评估器内部会调用 LLM 来分析和判断,因此会产生额外的 API 调用成本。
| 评估器 | LLM 调用次数 | 成本影响 |
|---|---|---|
FactCheckingEvaluator |
1 次 | 中等 |
RelevancyEvaluator |
1 次 | 中等 |
建议:
- 在生产环境中使用抽样评估(如 1%-5%)
- 在开发测试阶段可以 100% 评估
- 考虑使用更便宜的模型进行评估
Q2: 评估结果是否可靠?
A: 评估结果由 LLM 生成,存在一定的主观性,但通常比较可靠。
| 可靠性因素 | 说明 | 建议 |
|---|---|---|
| LLM 能力 | 评估质量取决于评估器使用的 LLM | 使用高质量的模型(如 GPT-4) |
| 提示词设计 | 评估器的内部提示词影响判断 | 使用 Spring AI 官方评估器 |
| 上下文质量 | 上下文文档的质量影响评估 | 确保上下文文档准确完整 |
最佳实践:
- 使用多个评估器交叉验证
- 结合人工审核进行关键评估
- 建立评估结果的置信度阈值
Q3: 如何选择合适的评估器?
A: 根据评估目标选择:
| 评估目标 | 推荐评估器 | 示例场景 |
|---|---|---|
| 检查事实准确性 | FactCheckingEvaluator |
知识库问答、法律咨询 |
| 检查回答相关性 | RelevancyEvaluator |
客服系统、智能助手 |
| 综合评估 | 两者结合 | 重要业务场景 |
Q4: 评估结果如何解读?
A : EvaluationResponse 包含两个主要部分:
java
EvaluationResponse response = evaluator.evaluate(request);
// 1. 评分(0.0 - 1.0)
double score = response.getScore();
// score >= 0.8: 优秀
// score >= 0.6: 良好
// score < 0.6: 需要改进
// 2. 解释(文本说明)
String explanation = response.getExplanation();
// 包含评估的详细说明和理由
Q5: 评估会影响 RAG 系统的性能吗?
A: 评估是异步的,不会直接影响 RAG 系统的响应时间,但会增加系统负载。
| 影响维度 | 说明 | 缓解措施 |
|---|---|---|
| 响应时间 | 评估是异步的,不影响主流程 | 使用异步评估 |
| 系统负载 | 增加 LLM API 调用 | 使用抽样评估 |
| 成本 | 增加 API 调用成本 | 优化评估频率和范围 |
Q6: 如何处理评估失败的情况?
A: 评估可能因为网络、API 限制等原因失败,需要做好异常处理:
java
try {
EvaluationResponse result = evaluator.evaluate(request);
// 处理成功结果
} catch (Exception e) {
// 记录失败日志
log.error("评估失败", e);
// 可以选择:
// 1. 重试评估
// 2. 标记为未评估
// 3. 使用默认评分
// 4. 发送告警
}
Q7: 可以自定义评估器吗?
A: 可以。Spring AI 提供了评估器接口,可以自定义实现:
java
public class CustomEvaluator implements Evaluator {
@Override
public EvaluationResponse evaluate(EvaluationRequest request) {
// 自定义评估逻辑
// 可以结合业务规则、规则引擎等
return EvaluationResponse.builder()
.score(calculateScore(request))
.explanation(generateExplanation(request))
.build();
}
}
Q8: 评估结果如何用于系统优化?
A: 评估结果可以指导多个方面的优化:
| 优化方向 | 评估数据来源 | 优化策略 |
|---|---|---|
| 模型选择 | 不同模型的评估结果对比 | 选择平均分最高的模型 |
| 参数调优 | 不同配置下的评估结果 | 调整 temperature、topK 等参数 |
| 知识库优化 | 低分回答的上下文分析 | 补充缺失的知识文档 |
| 检索优化 | 相关性评估结果 | 优化检索策略、调整 topK |
| 提示词优化 | 评估解释中的问题分析 | 改进系统提示词设计 |
示例:
java
// 1. 收集评估数据
List<EvaluationResult> results = collectEvaluationResults();
// 2. 分析低分原因
List<String> lowScoreReasons = results.stream()
.filter(r -> r.getScore() < 0.6)
.map(r -> r.getExplanation())
.collect(Collectors.toList());
// 3. 识别常见问题
Map<String, Long> issueFrequency = analyzeIssueFrequency(lowScoreReasons);
// 4. 针对性优化
if (issueFrequency.containsKey("上下文不足")) {
// 优化检索策略,增加 topK
optimizeRetrievalStrategy();
}
if (issueFrequency.containsKey("事实错误")) {
// 优化知识库,补充文档
enrichKnowledgeBase();
}
Q9: 评估器与人工审核如何结合?
A: 评估器可以辅助人工审核,提高效率:
| 方式 | 说明 | 优势 |
|---|---|---|
| 预筛选 | 评估器先筛选,人工只审核低分回答 | 减少人工工作量 |
| 交叉验证 | 评估器 + 人工双重检查 | 提高准确性 |
| 学习反馈 | 人工审核结果用于优化评估器 | 持续改进 |
实现示例:
java
public class HybridEvaluationService {
public EvaluationResult evaluateWithHumanReview(
EvaluationRequest request) {
// 1. 自动评估
EvaluationResponse autoResult = evaluator.evaluate(request);
// 2. 如果评分低,触发人工审核
if (autoResult.getScore() < 0.6) {
HumanReviewRequest reviewRequest = HumanReviewRequest.builder()
.query(request.getQuery())
.response(request.getResponse())
.autoEvaluation(autoResult)
.build();
// 发送到人工审核队列
humanReviewQueue.add(reviewRequest);
}
return EvaluationResult.builder()
.autoEvaluation(autoResult)
.requiresHumanReview(autoResult.getScore() < 0.6)
.build();
}
}
Q10: 如何建立评估基准(Benchmark)?
A: 建立评估基准有助于持续监控系统质量:
步骤:
-
准备测试集
javaList<TestCase> testCases = List.of( TestCase.builder() .query("退票需要多少费用?") .expectedContext("取消费用:经济舱 75 美元...") .expectedResponse("经济舱取消费用75 美元") .build(), // 更多测试用例... ); -
建立基准分数
javapublic class BenchmarkService { public BenchmarkResult establishBaseline( List<TestCase> testCases) { List<EvaluationResponse> results = testCases.stream() .map(this::evaluateTestCase) .collect(Collectors.toList()); double baselineScore = results.stream() .mapToDouble(EvaluationResponse::getScore) .average() .orElse(0.0); return BenchmarkResult.builder() .baselineScore(baselineScore) .testCases(testCases.size()) .timestamp(LocalDateTime.now()) .build(); } } -
定期对比
javapublic void compareWithBaseline(BenchmarkResult baseline) { BenchmarkResult current = establishBaseline(testCases); double scoreChange = current.getBaselineScore() - baseline.getBaselineScore(); if (scoreChange < -0.1) { // 质量下降超过 10%,发送告警 alertService.sendAlert("质量下降", scoreChange); } }
总结
核心要点
| 要点 | 说明 |
|---|---|
| 为什么评估 | RAG 系统不能完全消除幻觉,需要评估来保证质量 |
| 评估器类型 | FactCheckingEvaluator(事实性)和 RelevancyEvaluator(相关性) |
| 使用场景 | 开发测试、批量检查、系统监控、模型验证 |
| 评估价值 | 质量保证、风险控制、持续改进、成本优化 |
评估流程总结
1. 准备评估数据
├── 用户问题(query)
├── 检索到的上下文(context)
└── AI 回答(response)
↓
2. 选择评估器
├── FactCheckingEvaluator(事实性)
└── RelevancyEvaluator(相关性)
↓
3. 创建评估请求
└── EvaluationRequest(query, context, response)
↓
4. 执行评估
└── evaluator.evaluate(request)
↓
5. 处理评估结果
├── 记录结果
├── 分析问题
└── 触发优化
最佳实践清单
- ✅ 选择合适的评估器:根据场景选择事实性评估或相关性评估
- ✅ 设置合理的阈值:事实性 ≥ 0.9,相关性 ≥ 0.7
- ✅ 使用抽样评估:生产环境使用 1%-5% 抽样,降低成本
- ✅ 记录评估结果:建立评估日志,便于分析和追踪
- ✅ 建立评估基准:定期对比,监控质量变化
- ✅ 结合人工审核:关键场景使用评估器 + 人工双重检查
- ✅ 持续优化:根据评估结果优化模型、配置和知识库
评估指标参考
| 指标 | 优秀 | 良好 | 需改进 |
|---|---|---|---|
| 事实性评分 | ≥ 0.9 | 0.7 - 0.9 | < 0.7 |
| 相关性评分 | ≥ 0.8 | 0.6 - 0.8 | < 0.6 |
| 低分比例 | < 5% | 5% - 15% | > 15% |
| 平均评分 | ≥ 0.85 | 0.7 - 0.85 | < 0.7 |
相关资源
- Spring AI 官方文档:https://docs.spring.io/spring-ai/reference/
- 评估器 API 文档 :
FactCheckingEvaluator、RelevancyEvaluator - 代码示例 :
FactCheckingTest.java- 事实性评估示例RagEvalTest.java- 完整 RAG 流程评估示例
文档版本 :v1.0
最后更新 :2025年1月
维护者:Spring AI 学习团队