跳到正文

第 10 章:系统评估与上线优化

能用 30 行代码算出检索的 Recall@k 和 MRR,讲清评估五维度、测试集怎么构建、检索段/生成段分别怎么评,以及上线后怎么把首字响应压下来

📊 学习时长:55-70 分钟 🎯 完成后能力:能用 30 行代码算出检索的 Recall@k 和 MRR,讲清评估五维度、测试集怎么构建、检索段/生成段分别怎么评,以及上线后怎么把首字响应压下来 🔗 关联面试题:5 道(覆盖 5 个不同角度,见 Part 3)

这一章你会学到什么

  • ✓ 零基础也能跟着做:用一段零依赖代码,亲手把"检索好不好"算成 Recall@k 和 MRR 两个数字
  • ✓ 看懂评估为什么是优化的前提:没有评估集,调参全凭感觉
  • ✓ 掌握评估五维度(召回准确 / 可信度 / 速度 / 可扩展 / 体验)
  • ✓ 学会测试集怎么构建(标 ground-truth、覆盖难度、人工校验)
  • ✓ 分清检索段和生成段各自怎么评
  • ✓ 理解上线后的延迟优化:三层缓存、ANN、异步并发、流式输出

本章按三段式组织,不同基础的读者各取所需:

段落 给谁看 内容 占比
🚀 Part 1 · 主线实战 零基础,想先跑起来 概念白话 + 评估脚本 demo ~45%
🎯 Part 2 · 面试深度 想讲透、备战面试 五维度 + 测试集 + 分段评估 + 上线优化 ~45%
🏆 Part 3 · 验收串题 检验学到位没 关联面试题 + 自检清单 ~10%

🚀 Part 1 · 主线实战 | ~45% 这部分零基础也能跟着做。先用一段不依赖任何库的代码,把"检索好不好"算成客观数字。

在开始之前

你需要:

  • ✅ 装好 Python(3.9+),能看懂基础 Python(列表、字典、函数)
  • ✅ 本章 demo 零依赖,直接跑
  • ✅ 最好读过第 3 章(召回率概念)和第 5、6 章(检索 + 重排),这一章是给它们"打分"的

先看一个评估缺失的教学场景(这是本章的"为什么")

下面是为了说明评估流程而构造的团队讨论,不是作者或学员的客户项目记录。前面九章一路在"优化":换切分、上混合检索、加重排……每次改完都有人说"感觉更准了"。这时应该追问:"准了多少?你怎么知道是真变好,不是错觉?"

如果回答不出来,说明团队仍在凭感觉调参。改了 A 觉得好,改了 B 也觉得好,可到底哪个真有用、提升多少,谁也说不清,因为没有一把客观的尺子

正确做法是建立评估集:从目标资料与使用场景中抽样问题,每道事先人工标好"正确答案应该来自哪个 chunk"。每次改动都在同一套题上运行,比较 Recall、MRR、延迟和成本。正文不提供可复制到项目或简历里的固定提升数字。

一句话:准备上线前,应建立与业务风险匹配、可以复查的评估和验收依据;不能只凭主观感觉判断优化是否有效。

这一章,我们就先用一段零依赖代码,把这把"尺子"亲手做出来。

3 个核心概念(先白话,再术语)

概念 1:为什么需要"评估集"?

评估集,就是一批事先标好标准答案或证据位置的题。有了它,你的每次优化才能从"我觉得变好了"变成"在同一数据、标注规则和配置下,某项指标发生了可复查的变化"。

没有评估集,你没法回答"好多少",也没法判断两个方案哪个更好:优化就成了玄学。

概念 2:Recall@k 和 MRR,衡量检索的两把尺

  • Recall@k(召回率):正确的那个 chunk,有没有出现在前 k 个检索结果里。看的是"该找的找到没有"。
  • MRR(平均倒数排名):正确的 chunk 排在第几位(排第 1 得 1 分、第 2 得 0.5 分、没召回得 0 分),再对所有题取平均。看的是"找到了,但排得够不够靠前"。

一个看"全不全",一个看"靠不靠前",两个一起,检索质量就量化了。

概念 3:离线评估 vs 线上指标

  • 离线评估:用评估集在本地跑,算 Recall/MRR/准确率:上线前反复优化用。
  • 线上指标:系统真跑起来后看的:响应延迟(P95/P99)、QPS 吞吐、用户满意度:上线后盯着不出事。

离线评估与线上指标解决不同问题。通常先用离线数据筛掉明显不合格的方案,再通过灰度、监控和人工复核观察真实流量表现;高风险场景还需要更严格的上线门槛。具体顺序与覆盖范围应按风险、数据可得性和发布策略确定。

🛠️ 跟着做:30 行算出 Recall@k 和 MRR

Step 1:写评估函数

新建 eval_demo.py(完整文件见 code-snippets/eval_demo.py,这里是核心):

def recall_at_k(qa, retrieved, k):
    """Recall@k:有多少题的正确 chunk,出现在了前 k 个检索结果里。"""
    hit = sum(1 for item in qa if item["gold"] in retrieved[item["q"]][:k])
    return hit / len(qa)

