Skip to content

RAG 专项评测

检索增强生成的评测方法论 · 从认知到实操的五层递进 · 第11讲 / 共21讲

这篇怎么学

RAG(Retrieval-Augmented Generation)是目前企业落地大模型最主流的方式。本篇从"什么是 RAG"讲起,依次进入评测指标体系、分层测试方法论、工具实操,最后以企业级案例收尾。建议按顺序读完,遇到代码块建议上手跑一遍。

第1章:什么是 RAG

1.1 RAG 的定义与核心价值

RAG(Retrieval-Augmented Generation,检索增强生成)是一种将外部知识检索大模型生成相结合的架构模式。它的核心思路是:大模型回答用户问题之前,先从知识库中检索出相关的文档片段,把这些片段作为上下文塞进 Prompt,再让大模型基于这些上下文来生成答案。

📖 一句话理解 RAG

RAG = 先查资料,再回答问题。它让大模型从"凭记忆回答"变成"查完资料再回答"。

RAG 解决的核心问题:

  • **知识时效性:**大模型训练数据有截止日期,无法获取最新信息。RAG 通过实时检索知识库弥补这一缺陷。
  • **领域专业性:**通用大模型对企业内部业务规则、产品文档、SOP 流程一无所知。RAG 将企业私有知识注入回答过程。
  • **幻觉控制:**让大模型"有据可依"地回答,而非凭空编造,从根本上降低幻觉率。
  • **可溯源性:**回答可以标注引用来源,用户可以核实原文,增强可信度。

1.2 RAG 在企业大模型应用中的普及程度

根据行业调研,目前超过 80% 的企业大模型应用都采用了 RAG 架构。典型场景包括:

应用场景知识库类型用户群体典型规模
内部知识问答公司制度、HR 政策、IT 手册全体员工数百~数千篇文档
客服智能助手产品手册、FAQ、案例库客服坐席 / 终端用户数千~数万条
法律/合规咨询法规条文、判例库、合同模板法务/合规人员数万~数十万条
研发文档助手API 文档、设计文档、代码注释开发团队持续增长
医疗知识库诊疗指南、药品说明书、病例医护人员高度专业化

1.3 为什么 RAG 需要专项评测

很多团队测试 RAG 应用时,只看"回答对不对"。这种做法有一个致命问题:你不知道错在哪里

不能只测"回答对不对"的原因

RAG 的回答质量取决于一条长长的链路:文档切分 → 向量化 → 检索 → 重排序 → Prompt 拼接 → 大模型生成。任何一个环节出问题都会导致最终回答错误。如果只看最终结果,你根本无法定位问题出在哪个环节。

示例

同一个错误回答,可能有完全不同的根因:

现象可能的根因修复方向
回答错误检索到了错误的文档优化 Embedding 或检索策略
回答错误检索正确但大模型没有使用优化 Prompt 模板或模型选择
回答错误文档切分把关键信息拆断了优化分块策略
回答"不知道"知识库里有但没检索到检查 Embedding 质量和召回策略
回答"不知道"知识库里确实没有补充知识库
所以 RAG 评测必须分层进行,逐环节定位问题。

第2章:RAG 全链路架构

2.1 完整 RAG Pipeline

下面是一个典型的 RAG 系统完整链路。每个环节都可能出问题,每个环节都需要独立评测。 👤 用户提问 自然语言输入 → Query 预处理 改写 / 意图识别 / 扩展 → Embedding 向量化 文本 → 高维向量 → 向量数据库检索 ANN 近邻搜索

👤 返回用户 展示回答 + 引用来源 ← 后处理 引用标注 / 格式化 ← 大模型生成 基于上下文回答 ← Prompt 拼接 系统提示 + 上下文 + 问题 ← Rerank 重排序 相关性二次排序

2.2 每个环节可能出的问题

环节常见问题对最终回答的影响
Query 预处理改写过度/不足、意图误判、多轮上下文丢失后续检索方向完全偏离
Embedding 向量化语义表达不准确、领域词汇覆盖不足相关文档无法被召回
向量数据库检索召回数量不足、噪声过多、Top-K 设置不当关键信息缺失或被淹没
Rerank 重排序重排序模型性能差、把正确文档排到后面关键片段被截断
Prompt 拼接上下文过长被截断、格式混乱、指令不清大模型无法正确理解和利用上下文
大模型生成忽略上下文自行编造、过度摘要丢失细节幻觉、信息丢失
后处理引用标注错误、格式丢失(表格/列表)用户无法溯源或格式不可读

2.3 RAG 测试为什么必须分层

分层测试的核心逻辑

