智能体面试准备(十九):RAG 评估体系——检索指标、RAGAS、忠实度与故障归因
智能体面试准备(十九):RAG 评估体系——检索指标、RAGAS、忠实度与故障归因
上一篇《Function Calling 全链路》讲的是智能体怎么正确地调工具,这一篇回到检索这条主线。A 系列第十二篇讲过 RAG 怎么搭,但面试真正卡人的往往不是"你会不会搭",而是"你的 RAG 到底好不好,你怎么知道"。RAG 是一个多阶段级联系统,任何一环坏了最终都表现为"答得不对",如果没有分段归因能力,调优就变成了盲目试参数。今天这篇把 RAG 评估体系一次讲透:从检索侧的经典 IR 指标,到生成侧的忠实度与相关性,再到 RAGAS 这类自动化框架的原理与坑,最后给一套可落地的分层归因方案。结尾照例是面试速答与高频追问清单。
一、先想清楚:RAG 为什么必须分段评估
1.1 级联系统的误差传播
RAG 不是一个模型,是一条流水线。每一环的错误都会向下传播且不可逆。
用户问题
│
▼
[1] 查询理解/改写 ──── 改写跑偏 → 后面全错
│
▼
[2] 检索(召回) ──────── 没召回到 → 生成再强也答不出
│
▼
[3] 重排(rerank) ─────── 排序差 → 关键片段被截断丢弃
│
▼
[4] 上下文组装 ───────── 截断/顺序不当 → 中间迷失
│
▼
[5] 生成 ─────────────── 有材料却编造 → 忠实度问题
│
▼
最终答案
关键洞察:端到端只看一个"回答正确率",等于把五个环节的问题混成一锅粥。 假设某系统端到端准确率 60%,这 40% 的错误里到底是"没检索到"还是"检索到了但没用好"?两者的修复动作完全不同——前者要改 embedding 模型或分块策略,后者要改 prompt 或换生成模型。分不清就只能瞎调。
这就是面试的第一个高分点:RAG 评估的首要目标不是给出一个分数,而是提供归因能力。
1.2 评估的二维框架
检索质量(有没有给对材料)
高 低
┌──────────┬──────────┐
高 │ 理想 │ 幻觉/编造 │
生成质量 │ 区 │ (材料不足) │
(用没用好材料) ├──────────┼──────────┤
低 │ 有材料 │ 彻底失败 │
│ 不会用 │ │
└──────────┴──────────┘
四个象限对应四种完全不同的优化动作:
- 有材料不会用:改 prompt、加引用要求、换更强的生成模型、缩短上下文
- 材料不足导致编造:改分块、换 embedding、加 query 改写、扩大 top-k
- 彻底失败:先修检索,检索不修好,改生成没有意义
- 理想区:关注延迟和成本
二、检索侧评估:经典 IR 指标
2.1 需要的标注数据
检索评估需要 (query, 相关文档集合) 的标注。这是最贵的一步,但有低成本做法(后面 2.5 节讲)。
2.2 核心指标全解
设某 query 的真实相关文档集为 R,检索返回的前 k 个为 Retrieved@k。
Recall@k(召回率)—— RAG 里最重要的检索指标
Recall@k = |R ∩ Retrieved@k| / |R|
为什么最重要?因为召回是 RAG 的上限。没召回到的文档,生成阶段无论如何也变不出来。实践中通常关注 Recall@20 或 Recall@50(重排前的候选集),只要召回够高,排序问题可以靠 reranker 补救。
Precision@k(精确率)
Precision@k = |R ∩ Retrieved@k| / k
在 RAG 里 precision 的重要性低于 recall,但不是不重要——噪声文档会稀释注意力、挤占上下文、诱发幻觉。
MRR(Mean Reciprocal Rank)—— 只关心第一个正确答案排多前
MRR = (1/|Q|) × Σ_q 1 / rank_q(第一个相关文档的位置)
第 1 位命中 → 1.0 第 2 位 → 0.5 第 5 位 → 0.2
适合"只需要一个正确答案"的场景(如 FAQ 匹配)。
NDCG@k —— 考虑分级相关性和位置折扣,最全面
DCG@k = Σ_{i=1..k} rel_i / log2(i + 1)
IDCG@k = 理想排序下的 DCG
NDCG@k = DCG@k / IDCG@k
rel_i 可以是 0/1,也可以是 0-3 的分级相关度。NDCG 是学术界和搜索工业界的黄金标准。
Hit Rate@k(命中率)—— 最粗但最直观
Hit@k = 前 k 个里至少有一个相关文档的 query 占比
2.3 一份可直接用的实现
import numpy as np
from typing import Sequence
def recall_at_k(retrieved: Sequence[str], relevant: set, k: int) -> float:
if not relevant:
return 0.0
return len(set(retrieved[:k]) & relevant) / len(relevant)
def precision_at_k(retrieved: Sequence[str], relevant: set, k: int) -> float:
if k == 0:
return 0.0
return len(set(retrieved[:k]) & relevant) / k
def mrr(retrieved: Sequence[str], relevant: set) -> float:
for i, d in enumerate(retrieved, start=1):
if d in relevant:
return 1.0 / i
return 0.0
def ndcg_at_k(retrieved: Sequence[str], rel_map: dict, k: int) -> float:
"""rel_map: {doc_id: 相关度分级},未出现的按 0 计。"""
gains = [rel_map.get(d, 0) for d in retrieved[:k]]
dcg = sum(g / np.log2(i + 2) for i, g in enumerate(gains))
ideal = sorted(rel_map.values(), reverse=True)[:k]
idcg = sum(g / np.log2(i + 2) for i, g in enumerate(ideal))
return dcg / idcg if idcg > 0 else 0.0
def eval_retrieval(dataset, retriever, ks=(1, 5, 10, 20)):
"""dataset: [{'query':..., 'relevant': set(...), 'rel_map': {...}}, ...]"""
agg = {f"recall@{k}": [] for k in ks}
agg.update({f"ndcg@{k}": [] for k in ks})
agg["mrr"] = []
for item in dataset:
docs = retriever(item["query"], top_k=max(ks))
for k in ks:
agg[f"recall@{k}"].append(recall_at_k(docs, item["relevant"], k))
agg[f"ndcg@{k}"].append(ndcg_at_k(docs, item.get("rel_map", {d: 1 for d in item["relevant"]}), k))
agg["mrr"].append(mrr(docs, item["relevant"]))
return {m: round(float(np.mean(v)), 4) for m, v in agg.items()}
2.4 分块策略对指标的影响
一个容易忽略但面试很爱问的点:chunk 粒度直接决定指标的含义。
chunk = 整篇文档(2000 字)
召回容易(语义覆盖广),但塞进上下文浪费 token,噪声高
chunk = 段落(300 字)
精度高、token 省,但一个答案跨段落时容易只召回一半
chunk = 句子(50 字)
检索精准但上下文缺失,模型看不懂片段在说什么
主流解法是 small-to-big / 父子块:用小块做检索(精准匹配),命中后返回其父块(完整语境)。评估时要注意:指标要在"最终喂给模型的单元"上算,而不是在检索单元上算,否则会高估。
2.5 低成本构造标注集:反向生成法
没有标注怎么办?用 LLM 从文档反向生成问题。
GEN_Q_PROMPT = """阅读下面的文档片段,提出 2 个只能依据该片段回答的具体问题。
要求:
- 问题必须能被该片段完整回答,不依赖外部知识
- 问题要像真实用户提问,不要出现"根据上文""该片段"等字样
- 避免过于宽泛的问题
文档片段:
{chunk}
输出 JSON 数组:["问题1", "问题2"]"""
def build_eval_set(chunks: list[dict], llm, sample_n: int = 300):
"""每个 chunk 生成问题,天然形成 (query -> 该 chunk 为正例) 的标注。"""
import json, random
dataset = []
for c in random.sample(chunks, min(sample_n, len(chunks))):
try:
qs = json.loads(llm(GEN_Q_PROMPT.format(chunk=c["text"])))
except Exception:
continue
for q in qs:
dataset.append({"query": q, "relevant": {c["id"]}, "source_chunk": c["text"]})
return dataset
这个方法的已知缺陷必须主动说出来(面试加分):
1. 生成的问题往往和 chunk 用词高度重合,导致高估向量检索效果(词面匹配就能中)。缓解办法是要求 LLM 做同义改写、或者用另一个模型改写问题。
2. 只有单一正例,实际上可能有多个 chunk 都能回答,会低估 precision。
3. 分布与真实用户提问不同——真实用户会打错字、问得模糊、跨文档提问。所以这套集合适合做相对比较(A 方案 vs B 方案),不适合当绝对值汇报。
三、生成侧评估:忠实度与相关性
检索到了材料,生成阶段的问题是"有没有老老实实用材料回答"。
3.1 四个核心维度
┌──────────────┬────────────────────────────────────┐
│ Faithfulness │ 答案的每个论断是否都能由检索材料支撑 │
│ 忠实度 │ 低 → 幻觉、编造 │
├──────────────┼────────────────────────────────────┤
│ Answer │ 答案是否切题、直接回应了用户问题 │
│ Relevance │ 低 → 答非所问、大段无关铺陈 │
├──────────────┼────────────────────────────────────┤
│ Context │ 检索到的材料中有多少是真正有用的 │
│ Precision │ 低 → 噪声多,浪费 token 且干扰生成 │
├──────────────┼────────────────────────────────────┤
│ Context │ 回答所需的信息是否都被检索到了 │
│ Recall │ 低 → 材料不全,模型只能残缺作答 │
└──────────────┴────────────────────────────────────┘
注意 Faithfulness 和 Answer Relevance 是正交的:一个回答可以完全忠于材料但答非所问(材料讲的不是用户想问的),也可以切题但编造。必须分开测。
3.2 Faithfulness 的计算:论断分解法
这是 RAGAS 的核心思路,也是面试要能讲清楚的算法。
Step 1 把答案拆成若干原子论断 (atomic claims)
"北京是中国首都,人口约2100万" → ["北京是中国首都", "北京人口约2100万"]
Step 2 对每个论断,判断能否由 context 推出(NLI 二分类)
论断1: context 支持 → 1
论断2: context 未提及 → 0
Step 3 Faithfulness = 被支持的论断数 / 总论断数 = 1/2 = 0.5
CLAIM_SPLIT_PROMPT = """把下面这段答案拆解为若干条独立的原子论断。
要求:每条论断只包含一个可独立验证的事实,代词要还原为具体实体。
答案:
{answer}
输出 JSON 数组,每个元素是一条论断字符串。"""
NLI_PROMPT = """判断"论断"是否能由"参考材料"直接推出。
参考材料:
{context}
论断:
{claim}
规则:
- 材料明确支持该论断 → supported
- 材料明确反驳该论断 → contradicted
- 材料未提及、或需要外部知识才能判断 → not_mentioned
只输出一个词:supported / contradicted / not_mentioned"""
def faithfulness(answer: str, contexts: list[str], llm) -> dict:
import json
try:
claims = json.loads(llm(CLAIM_SPLIT_PROMPT.format(answer=answer)))
except Exception:
return {"score": None, "error": "claim_split_failed"}
if not claims:
return {"score": 1.0, "claims": []}
ctx = "\n\n".join(contexts)
results = []
for c in claims:
verdict = llm(NLI_PROMPT.format(context=ctx, claim=c)).strip().lower()
results.append({"claim": c, "verdict": verdict})
supported = sum(r["verdict"] == "supported" for r in results)
return {
"score": supported / len(claims),
"claims": results,
# 矛盾比未提及更严重,单独统计便于定位
"contradicted": sum(r["verdict"] == "contradicted" for r in results),
}
工程要点:
- 要区分 "contradicted"(与材料矛盾)和 "not_mentioned"(材料没提)。前者是严重错误,后者可能只是模型补充了常识(比如"北京是中国的城市")。只用二分类会把两者混淆,导致分数虚低。
- 论断拆解质量决定整个指标的质量。拆得太细(把"2100万"和"人口"拆开)会产生大量无意义论断;拆得太粗则漏检。
- 这个方法每个答案要调用 1 + N 次 LLM(N 为论断数),成本不低,要做采样评估而非全量。
3.3 Answer Relevance:反向生成问题法
思路很巧:如果答案切题,那么从答案反推出的问题应该和原问题很像。
def answer_relevance(question: str, answer: str, llm, embed, n: int = 3) -> float:
"""从答案反向生成 n 个问题,算与原问题的平均余弦相似度。"""
import numpy as np
prompt = f"根据下面的答案,反推出用户最可能问的问题。生成 {n} 个不同表述。\n答案:{answer}\n输出JSON数组。"
import json
try:
gen_qs = json.loads(llm(prompt))
except Exception:
return 0.0
q_vec = np.array(embed(question))
sims = []
for gq in gen_qs:
g_vec = np.array(embed(gq))
cos = float(q_vec @ g_vec / (np.linalg.norm(q_vec) * np.linalg.norm(g_vec) + 1e-9))
sims.append(cos)
return float(np.mean(sims)) if sims else 0.0
局限也要知道:这个指标对"答非所问"敏感,但对"答错了但很切题"不敏感——它测的是相关性不是正确性。
3.4 Context Precision:加权命中位置
Context Precision@K = Σ_k (Precision@k × v_k) / (相关 chunk 总数)
其中 v_k = 1 如果第 k 个 chunk 相关,否则 0
它奖励"相关 chunk 排在前面",因为靠前的 chunk 更容易被模型利用
这个指标把 IR 的排序质量和 LLM 的注意力分布结合起来了——呼应第十三篇讲过的"中间迷失(lost in the middle)"现象。
四、RAGAS 框架:能用,但要知道它的坑
RAGAS 是目前最流行的 RAG 自动评估库,把上面几个指标封装好了。
# 典型用法
from ragas import evaluate
from ragas.metrics import faithfulness, answer_relevancy, context_precision, context_recall
from datasets import Dataset
data = Dataset.from_dict({
"question": questions,
"answer": answers,
"contexts": contexts_list, # list[list[str]]
"ground_truth": ground_truths, # context_recall 需要
})
result = evaluate(data, metrics=[faithfulness, answer_relevancy,
context_precision, context_recall])
print(result)
四个必须知道的坑
坑一:分数不可跨项目比较。 RAGAS 分数严重依赖裁判模型、prompt 版本和库版本。你的 faithfulness 0.85 和别人的 0.85 不是一回事。只能用于同一套配置下的纵向对比(这版 vs 上版)。
坑二:成本和延迟被低估。 faithfulness 一条要跑 1+N 次 LLM 调用,1000 条样本可能就是上万次调用。必须做采样(200~500 条足够)和并发控制,否则一次评估跑几小时、花几百块。
坑三:中文场景要换 prompt。 默认 prompt 是英文的,对中文的论断拆解质量明显下降(尤其是长句拆分和代词还原)。必须自己重写中文版 prompt 并做人工校准。
坑四:指标之间会打架。 增大 top-k 会提升 context recall,但降低 context precision;强化"必须基于材料回答"的 prompt 会提升 faithfulness,但可能降低 answer relevance(模型变得过于保守、频繁拒答)。不能只优化单一指标,要看组合。
top_k 增大的效果
context_recall ↑↑
context_precision ↓↓
faithfulness ↓ (噪声诱发编造)
延迟/成本 ↑↑
→ 存在最优点,通常在 top_k = 5~10 区间,要实测
五、故障归因:一套可落地的分层诊断
这是本文最实用的部分,也是面试的压轴回答。
5.1 归因决策树
答案错了
│
┌──────────────┴──────────────┐
│ │
context_recall 低? context_recall 高
(材料里没有答案) (材料里有答案)
│ │
▼ ▼
检索侧问题 ┌────┴────┐
│ │ │
┌────┼────┬────────┐ faithfulness faithfulness
│ │ │ │ 低 高但答案错
▼ ▼ ▼ ▼ │ │
分块 向量 query 索引 ▼ ▼
太碎 模型 没改写 缺失 生成在编造 材料本身有误
或太 不匹配 (改prompt/ (数据源问题,
粗 领域 换模型/ 要治理知识库)
加引用约束)
5.2 实现:一次评估打全套标签
from dataclasses import dataclass, asdict
@dataclass
class RagDiagnosis:
qid: str
ctx_recall: float # 材料是否包含答案所需信息
ctx_precision: float # 材料噪声程度
faithfulness: float # 答案是否忠于材料
ans_relevance: float # 答案是否切题
correct: bool | None # 端到端是否正确(有 ground truth 时)
bucket: str = "" # 归因结论
def diagnose(self, th=0.6):
if self.ctx_recall < th:
self.bucket = "retrieval_miss" # 检索没给到材料
elif self.faithfulness < th:
self.bucket = "generation_hallucination" # 有材料却编造
elif self.ans_relevance < th:
self.bucket = "off_topic" # 忠实但答非所问
elif self.correct is False:
self.bucket = "source_error" # 材料本身错误
else:
self.bucket = "ok"
return self
def aggregate(diags: list[RagDiagnosis]) -> dict:
from collections import Counter
c = Counter(d.diagnose().bucket for d in diags)
total = max(len(diags), 1)
return {k: round(v / total, 4) for k, v in c.most_common()}
# 输出示例:
# {'ok': 0.62, 'retrieval_miss': 0.24, 'generation_hallucination': 0.09,
# 'off_topic': 0.03, 'source_error': 0.02}
# → 结论清晰:主要矛盾是检索召回,优先投入改分块和 embedding
有了这张分布表,优化方向就不再靠猜。"我们的 RAG 准确率 62%,其中 24% 的错误来自检索未召回,9% 来自生成幻觉,所以这个季度重点做检索优化" —— 这句话在面试里的分量远超任何指标罗列。
5.3 检索侧的针对性优化清单
一旦定位到 retrieval_miss 占比高,按性价比排序的动作是:
| 优先级 | 动作 | 典型收益 | 成本 |
|---|---|---|---|
| 1 | 加 BM25 做混合检索(RRF 融合) | Recall +5~15 点 | 极低,半天 |
| 2 | 加 reranker(bge-reranker / cohere) | NDCG +10 点 | 低,延迟 +50ms |
| 3 | 调分块策略(父子块 / 重叠窗口) | Recall +3~10 点 | 中,要重建索引 |
| 4 | Query 改写 / HyDE / 多查询 | Recall +5~10 点 | 中,延迟 +1 次 LLM |
| 5 | 领域微调 embedding | Recall +10~20 点 | 高,要标注数据 |
| 6 | 知识库治理(去重、补全、更新) | 视情况,可能最大 | 最高,但常被忽视 |
混合检索的 RRF 融合值得给一段代码,因为它是性价比最高的一招:
def rrf_fuse(rank_lists: list[list[str]], k: int = 60, top_n: int = 20):
"""Reciprocal Rank Fusion:把多路召回结果融合。
k=60 是论文推荐值,作用是压制头部排名的过度影响。"""
scores = {}
for lst in rank_lists:
for rank, doc in enumerate(lst, start=1):
scores[doc] = scores.get(doc, 0.0) + 1.0 / (k + rank)
return [d for d, _ in sorted(scores.items(), key=lambda x: -x[1])[:top_n]]
# 用法:向量召回 + BM25 召回融合
fused = rrf_fuse([vector_search(q, 50), bm25_search(q, 50)], top_n=20)
RRF 的好处是不需要归一化分数——向量相似度和 BM25 分数量纲完全不同,直接加权融合要调参,而 RRF 只用排名,鲁棒性极强。
六、线上评估:离线指标之外
离线评测集再好也是静态的,线上还需要无标注的质量监控。
6.1 可用的无标注信号
显式信号 点赞/点踩、"这个回答有帮助吗"
覆盖率低(<5% 用户会点),但准确
─────────────────────────────────────────
隐式信号 引用点击率 用户点开了引用来源 → 回答可信
重问率 同一 session 内换个说法再问 → 上次没答好
会话轮次 异常增多 → 反复澄清,体验差
复制率 用户复制了答案 → 大概率有用
人工转接率 客服场景的黄金指标
─────────────────────────────────────────
自动信号 无引用回答占比 答案里没有任何 citation → 高幻觉风险
检索置信度低 top1 相似度 < 阈值 → 该走兜底话术
答案长度异常 过短(可能拒答)/过长(可能跑偏)
6.2 一个实用的线上守卫
def rag_guardrail(query, contexts, scores, answer) -> dict:
"""线上实时护栏:低置信直接兜底,不要硬答。"""
flags = []
if not contexts or max(scores, default=0) < 0.35:
flags.append("low_retrieval_confidence")
if "[" not in answer and "引用" not in answer:
flags.append("no_citation")
if len(answer) < 20:
flags.append("too_short")
action = "pass"
if "low_retrieval_confidence" in flags:
action = "fallback" # 回复"未找到相关资料"并引导转人工
elif "no_citation" in flags:
action = "regenerate" # 强制要求带引用重新生成一次
return {"flags": flags, "action": action}
这个思路的价值在于:RAG 系统最该避免的不是"答不出",而是"自信地答错"。宁可承认查不到,也不要编。这一点在金融、医疗、法务场景是硬要求。
七、面试速答
Q:你怎么评估一个 RAG 系统?
A:分检索侧、生成侧、端到端三层,核心目的不是拿一个分数而是能归因。检索侧用 Recall@k、NDCG@k、MRR,其中 Recall 最关键因为它决定系统上限——没召回到的内容生成阶段变不出来。生成侧用 Faithfulness(是否忠于材料)、Answer Relevance(是否切题)、Context Precision(材料噪声度)。端到端再看正确率和业务指标。最后把每条样本打上归因标签,输出一张"错误来源分布表",比如 24% 检索未召回、9% 生成幻觉,这样优化方向就明确了。
Q:Faithfulness 怎么算?
A:论断分解法。先用 LLM 把答案拆成若干原子论断,每条只含一个可独立验证的事实,代词要还原;然后对每条论断做 NLI 判断,看能否由检索材料推出;最后用"被支持的论断数除以总论断数"作为分数。工程上有两个要点:一是必须区分"与材料矛盾"和"材料未提及",前者是严重错误后者可能只是补充常识,混在一起会让分数虚低;二是每个答案要调用 1+N 次 LLM,成本高,实践中做 200 到 500 条采样评估而非全量。
Q:只看端到端准确率有什么问题?
A:RAG 是五级级联流水线——查询改写、检索、重排、上下文组装、生成,任何一环出错都表现为最终答案不对。只看端到端等于把五个环节的问题混成一锅粥。比如准确率 60%,剩下 40% 的错误里,"没检索到"要改 embedding 和分块策略,"检索到了但编造"要改 prompt 或换生成模型,两者的修复动作完全相反。没有分段归因,调优就变成盲目试参数。
Q:没有标注数据怎么评估检索?
A:用反向生成法,让 LLM 读每个 chunk 生成只能依据该片段回答的问题,天然形成 query 到正例 chunk 的映射。但必须清楚三个缺陷:生成的问题和原文用词高度重合,会高估向量检索效果,缓解办法是加一轮同义改写;只标了单一正例,实际可能有多个 chunk 都能回答,会低估 precision;分布和真实用户不同,用户会打错字、问得模糊、跨文档提问。所以这套集合适合做 A 方案 vs B 方案的相对比较,不适合当绝对值汇报。真正可信的还是从线上日志采样人工标注几百条。
Q:RAGAS 好用吗?有什么坑?
A:能用,是快速起步的好选择,但四个坑要知道。第一,分数不可跨项目比较,它严重依赖裁判模型、prompt 版本和库版本,只能做同配置下的纵向对比。第二,成本被低估,faithfulness 一条要 1+N 次调用,1000 条样本上万次调用,必须采样和控并发。第三,默认 prompt 是英文的,中文场景论断拆解质量明显下降,必须重写中文 prompt 并人工校准。第四,指标之间会打架,增大 top-k 提升 context recall 但降低 precision,强化"必须基于材料"的约束提升 faithfulness 但可能让模型过度保守频繁拒答,所以要看指标组合而不是单点优化。
Q:检索召回率低,你会怎么优化?
A:按性价比排序。第一步加 BM25 做混合检索用 RRF 融合,半天工作量通常能提 5 到 15 个点,而且 RRF 只用排名不用归一化分数,鲁棒性很好。第二步加 reranker,延迟只增加几十毫秒,NDCG 能提 10 个点。第三步调分块策略,用父子块做 small-to-big,小块检索大块喂给模型。第四步做 query 改写或 HyDE,代价是多一次 LLM 调用。第五步领域微调 embedding,收益最大但需要标注数据。最后是知识库治理——去重、补全、更新,这一步最容易被忽视但很多时候是根因,材料里压根没有答案,改多少检索算法都没用。
Q:线上没有标注怎么监控 RAG 质量?
A:靠隐式信号加自动护栏。隐式信号包括引用点击率、重问率、会话轮次、答案复制率、人工转接率,其中重问率和转接率最灵敏。自动信号包括无引用回答占比、检索 top1 相似度低于阈值的比例、答案长度异常。更重要的是做实时护栏:检索置信度过低时不要硬答,直接走"未找到相关资料"的兜底话术并引导转人工。RAG 系统最该避免的不是答不出,而是自信地答错,这在金融医疗法务场景是硬要求。
Q:Context Precision 和 Precision@k 有什么区别?
A:Precision@k 是纯 IR 指标,只看前 k 个里有多少相关,不考虑顺序。Context Precision 引入了位置加权,相关 chunk 排得越靠前分数越高。这个设计是有依据的——大模型存在"中间迷失"现象,上下文开头和结尾的信息利用率明显高于中间,所以同样是召回到了,排在第 1 位和排在第 15 位对生成质量的实际贡献差别很大。Context Precision 把 IR 的排序质量和 LLM 的注意力分布特性结合起来了。
八、高频追问清单
- Recall 和 Precision 在 RAG 里为什么不对等重要?什么场景下 Precision 会变得更关键?
- 父子块(small-to-big)策略下,检索指标应该在哪个粒度上计算?
- 如果 faithfulness 很高但用户还是投诉答错,可能是什么原因?
- 多跳问题(需要综合两篇文档)的检索评估该怎么设计?
- RRF 里的 k=60 是怎么来的?改成 10 或 200 会有什么变化?
- 如何评估 query 改写模块本身的收益?怎么做消融?
- 知识库更新后,历史评测集还有效吗?怎么做版本管理?
- Agentic RAG(多轮检索、自主决定检索时机)的评估比单轮难在哪?
- 如果 LLM 裁判和人工标注只有 55% 一致率,这个自动指标还能用吗?
- RAG 系统的 P95 延迟从 2s 涨到 5s,但准确率提了 3 个点,你怎么决策?
- 引用(citation)的准确性怎么单独评估?引用了但内容对不上怎么检测?
- 怎么评估"该拒答时有没有拒答"这个能力?
下一篇(B20)讲智能体可观测性——本篇解决的是"RAG 好不好"的离线度量问题,下一篇解决"线上正在发生什么"的运行时可见性问题:Trace 与 Span 的建模、OpenTelemetry 的 GenAI 语义约定、token 成本归因、以及多步 Agent 的故障定位。两篇合起来,才算把"智能体的质量工程"讲完整。
更多推荐
所有评论(0)