def mrr(qa, retrieved):
    """MRR:正确 chunk 排得越靠前分越高(第1得1,第2得0.5…没召回得0),再取平均。"""
    total = 0.0
    for item in qa:
        ranked = retrieved[item["q"]]
        rank = ranked.index(item["gold"]) + 1 if item["gold"] in ranked else 0
        total += (1.0 / rank) if rank else 0.0
    return total / len(qa)

Step 2:运行

python eval_demo.py

你应该看到(评估集固定,结果是确定的):

评估集共 3 道题

Recall@1 = 0.33   (只看排第1有没有命中)
Recall@3 = 0.67   (前3个里有没有命中)
MRR      = 0.50   (正确chunk的平均排名,越高越靠前)

🐛 跑不通看这里

  • KeyError?RETRIEVED 的键必须和 QA 里每道题的 q 一字不差,这个 demo 用问题文本当键来查检索结果。
  • 数字和上面对不上?确认没改 QAgoldRETRIEVED 的排序;改了任何一个,指标当然就变了(这正是 Step 4 要你做的)。
  • Recall@1 比 Recall@3 还高?不可能:k 越大召回只会不降反升,出现这种结果说明切片 [:k] 写错了。

Step 3:看懂这三个数字

  • Recall@1 = 0.33:3 道题里只有 1 道(犹豫期)正确 chunk 排在第 1;
  • Recall@3 = 0.67:放宽到前 3,有 2 道命中(犹豫期、等待期),核辐射那道根本没召回;
  • MRR = 0.50:(1/1 + 1/2 + 0) ÷ 3 = 0.5,综合反映"找到了没、排第几"。

换一套切分/检索方案,再跑这同一批题,两组数字一对比,到底哪个更好就一目了然。 这就是"用数据说话"。

Step 4:🛠️ 动手实验,改一条结果,看指标怎么动

RETRIEVED["核辐射保不保"] 改成 ["c3", "c4", "c5"](让正确的 c3 排第 1),再跑一次:

  • 你会看到 Recall@1 从 0.33 涨到 0.67、MRR 从 0.50 涨到 0.83:一条结果变好,整体指标立刻反映出来。
  • 这正是真实优化的缩影:你改进检索 → 某些题的正确 chunk 排得更靠前 → 指标上涨。亲手改一条,你就懂了指标是怎么"感知"优化的。

🧭 你刚才做的,等于真实系统的什么?

  • 你的 QA(标好 gold chunk 的题)= 后续项目里可继续扩展的人工标注评估集;样本量应按资料类型、风险与统计稳定性决定,不能预填固定规模;
  • 你的 recall_at_k / mrr = 业界标准的检索评估指标,公式一模一样;
  • 你这个"换方案再跑一遍对比" = 离线评估流程的最小演示:固定评估集,只改变被比较的方案,再记录结果。 你手搓的是评估的内核;Part 2 讲完整的评估五维度、测试集怎么标、生成段怎么评、上线后延迟怎么压。

这一节你掌握了什么?

  • ✅ 为什么没有评估集就没法谈优化
  • ✅ Recall@k 和 MRR 各看什么、怎么算
  • ✅ 亲手把"检索好不好"算成了客观数字,看懂了指标怎么感知优化

🎯 Part 2 · 面试深度 | ~45% 这部分讲透评估五维度、测试集怎么建、检索段/生成段分开评、上线后怎么提速。

2.1 评估五维度:别只盯着"准不准"

评估上线准备时,光看"答得对"通常不够。可以按业务风险从下面五个维度选择指标:

  • ① 召回 / 准确:检索用 Recall@k、MRR;生成用 BLEU、ROUGE 对比参考答案。
  • ② 可信度:答案里有几成内容能在检索到的原文里找到依据(覆盖率)。
  • ③ 响应速度:平均延迟,以及 P95 / P99 尾延迟(95%、99% 的请求多久内返回)。
  • ④ 可扩展性:数据涨到百万级、并发上来后,QPS(每秒查询数)还扛不扛得住。
  • ⑤ 用户体验:人工满意度评分、答案可读性(别一堆术语让用户看不懂)。

业内行话:只算准确率可能漏掉尾延迟、负载、成本、可信度和可读性风险。面试聊评估时,应先说明目标用户和失败代价,再从准确、可信、速度、扩展、体验中选择适用指标,并给出验收阈值如何通过基线与实测确定。

2.2 测试集怎么构建

评估集的质量,直接决定评估靠不靠谱。构建流程:

  1. 选代表性文档:覆盖各主题/产品,别偏斜;
  2. 生成问题:可以用 LLM 从文档生成候选题,但正式纳入评估前应按风险抽检或复核,关键题由人工确认,避免题目只覆盖简单阅读理解;
  3. 标 ground-truth:为每道题标好参考答案 + 支撑它的 chunk(答案来自哪个片段);
  4. 人工校验:抽查一部分,确认"答案确实能由标注的证据推出";
  5. 覆盖 + 平衡:难度要分层:既有"犹豫期多久"这种事实题,也要有需要综合多段的多跳复杂题;多领域时各领域题量要均衡。