如果只做端到端测试(问一个问题 → 看回答对不对),你面临的是一个"黑盒中的黑盒"。当回答错误时,你无法区分是检索问题、排序问题、Prompt 问题还是生成问题。分层测试的目的是:让每一层的输入和输出都可以独立验证

端到端测试(黑盒):只看最终回答是否正确。 **优点:**贴近用户体验。 **缺点:**无法定位问题环节,调优成本高。 分层测试(灰盒):逐层验证:检索是否召回正确文档 → 重排序是否优先正确文档 → 生成是否基于上下文。 **优点:**精准定位,高效调优。 **缺点:**需要记录中间数据。

第3章:RAG 评测指标体系

3.1 核心指标详解

RAG 评测有一套被学界和工业界广泛认可的指标体系。以下是每个核心指标的详细解释:

指标英文名测量什么直觉解释取值范围
忠实度Faithfulness生成的回答是否 基于检索到的上下文 ,而非自行编造回答里的每一句话,是否都能在上下文中找到依据?0~1,越高越好
答案相关性Answer Relevancy生成的回答是否 回答了用户的问题用户问的是 A,你回答的内容和 A 有多相关?0~1,越高越好
上下文精准度Context Precision检索到的文档片段中, 有多少是真正有用的检索出来 5 段内容,其中有 3 段和问题有关,精准度 = 3/50~1,越高越好
上下文召回率Context Recall回答所需的信息是否 都被检索到了标准答案包含 4 个关键点,检索出来的上下文覆盖了几个?0~1,越高越好
答案正确性Answer Correctness生成的回答与 标准答案的匹配度综合语义相似度和事实要素覆盖度来衡量0~1,越高越好

指标之间的关系

Faithfulness 高但 Answer Relevancy 低 = 回答忠实于上下文,但答非所问。 Answer Relevancy 高但 Faithfulness 低 = 回答看起来相关,但内容是编造的。 Context Recall 低 = 不管模型多好,巧妇难为无米之炊,关键信息根本没被检索到。

3.2 RAG Triad(RAG 三角)

RAG 评测指标可以组织成一个"三角"结构,分别覆盖检索质量生成质量端到端质量三个维度: 端到端质量 Answer Correctness
Answer Relevancy 检索质量 Context Precision
Context Recall 生成质量 Faithfulness
Answer Semantic Sim. RAG Triad

3.3 指标组合诊断表

当你拿到一组指标数值时,怎么快速判断系统哪里出了问题?

Context RecallContext PrecisionFaithfulnessAnswer Relevancy诊断结论优化方向
任意任意检索层瓶颈:关键信息没有被召回改进 Embedding、扩大检索范围、优化分块
任意任意检索噪声过多:信息被淹没加 Rerank、缩小 Top-K、过滤低分结果
任意模型幻觉:检索到了但没用优化 Prompt 指令、换更强的模型、降温度
答非所问:忠实但不相关Query 改写、意图分类、Prompt 优化
系统健康持续监控,关注边缘 case

第4章:检索层评估方法

4.1 核心检索指标

指标全称含义适用场景
召回率Recall@K前 K 个结果中包含了多少相关文档关注"别漏掉"
精准率Precision@K前 K 个结果中有多少是相关的关注"别搞错"
MRRMean Reciprocal Rank第一个正确结果排在第几位关注排序质量
NDCGNormalized DCG考虑排序位置的综合质量分关注整体排序合理性

4.2 计算公式与直观解释

Recall@K(召回率)

Recall@K = 前K个结果中的相关文档数 / 所有相关文档总数

示例

**举例:**知识库中有 3 篇和用户问题相关的文档。检索返回 Top-5,其中包含 2 篇相关文档。 Recall@5 = 2 / 3 = 0.667 含义:有 1 篇该检索到的文档漏掉了。

Precision@K(精准率)

Precision@K = 前K个结果中的相关文档数 / K

示例

**举例:**检索返回 Top-5,其中有 2 篇相关、3 篇不相关。 Precision@5 = 2 / 5 = 0.4 含义:60% 的检索结果是噪声。

MRR(平均倒数排名)

MRR = (1/N) × Σ (1 / rank_i),其中 rank_i 是第 i 个查询的第一个正确结果的排名

示例

**举例:**3 个查询中,第一个正确结果分别排在第 1、3、2 位。 MRR = (1/3) × (1/1 + 1/3 + 1/2) = (1/3) × 1.833 = 0.611 含义:平均来看,正确结果大约在第 1.6 位才出现。

NDCG@K(归一化折损累计增益)

DCG@K = Σ (rel_i / log₂(i+1)),NDCG@K = DCG@K / IDCG@K NDCG 同时考虑了结果的相关性和排序位置。排在前面的相关文档权重更高。IDCG 是理想排序下的 DCG,用于归一化。

