Spring AI Framework (五:测试集与三维评分(相关性 / 忠实度 / 关键词)

📅2026/7/15 5:25:23 👁️次浏览
Spring AI Framework (五:测试集与三维评分(相关性 / 忠实度 / 关键词)
前言构建RAG 评估模块,包括 测试集管理 评估流水线Evaluation Pipeline。流程维护与知识库绑定的问答测试集Test Set及用例Test Case 对测试集批量执行 RAG 检索 → 生成 → 三维评分相关性 / 忠实度 / 关键词评分采用 RAG LLM-as-Judge大模型评估。 持久化每次评估运行Run及单条用例结果前端-portalUI展示回归评估流水记录一、流程架构概览模块内容模块路径数据库表启动自动建表RagEvalSchemaInitializer表说明rag_eval_test_set — 测试集FK → rag_knowledge_baserag_eval_test_case — 测试用例FK → rag_eval_test_setrag_eval_run — 一次评估运行汇总rag_eval_run_case — 单条用例运行结果FK → rag_eval_run测试集 CRUDbusiness/rag/eval/service/RagEvalTestSetService评估流水线business/rag/eval/service/RagEvalPipelineServiceREST APIbusiness/rag/eval/controller/RagEvalController示例测试集src/main/resources/eval/sample-test-set.json配置app.rag.eval.*inapplication.yml使用前准备PostgreSQL 中已有知识库表rag_knowledge_base评估表会在应用启动时自动创建目标知识库已完成文档上传 解析 向量化配置 LLM provider如 DeepSeek有效 API Key用于 RAG 生成和 Judge 评估。评估运行会返回passRate、avgRelevancyScore、avgFaithfulnessScore、avgKeywordScore及每条用例的详细结果便于对比不同 chunk 策略、TopK、模型配置的 RAG 质量。二、评估指标评估维度相关性Relevancy —RelevancyEvaluator答案是否与[问题 检索上下文]一致忠实度Faithfulness —FactCheckingEvaluator答案是否被检索内容支撑防幻觉关键词重叠Keyword Overlap — 计算[实际答案与测试集期望答案]关键词重叠率(0~1), 无需额外 LLM 调用,再与关键词阈值[0.3配置化]比较通过则通过。用例 PASS 条件启用的指标全部通过。三、页面功能4 个 Tab测试集 — 按知识库筛选、新建/删除测试集用例管理 — 增删改用例、JSON 批量导入运行评估 — 配置 RAG/Judge 模型、评估维度一键运行并展示通过率与明细运行历史 — 历史记录列表、详情抽屉测试集基于知识库创建的测试集。用例管理每个测试集创建多条测试用例。采用引入JSON 或 表单模型。运行评估配置RAG、Judge 模型、召回数量TopK、相似度阈值、评估维度和关键字阈值进行相关性 / 忠实度 / 关键词 三维评分并展示通过率和三项平均分。相关性提示词填充[问题、召回文档上下文、答案]后调用LLM生成答案yes、no; 评估答案是否切题且与检索上下文一致。核心代码//入口 if (enableRelevancy relevancyEvaluator ! null) { EvaluationResponse relevancy relevancyEvaluator.evaluate( new EvaluationRequest(testCase.getQuestion(), contextDocs, answer)); result.setRelevancyPass(relevancy.isPass()); result.setRelevancyScore((double) relevancy.getScore()); result.setRelevancyFeedback(truncate(relevancy.getFeedback(), 512)); if (!relevancy.isPass()) { pass false; } } //Spring AI- RelevancyEvaluator public class RelevancyEvaluator implements Evaluator { // 默认提示词 private static final PromptTemplate DEFAULT_PROMPT_TEMPLATE new PromptTemplate(\tYour task is to evaluate if the response for the query\n\tis in line with the context information provided.\n\n\tYou have two options to answer. Either YES or NO.\n\n\tAnswer YES, if the response for the query\n\tis in line with context information otherwise NO.\n\n\tQuery:\n\t{query}\n\n\tResponse:\n\t{response}\n\n\tContext:\n\t{context}\n\n\tAnswer:\n); public EvaluationResponse evaluate(EvaluationRequest evaluationRequest) { String response evaluationRequest.getResponseContent(); String context this.doGetSupportingData(evaluationRequest); String userMessage this.promptTemplate.render(Map.of(query, evaluationRequest.getUserText(), response, response, context, context)); String evaluationResponse this.chatClientBuilder.build().prompt().user(userMessage).call().content(); boolean passing false; float score 0.0F; if (yes.equalsIgnoreCase(evaluationResponse)) { passing true; score 1.0F; } return new EvaluationResponse(passing, score, , Collections.emptyMap()); } }忠实度提示词填充[召回文档上下文、答案]后调用LLM生成答案yes、no;评估答案是否被本次召回的文档上下文支撑,核心代码//入口 if (enableFaithfulness faithfulnessEvaluator ! null) { EvaluationResponse faithfulness faithfulnessChecker.evaluate( faithfulnessEvaluator, contextDocs, testCase.getQuestion(), answer); result.setFaithfulnessPass(faithfulness.isPass()); result.setFaithfulnessScore((double) faithfulness.getScore()); result.setFaithfulnessFeedback(truncate(faithfulness.getFeedback(), 512)); if (!faithfulness.isPass()) { pass false; } } /** * 执行忠实度评估。 * * param evaluator 已装配 Judge 模型的 FactCheckingEvaluator * param contextDocs 本次 RAG 召回的分块非 expectedAnswer / referenceContext * param question 用户问题用于短答案 Claim 构建 * param answer 模型实际生成的答案 * return 含归一化 scorepass→1.0, fail→0.0的 EvaluationResponse */ public EvaluationResponse evaluate( FactCheckingEvaluator evaluator, ListDocument contextDocs, String question, String answer) { if (contextDocs null || contextDocs.isEmpty()) { return new EvaluationResponse(false, 0f, 无检索上下文无法验证忠实度, Map.of()); } String claim buildClaim(question, answer); // 策略 1全量上下文 EvaluationResponse fullContext evaluateClaim(evaluator, contextDocs, claim); if (fullContext.isPass()) { return withNormalizedScore(fullContext); } // 策略 2逐 chunk 回退 for (Document doc : contextDocs) { EvaluationResponse chunkResult evaluateClaim(evaluator, List.of(doc), claim); if (chunkResult.isPass()) { return withNormalizedScore(chunkResult); } } // 策略 3字面量兜底 if (contextContainsLiteral(contextDocs, answer)) { return new EvaluationResponse(true, 1f, 检索上下文包含答案字面量LLM Judge 未通过时的规则兜底, Map.of()); } return withNormalizedScore(fullContext); } /** * 调用 FactCheckingEvaluatorDocument 列表 → dataListclaim → responseContent。 */ private static EvaluationResponse evaluateClaim( FactCheckingEvaluator evaluator, ListDocument contextDocs, String claim) { return evaluator.evaluate(new EvaluationRequest(contextDocs, claim)); } //Spring AI-FactCheckingEvaluator public class FactCheckingEvaluator implements Evaluator { private static final String DEFAULT_EVALUATION_PROMPT_TEXT \tEvaluate whether or not the following claim is supported by the provided document.\n\tRespond with \yes\ if the claim is supported, or \no\ if it is not.\n\n\tDocument:\n\t{document}\n\n\tClaim:\n\t{claim}\n; private static final String BESPOKE_EVALUATION_PROMPT_TEXT \tDocument:\n\t{document}\n\n\tClaim:\n\t{claim}\n; public EvaluationResponse evaluate(EvaluationRequest evaluationRequest) { String response evaluationRequest.getResponseContent(); String context this.doGetSupportingData(evaluationRequest); String evaluationResponse this.chatClientBuilder.build().prompt().user((userSpec) - { userSpec.text(this.evaluationPrompt).param(document, context).param(claim, response); }).call().content(); boolean passing yes.equalsIgnoreCase(evaluationResponse); return new EvaluationResponse(passing, , Collections.emptyMap()); } }运行历史记录运行评估的记录和详情。评估调优从评估模型选型、评估策略升级、评估标准定制三个层面进行调优流程如下用户提问 → 【关键词提取/查询增强】→ 向量检索 → 获取上下文 → LLM生成回答 → 【相关性/忠实度评估】 ↑ 前置优化环节 ↑ 后置质量检查环节评估模型选型采用专用评估模型如Flow-Judge-v0.1、Meta Llama 3.3 70B、Llama-3.1-Bespoke-MiniCheck-7B专用于判断句子级主张claim是否被给定文档document支持 输出二元结果1支持0不支持在准确率与推理速度上达到 SOTA水平; 可降低成本并提高判断的稳定性。评估策略升级采用递归评估机制Evaluator-Optimizer模式通过“生成-评估通过率--- 反馈反馈追加到原始问题--- 优化(评分低于阈值,如0.75)”的闭环通过1-2轮迭代优化确保输出质量稳定达到阈值,可显著提升最终答案的质量。评估标准定制优化Prompt模板明确标准相关且简洁消除偏见冗长不等于相关引用证据上下文有效地提升评估的准确性和通过率。出现的问题提升忠实度1. 检索侧召回质量决定忠实度上限参数建议原因topK5 → 8~10关键信息可能分散在多个 chunksimilarityThreshold0.6 → 0.5~0.55阈值过高会漏召回支撑片段chunkTokenSize知识库创建时 512→768过小会把完整知识点切碎2. 生成侧Prompt 约束系统提示词明确回答要求你是企业知识库问答助手。请严格依据提供的参考上下文回答问题。 若上下文不足以回答请明确说明「根据现有资料无法回答」不要编造。 回答要求直接给出事实不要出现「根据参考上下文」「资料显示」等套话。 当前知识库{kbName}3. 评估侧Judge 模型专用评估模型如Flow-Judge-v0.1、Meta Llama 3.3 70B、Llama-3.1-Bespoke-MiniCheck-7B-专用于判断句子级主张claim是否被给定文档document支持输出二元结果1支持0不支持在准确率与推理速度上达到 SOTA水平; 降低成本并提高判断的稳定性