业内行话:ground-truth 证据(答案来自哪个 chunk)是检索和生成评估的重要基础:检索段用它判断是否找到目标证据,生成段用它检查答案是否受到证据支持。评估集规模应由查询分布、风险和期望覆盖决定;比固定题数更重要的是记录来源、标注规则、分层覆盖和复核流程。

2.3 检索段 vs 生成段:分开评才能定位问题

RAG 回答差,可能是"没找到",也可能是"找到了没用好"。分段评估才能定位:

  • 检索段(找得对不对):Precision@K(前 K 个里多少相关)、Recall@K(该找的找回多少)、MRR(排得靠不靠前)、冗余率(结果是否大量重复)。
  • 生成段(答得好不好):准确性(BLEU/ROUGE 对参考答案)、忠实度(答案是否都有依据,可用 RAGAS 这类工具让 LLM 打分)、证据充分性(关键要点覆盖全没)、可读性。

业内行话:这是排障的关键思路:回答不好,先看检索段指标。如果 Recall 就低,说明"该找的没找到",问题在切分/检索,生成再强也白搭(第 5 章那条铁律:召回率是天花板);如果检索 Recall 很高、但生成还差,问题就在 prompt 或模型,该去优化第 9 章那套。不分段,你连往哪使劲都不知道。

2.4 别全从零造:可复用基准与自动评估

  • 公开基准:HotpotQA(多跳问答,带"支持事实"标注,测跨文档检索 + 推理)、TriviaQA / NQ(开放域问答)等,适合横向比能力。
  • 自动评估框架:RAGAS 这类工具,让 LLM 自动给答案的"忠实度/相关性"打分,省去大量人工。

工程提示:公开基准适合横向比较能力,但部署到具体场景前还要用目标数据分布的评估集验收。公开基准是参考,目标场景的离线评估、灰度数据、人工复核和线上监控共同构成主要依据。

2.5 上线前后的性能优化:先测量,再定目标

离线评估达到门槛后,还要验证延迟和并发。缓存是候选手段之一,是否值得做取决于重复查询比例、数据新鲜度和一致性要求:

  • 三层缓存(按需选择):① Embedding 缓存(重复 query 不重算)② 检索结果缓存(热门问题复用文档列表)③ 答案缓存(适合答案稳定、可正确失效的 FAQ)。可用 Redis 等缓存实现并设置 TTL,同时验证命中率与陈旧风险。
  • ANN 索引(HNSW):减少需要精确比较的候选;召回与延迟收益受规模、硬件和参数影响,需与精确检索实测。
  • 异步并发 + 批量:让 Embedding、检索、生成流水线重叠执行;高并发用任务队列摊薄开销。
  • 流式输出:边生成边返回结果,可能改善首字时间(TTFT)和等待感受,但不会缩短全部生成耗时,仍需实测。

工程提示:盯延迟不能只看平均值,还要看 P95 / P99。缓存、ANN、并发和流式输出都可能改善不同阶段,但不存在对所有数据规模和硬件都成立的固定收益或固定顺序。先用链路追踪找到瓶颈,再一次只改一个变量并复测。


🏆 Part 3 · 验收串题 | ~10% 学到这一步,做几道题验证一下,知道自己学到位没。

关联面试题(5 道,覆盖 5 个角度)

  1. 【评估维度与方法】 评估 RAG / Agent 系统通常用哪些指标和方法?请说明评估的维度。
  2. 【MRR 指标】 RAG 里 MRR(平均倒数排名)怎么定义?它在评估时有什么特殊意义?
  3. 【评估数据集设计】 如何设计和构建高质量评估数据集?数据采集、标注流程、负样本怎么处理?
  4. 【检索段评价体系】 衡量召回阶段质量有哪些成熟的离线与在线评估指标?
  5. 【高并发延迟优化】 高并发场景下,如何从架构/算法/工程角度优化召回段和生成段的延迟?

学完这一章你应该能干嘛

跟着做(Part 1):

  • 能用 30 行算出 Recall@k 和 MRR,讲清它们各看什么
  • 能说清为什么没有评估集就没法谈优化

讲深度(Part 2):

  • 能讲清评估五维度,以及为什么只看准确率会翻车
  • 能讲清测试集怎么构建(标 ground-truth、覆盖难度、人工校验)
  • 能讲清检索段/生成段分开评,以及怎么用它定位问题
  • 能说出上线延迟优化的主要手段(三层缓存 / ANN / 并发 / 流式),并知道要盯 P95/P99

4 项以下 → 回去 Part 1 重做;4–5 项 → Part 2 再看一遍;6 项以上 → 进入附录。

完整代码 + 数据集

本书每章的完整可运行代码 + 测试样本,都在配套 GitHub 仓库:

🔗 github.com/MisterBooo/rag-from-zero

  • 跟着教程 clone 下来就能跑
  • 本章的检索评估脚本(零依赖)也在里面
  • 欢迎 Star ⭐ / Issue 反馈

导航

← 上一章:上下文问答与引用溯源 | 下一章:把这个项目写进简历 & 面试应答 → | 回到项目首页