4.3 检索方式对比测试

检索方式原理优势劣势适用场景
向量检索将文本编码为向量,通过余弦相似度匹配语义理解能力强,能匹配"同义表达"对精确术语、编号、日期不敏感自然语言问答、模糊搜索
关键词检索BM25 等基于词频的匹配精确匹配能力强,对术语和编号敏感无法理解语义,同义词无法匹配精确查找、法规条文检索
混合检索向量 + 关键词结果融合(RRF 等)兼顾语义和精确匹配融合权重需要调优企业级 RAG,兼顾多种查询类型

测试建议

构建评测数据集时,刻意设计三类查询:纯语义查询(如"员工请假怎么办")、精确查询(如"条款 3.2.1 的内容是什么")、混合查询(如"2024年修订的数据安全管理办法第5条")。然后分别测试三种检索方式的 Recall@K,找到最优策略。

第5章:生成层评估方法

5.1 基于知识库的幻觉检测

与通用大模型的幻觉检测不同,RAG 场景下的幻觉有一个明确的判断标准:回答是否忠实于检索到的上下文。如果回答中出现了上下文中没有的信息,就可以判定为幻觉。

幻觉检测方法

方法原理优缺点
NLI(自然语言推理)把回答拆成独立声明(claims),逐条判断每个声明是否能被上下文"蕴含"颗粒度细,但依赖 NLI 模型质量
LLM-as-Judge用另一个大模型判断回答与上下文的一致性灵活度高,但成本较大且存在评判偏差
关键词交叉验证提取回答中的实体和数值,检查是否出现在上下文中简单高效,但只能检测表面层次的幻觉
示例

幻觉检测示例: 上下文:"公司年假制度规定:入职满一年可享受5天带薪年假,满五年增至10天。" 模型回答:"入职满一年可享受5天年假,满三年增至8天,满五年增至15天。" 检测结果:

  • ✅ "入职满一年可享受5天年假" — 上下文支持
  • ❌ "满三年增至8天" — 上下文无此信息,幻觉
  • ❌ "满五年增至15天" — 上下文说10天,事实错误,幻觉

5.2 引用溯源准确率

很多 RAG 应用会在回答中标注引用来源(如 [文档1] [文档2])。引用溯源准确率衡量的是:标注的引用是否真的支撑了对应的内容。 引用准确率 = 正确引用数 / 总引用标注数 常见问题:

  • **虚假引用:**标注了来源,但该来源中并不包含对应内容
  • **张冠李戴:**内容来自文档 A,却引用了文档 B
  • **引用缺失:**有关键声明但没有标注引用来源

5.3 多文档融合质量评估

当回答需要综合多个文档片段时,模型需要正确地理解和融合不同来源的信息。评测要点:

评测维度关注点检测方法
信息完整性各文档中的关键信息是否都被覆盖对照关键要素清单逐一核对
一致性处理不同文档中的冲突信息如何处理构造含矛盾信息的上下文测试
逻辑连贯性多来源信息的组织是否有逻辑人工或 LLM 评判连贯度
去重能力重复信息是否被合理归并提供包含重叠内容的上下文测试

5.4 拒答策略测试

一个好的 RAG 系统不仅要答对,还要会说"不知道"。当知识库中没有相关信息时,系统应该明确拒绝回答,而不是编造一个看起来合理的答案。

拒答测试设计方法

  1. 构造一批知识库中不存在的问题(如竞品的产品信息、未来的政策)
  2. 构造跨领域问题(如向技术知识库提问财务问题)
  3. 构造部分相关的问题(关键信息缺失,仅有部分上下文)
  4. 验证系统是否明确声明"当前知识库暂无相关信息"
测试类型输入期望行为最差行为
完全无关问题"今天天气怎么样"明确拒答编造天气信息
知识库无覆盖"竞品 X 的定价策略"明确拒答用本公司信息冒充回答
部分覆盖"员工年假有多少天?加班费怎么算?"回答已知部分,声明未知部分用已知信息推测未知信息

第6章:RAG 测试策略

6.1 分层测试矩阵

RAG 测试应覆盖检索层、理解层和生成层三个维度,每个维度有不同的测试关注点:

测试维度 ↓ / 层级 →检索层理解层(Prompt/Rerank)生成层
功能正确性相关文档被召回重排序后排名合理回答内容正确
噪声鲁棒性不相关文档不干扰干扰文档被过滤不被噪声上下文误导
边界处理空结果、超长文档上下文截断策略拒答、不确定性表达
一致性同语义不同表达的召回一致性上下文窗口一致性多次生成结果稳定性
性能检索延迟 (P50/P99)Rerank 延迟首 token 延迟、总生成时间
安全性权限隔离检索防 Prompt 注入敏感信息过滤

6.2 数据集构建方法

一个好的 RAG 评测数据集是整个评测的基础。以下是从知识库中系统化构建评测数据集的方法:

选取知识库文档 → 人工/LLM 生成问题 → 标注标准答案 → 标注对应文档片段 → 分类与分级

构建步骤具体操作注意事项
文档选取从知识库中按类别均匀抽取文档覆盖不同主题、长度、格式(文本/表格/列表)
问题生成基于文档内容生成自然语言问题包含简单事实型、推理型、多文档综合型、否定型
答案标注人工撰写标准答案(ground truth)答案必须完全基于对应文档,不能引入外部知识
片段标注标记支撑答案的具体文档片段用于评估 Context Recall 和 Precision
难度分级标注问题难度(简单/中等/困难)简单 = 单段落答案,困难 = 跨文档推理

6.3 Golden Set 设计

Golden Set 是一组精心标注的"黄金"测试集,包含已知的正确答案对应的文档片段。它是 RAG 评测的基准参照。

Golden Set 结构

每条测试数据包含四个字段:

  • question:用户问题
  • ground_truth:标准答案
  • contexts:支撑答案的文档片段列表
  • metadata:问题类型、难度等级、涉及文档 ID
json
{
  "question": "新员工入职第一年有多少天年假?",
  "ground_truth": "根据公司《员工手册》第4.2条规定,新员工入职满一年后可享受5天带薪年假。",
  "contexts": [
    "4.2 年假制度:员工自入职之日起满一年,可享受5天带薪年假。满五年者,年假增至10天。满十年者,年假增至15天。"
  ],
  "metadata": {
    "type": "factual",
    "difficulty": "easy",
    "source_doc": "员工手册_v3.2.pdf",
    "tags": ["HR", "年假"]
  }
}

第7章:RAG 常见缺陷模式

7.1 缺陷分类总览

以下是 RAG 系统中最常见的 18 种缺陷模式,按所属环节分类:

#缺陷名称所属环节触发条件检测方法
1检索未命中检索层Embedding 不佳或 Top-K 过小对比 ground_truth contexts 和实际召回
2检索噪声过多检索层语义模糊的查询计算 Precision@K,人工审查 Top-K 相关性
3切分导致信息断裂索引层Chunk 边界恰好在关键信息中间检查跨 Chunk 的信息完整性
4跨文档冲突知识库不同版本的文档包含矛盾信息构造含矛盾的测试用例,验证模型取舍策略
5引用标注错误后处理引用映射逻辑有 bug逐条验证引用标注与原文的对应关系
6超出知识库编造生成层上下文不足但模型没有拒答Faithfulness 检测 + 拒答测试
7格式丢失索引/生成层原文是表格或列表,切分后格式破坏用包含表格/列表的文档做测试
8时效性问题知识库知识库未及时更新用涉及时间的问题测试,验证是否返回最新版本
9多轮上下文丢失Query 预处理多轮对话中前文引用未传递构造需要指代消解的多轮对话
10Query 改写过度Query 预处理改写器把用户意图改变了对比原始 Query 和改写后 Query 的语义
11Rerank 排序异常Rerank 层Rerank 模型把正确文档排到后面对比 Rerank 前后的排序结果
12上下文截断Prompt 拼接上下文超过模型窗口被截断用长上下文测试,验证关键信息是否被保留
13过度摘要生成层模型将详细信息压缩为笼统描述验证数值、日期、条件等关键细节是否保留
14权限穿透检索层用户查到了无权限的文档用不同角色账号测试同一问题
15语言混淆Embedding/生成中英文或多语言文档混合用中文问题检索英文文档,验证跨语言能力
16数值/日期幻觉生成层数字和日期最容易被模型"微调"提取回答中的数值与上下文严格比对
17否定语义翻转生成层上下文说"不允许",回答变成"允许"构造含否定信息的测试用例
18拒答过度生成层知识库有信息但模型仍拒绝回答用明确有答案的问题测试拒答率

缺陷优先级建议

在企业场景中,优先关注的缺陷:#6(编造)> #1(未命中)> #14(权限穿透)> #4(跨文档冲突)> #16(数值幻觉)。这些缺陷的业务影响最大,而且用户往往无法自行发现。

第8章:RAGAS 工具实操

8.1 RAGAS 介绍

RAGAS(Retrieval Augmented Generation Assessment)是目前 GitHub 上最高星的开源 RAG 评测框架。它提供了一套标准化的指标和评估流程,能够自动化地评估 RAG 系统的各个维度。

RAGAS 核心特点

  • 基于 LLM 的自动评估,无需人工打分
  • 支持 Faithfulness、Answer Relevancy、Context Precision、Context Recall 等核心指标
  • 与 LangChain、LlamaIndex 等框架无缝集成
  • 支持自定义评估指标

8.2 安装与基础配置

bash
pip install ragas datasets

如果需要使用 OpenAI 作为评估模型:

bash
pip install openai
export OPENAI_API_KEY="sk-your-key-here"

8.3 完整代码示例

第一步:准备评测数据集

python
from datasets import Dataset

questions = [
    "新员工入职第一年有多少天年假?",
    "公司的加班审批流程是什么?",
    "试用期员工可以请年假吗?",
    "年假可以跨年使用吗?",
    "员工离职时未休年假如何处理?"
]

ground_truths = [
    ["入职满一年后可享受5天带薪年假。"],
    ["加班需提前一天由直属主管在OA系统中审批,紧急加班可事后24小时内补审批。"],
    ["试用期员工不享受年假,转正后从转正日开始计算年假资格。"],
    ["年假原则上不跨年使用,特殊情况经部门总监审批后可延至次年3月31日前使用。"],
    ["离职时未休年假按日薪标准折算补偿,计算公式为:月薪÷21.75×未休天数。"]
]

# 这些是 RAG 系统实际检索返回的上下文
contexts = [
    ["4.2 年假制度:员工自入职之日起满一年,可享受5天带薪年假。满五年者增至10天。"],
    ["5.1 加班管理:所有加班需提前一天由直属主管在OA系统中提交审批。如遇紧急情况,可在加班后24小时内补充审批流程。"],
    ["4.1 试用期管理:试用期一般为3个月,试用期内不享受年假和带薪病假。",
     "4.2 年假制度:员工自入职之日起满一年,可享受5天带薪年假。"],
    ["4.2.3 年假跨年规定:年假原则上应在当年12月31日前休完,不可跨年累积。",
     "如有特殊原因,经部门总监书面审批后,最迟可延至次年3月31日前使用完毕。"],
    ["6.3 离职结算:员工离职时,人力资源部应在最后工作日前完成以下结算...",
     "未休年假按照以下公式折算补偿:月薪÷21.75×未休天数。"]
]

# RAG 系统实际生成的回答
answers = [
    "根据公司规定,新员工入职满一年后可以享受5天带薪年假。",
    "加班需要提前一天由直属主管在OA系统中审批。紧急情况下可以在加班后24小时内补审批。",
    "试用期员工不能请年假。根据公司制度,试用期内不享受年假,需要转正后从转正日起计算年假资格。",
    "年假原则上不能跨年使用,但经部门总监书面审批后可以延至次年3月31日前使用。",
    "离职时未休年假会按日薪标准折算补偿,计算公式为月薪除以21.75再乘以未休天数。"
]

eval_dataset = Dataset.from_dict({
    "question": questions,
    "answer": answers,
    "contexts": contexts,
    "ground_truth": [gt[0] for gt in ground_truths]
})

第二步:配置评估指标并运行

python
from ragas import evaluate
from ragas.metrics import (
    faithfulness,
    answer_relevancy,
    context_precision,
    context_recall,
    answer_correctness
)

result = evaluate(
    eval_dataset,
    metrics=[
        faithfulness,
        answer_relevancy,
        context_precision,
        context_recall,
        answer_correctness
    ]
)

print(result)

第三步:解读结果

# 输出示例:
# {
#   'faithfulness': 0.9500,
#   'answer_relevancy': 0.9200,
#   'context_precision': 0.8800,
#   'context_recall': 0.9000,
#   'answer_correctness': 0.8700
# }

# 转为 DataFrame 查看每条的详细结果
df = result.to_pandas()
print(df[['question', 'faithfulness', 'context_recall', 'answer_correctness']])

# 找出表现最差的问题
worst = df.sort_values('answer_correctness').head(3)
print("表现最差的 3 个问题:")
print(worst[['question', 'faithfulness', 'context_recall', 'answer_correctness']])

结果解读指南

  • faithfulness = 0.95:95% 的回答内容有上下文支撑,仅 5% 存在潜在幻觉
  • context_recall = 0.90:90% 的必要信息被成功检索到,有 10% 的信息缺失
  • context_precision = 0.88:检索结果中 88% 是有用的,12% 是噪声
  • 如果某个指标突然下降,顺着本章第3节的"诊断表"去定位问题

第9章:自定义 RAG 评测脚本

9.1 分段计时:定位性能瓶颈

RAG 链路的每个环节都有耗时,找到性能瓶颈需要分段计时。

python
import time
import json
from openai import OpenAI

client = OpenAI()

def rag_pipeline_with_timing(query: str, knowledge_base, embed_model, reranker=None):
    """RAG 全链路带分段计时"""
    timings = {}

    # 1. Embedding 耗时
    t0 = time.perf_counter()
    query_vector = embed_model.encode(query)
    timings["embedding_ms"] = round((time.perf_counter() - t0) * 1000, 2)

    # 2. 检索耗时
    t0 = time.perf_counter()
    raw_results = knowledge_base.search(query_vector, top_k=20)
    timings["retrieval_ms"] = round((time.perf_counter() - t0) * 1000, 2)

    # 3. Rerank 耗时
    if reranker:
        t0 = time.perf_counter()
        reranked = reranker.rerank(query, raw_results, top_k=5)
        timings["rerank_ms"] = round((time.perf_counter() - t0) * 1000, 2)
    else:
        reranked = raw_results[:5]
        timings["rerank_ms"] = 0

    # 4. 大模型生成耗时
    contexts = "\n---\n".join([r["text"] for r in reranked])
    prompt = f"基于以下上下文回答用户问题。如果上下文中没有相关信息,请明确说明。\n\n上下文:\n{contexts}\n\n问题:{query}"

    t0 = time.perf_counter()
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": prompt}],
        temperature=0.1
    )
    timings["generation_ms"] = round((time.perf_counter() - t0) * 1000, 2)

    # 汇总
    timings["total_ms"] = round(sum(timings.values()), 2)
    return {
        "answer": response.choices[0].message.content,
        "contexts": [r["text"] for r in reranked],
        "timings": timings
    }

# 输出示例:
# {
#   "timings": {
#     "embedding_ms": 12.35,
#     "retrieval_ms": 45.67,
#     "rerank_ms": 128.90,
#     "generation_ms": 2340.12,
#     "total_ms": 2527.04
#   }
# }

9.2 检索质量自动验证

python
def evaluate_retrieval_quality(test_cases: list) -> dict:
    """
    test_cases 格式:
    [{"query": "...", "expected_doc_ids": ["doc1", "doc2"], "retrieved_doc_ids": ["doc2", "doc3", "doc4"]}]
    """
    total_recall, total_precision, total_mrr = 0, 0, 0

    for case in test_cases:
        expected = set(case["expected_doc_ids"])
        retrieved = case["retrieved_doc_ids"]
        retrieved_set = set(retrieved)

        hits = expected & retrieved_set
        recall = len(hits) / len(expected) if expected else 0
        precision = len(hits) / len(retrieved) if retrieved else 0

        rr = 0
        for i, doc_id in enumerate(retrieved):
            if doc_id in expected:
                rr = 1 / (i + 1)
                break

        total_recall += recall
        total_precision += precision
        total_mrr += rr

    n = len(test_cases)
    return {
        "avg_recall": round(total_recall / n, 4),
        "avg_precision": round(total_precision / n, 4),
        "mrr": round(total_mrr / n, 4),
        "total_cases": n
    }

9.3 LLM-as-Judge 评估回答质量

python
def llm_judge_answer(question: str, context: str, answer: str, ground_truth: str) -> dict:
    """用 LLM 从三个维度评判回答质量"""
    judge_prompt = f"""你是一个严格的RAG系统评审员。请根据以下信息评估回答质量。

【用户问题】
{question}

【检索到的上下文】
{context}

【系统回答】
{answer}

【标准答案】
{ground_truth}

请从以下三个维度打分(1-5分),并给出简短理由:
1. 忠实度:回答是否完全基于上下文,没有编造信息
2. 完整性:回答是否覆盖了问题所需的全部关键信息
3. 准确性:回答与标准答案的事实一致程度

请以 JSON 格式输出:
{{"faithfulness": {{"score": X, "reason": "..."}}, "completeness": {{"score": X, "reason": "..."}}, "accuracy": {{"score": X, "reason": "..."}}}}"""

    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": judge_prompt}],
        temperature=0,
        response_format={"type": "json_object"}
    )
    return json.loads(response.choices[0].message.content)

# 用法示例
result = llm_judge_answer(
    question="新员工年假几天?",
    context="4.2 年假制度:员工自入职之日起满一年,可享受5天带薪年假。",
    answer="新员工入职满一年后可享受5天带薪年假。",
    ground_truth="入职满一年后可享受5天带薪年假。"
)
# 输出: {"faithfulness": {"score": 5, "reason": "完全基于上下文"}, ...}

第10章:TruLens 实操

10.1 TruLens 介绍

TruLens 是由 Truera 公司开源的 LLM 应用评测和追踪框架。它的核心理念是RAG Triad——用三个维度来衡量 RAG 系统的质量。

TruLens 核心能力:TruLens 核心能力 自动化的 RAG Triad 评测 可视化仪表盘(Leaderboard) 应用调用的完整追踪记录 与 LangChain / LlamaIndex 集成 RAG Triad 三维度:RAG Triad 三维度 Context Relevance: 检索到的上下文与问题的相关性 Groundedness: 回答是否基于上下文(≈ Faithfulness) Answer Relevance: 回答与问题的相关性

10.2 安装与配置

bash
pip install trulens trulens-providers-openai

10.3 RAG Triad 评测配置

from trulens.core import TruSession, Feedback, Select
from trulens.providers.openai import OpenAI as TruOpenAI

# 初始化
session = TruSession()
provider = TruOpenAI(model_engine="gpt-4o")

# 定义 RAG Triad 三个反馈函数
f_context_relevance = (
    Feedback(provider.context_relevance_with_cot_reasons, name="Context Relevance")
    .on_input()
    .on(Select.RecordCalls.retrieve.rets[:])
    .aggregate(lambda x: sum(x) / len(x) if x else 0)
)

f_groundedness = (
    Feedback(provider.groundedness_measure_with_cot_reasons, name="Groundedness")
    .on(Select.RecordCalls.retrieve.rets[:].collect())
    .on_output()
)

f_answer_relevance = (
    Feedback(provider.relevance_with_cot_reasons, name="Answer Relevance")
    .on_input()
    .on_output()
)

10.4 包装 RAG 应用并追踪

from trulens.apps.custom import TruCustomApp, instrument

class RAGApp:
    @instrument
    def retrieve(self, query: str) -> list:
        """检索环节 — TruLens 会自动追踪这个方法"""
        # 你的检索逻辑
        results = vector_db.search(query, top_k=5)
        return [r["text"] for r in results]

    @instrument
    def generate(self, query: str, contexts: list) -> str:
        """生成环节"""
        context_str = "\n".join(contexts)
        response = client.chat.completions.create(
            model="gpt-4o",
            messages=[{"role": "user", "content": f"上下文:{context_str}\n问题:{query}"}]
        )
        return response.choices[0].message.content

    @instrument
    def query(self, question: str) -> str:
        """完整 RAG 调用"""
        contexts = self.retrieve(question)
        return self.generate(question, contexts)

rag_app = RAGApp()

# 用 TruLens 包装
tru_app = TruCustomApp(
    rag_app,
    app_name="企业知识库问答",
    app_version="v1.0",
    feedbacks=[f_context_relevance, f_groundedness, f_answer_relevance]
)

# 运行评测
test_questions = [
    "新员工年假有多少天?",
    "加班审批流程是什么?",
    "出差报销标准是多少?"
]

with tru_app as recorder:
    for q in test_questions:
        rag_app.query(q)

10.5 启动仪表盘查看结果

from trulens.dashboard import run_dashboard

run_dashboard(session)
# 打开 http://localhost:8501 查看可视化仪表盘
# 仪表盘包含:
# - 应用排行榜(Leaderboard)
# - 每条调用的详细追踪记录
# - RAG Triad 三个维度的得分分布
# - 低分记录的自动高亮

TruLens 仪表盘的核心价值

  1. 对比不同版本(v1.0 vs v2.0)的 RAG 系统表现
  2. 快速定位低分问题,查看具体哪个环节出了问题
  3. 追踪每次调用的完整链路(检索 → 上下文 → 生成)
  4. 支持持续集成——每次发布后自动运行评测集

第11章:案例 — 企业知识库问答系统评测

11.1 场景描述

评测对象

某中型互联网公司的内部知识库问答系统(HR 制度、IT 运维、财务报销),使用 LlamaIndex + Milvus + GPT-4o 搭建。知识库规模约 1200 篇文档,涵盖公司制度手册、技术文档、FAQ 等。

11.2 评测数据集(15条示例)

#问题类型难度标准答案(摘要)
1新员工入职第一年有几天年假?事实型简单满一年后5天
2加班审批流程是怎样的?流程型简单OA 提前一天申请,主管审批
3年假可以跨年使用吗?条件型中等原则不可,特批可延至次年3月底
4出差报销的住宿标准是多少?事实型简单一线城市500/天,二线350/天
5如何申请 VPN 账号?操作型简单在 IT 工单系统提交申请
6试用期被辞退有没有补偿?法规型中等无经济补偿,但需提前3天通知
7公司支持远程办公吗?需要什么条件?多条件中等支持,需主管审批+VPN+周报
82024年修订的数据安全管理办法第5条说了什么?精确引用困难第5条内容全文
9产假和陪产假分别是多少天?多点事实中等产假158天,陪产假15天
10研发部门和市场部门的加班审批流程有什么区别?对比型困难研发需CTO审批,市场需CMO审批
11公司有没有宠物托管的福利?拒答测试简单知识库无此信息,应拒答
12竞品A的薪酬体系是怎样的?拒答测试简单应明确拒答
13同时满足哪些条件可以申请居家办公?条件汇总困难需汇总3个文档的条件
14上个季度公司营收是多少?时效性中等应说明信息可能不是最新或拒答
15员工手册里关于"迟到"的规定(原文有表格)格式保持困难应保持表格格式输出

11.3 分层评测结果

评测维度指标得分判定备注
检索层Context Recall0.82待改进#8 精确引用型和 #13 跨文档型未召回
Context Precision0.75待改进平均每个问题有 25% 的噪声上下文
生成层Faithfulness0.91良好#9 存在数值幻觉(158→180天)
Answer Relevancy0.88良好#10 对比型问题回答偏离
端到端Answer Correctness0.79待改进被检索层拖累
拒答准确率0.50#11 编造了宠物托管福利

11.4 问题定位与优化建议

问题根因优化方案优先级
精确引用型检索失败Embedding 对"第X条"等结构化引用表达不敏感引入混合检索(BM25 + 向量),给精确查询走关键词通道P0
拒答率低,会编造信息Prompt 中缺少"无信息时拒答"的强制指令在系统 Prompt 中增加拒答规则,添加"仅基于以下上下文"的约束P0
跨文档综合检索不足单次检索 Top-5 无法覆盖分散在多个文档中的信息增大 Top-K 到 10,加 Rerank 筛选;或使用子问题拆分策略P1
数值幻觉模型对数值信息的复现不够精确在 Prompt 中添加"数值务必与原文保持一致"的指令,降低 temperatureP1
检索噪声过多缺少 Rerank 环节引入 Rerank 模型(如 bge-reranker-v2),过滤低相关性片段P2

优化后的预期效果

根据经验,实施 P0 级优化(混合检索 + 拒答策略)后,Context Recall 通常可提升至 0.90+,拒答准确率可提升至 0.85+。配合 Rerank 的加入,Context Precision 通常可达 0.85+。

第12章:课堂练习

课堂练习

练习1:构建 RAG 评测数据集 选择你所在团队的一个 RAG 应用(或使用公开的知识库问答系统),完成以下任务:

  1. 从知识库中选取 10 篇有代表性的文档
  2. 基于这些文档,构建 20 条测试数据(覆盖事实型、流程型、对比型、拒答型)
  3. 为每条数据标注 questionground_truthcontexts
  4. 使用 RAGAS 运行评估,记录五项核心指标
  5. 找出得分最低的 3 条数据,分析根因并提出优化建议

课堂练习

练习2:分层诊断一个 RAG 缺陷 假设你的 RAG 系统出现以下现象:

  • 用户问:"公司差旅报销的住宿标准是多少?"
  • 系统回答:"一线城市住宿标准为每天800元,二线城市为500元。"
  • 标准答案:"一线城市500元/天,二线城市350元/天。" 请完成以下分析:
  1. 这是检索层问题还是生成层问题?列出你的判断依据。
  2. 设计 3 个实验来验证你的假设(例如:检查检索到的上下文是否正确)。
  3. 根据实验结果,给出具体的修复方案。

课堂练习

练习3:编写自动化 RAG 回归测试 编写一个 Python 脚本,实现以下功能:

  1. 从 JSON 文件加载 Golden Set(至少 10 条)
  2. 逐条调用 RAG 系统 API,获取回答和检索到的上下文
  3. 计算 Recall@5 和 Precision@5
  4. 用 LLM-as-Judge 打分 Faithfulness(1-5分)
  5. 生成评测报告,包含:整体通过率、各指标均值、最差 case 列表
  6. 当任一指标低于阈值时,脚本返回非零退出码(用于 CI/CD 集成)

补充参考答案要点

  • 练习1构建数据集时,必须同时标注问题、标准答案、支撑文档片段和问题类型,否则后续无法做分层评测。
  • 练习2分层诊断时,要先区分是没召回、召回错、拼接错,还是生成阶段乱说,不能把所有问题都归成“幻觉”。
  • 练习3自动化回归时,至少要输出检索指标、答案指标和拒答指标,并保留 case 级结果用于 diff 分析。