一、RAG 的三类失败与对应指标
RAG 不是一个黑盒——把它拆成检索 + 合成两段,失败可归三类:
| 失败类型 | 表现 | 对应指标 |
|---|---|---|
| 召回错 | 相关片段没找到 | hit_rate / MRR / NDCG(检索指标) |
| 合成错 | 片段在但答案乱合 | Faithfulness(忠实度) |
| 幻觉 | 编造片段里没有的内容 | Faithfulness + Groundedness |
| 答非所问 | 答对但不回应问题 | Relevancy(答案相关性) |
| 事实错 | 和标准答案不一致 | Correctness / SemanticSimilarity |
二、LlamaIndex 内置 Evaluator
from llama_index.core.evaluation import (
FaithfulnessEvaluator, # 答案是否能由 context 支持(反幻觉)
RelevancyEvaluator, # 答案是否回应了 query
CorrectnessEvaluator, # 和 reference 答案的一致性(1-5 打分)
SemanticSimilarityEvaluator, # 和 reference 的 embedding 相似度
AnswerRelevancyEvaluator, # 答案 vs query 相关性
ContextRelevancyEvaluator, # context 本身 vs query 相关性
)
from llama_index.llms.openai import OpenAI
judge = OpenAI(model="gpt-4o") # 评审用强模型,别省
faith = FaithfulnessEvaluator(llm=judge)
rel = RelevancyEvaluator(llm=judge)
response = query_engine.query("双因素认证怎么配?")
f_result = faith.evaluate_response(response=response)
r_result = rel.evaluate_response(query="双因素认证怎么配?", response=response)
print(f_result.passing, f_result.score, f_result.feedback)
关键细节:
- 评审 LLM 必须比生产 LLM 强——用 gpt-4o 评 gpt-4o-mini 才有意义。同模型自己评自己会偏高
evaluate_response会把response.source_nodes当 context——所以 response 对象里要带着召回的节点- 不同 Evaluator 的
.score量纲不同:Faithfulness 是 0/1,Correctness 是 1-5,SemanticSimilarity 是 0-1,比较前要标准化
三、RetrieverEvaluator:检索阶段单独评
召回阶段有没有问题,不用等到合成完再看——RetrieverEvaluator 直接在节点层评估:
from llama_index.core.evaluation import RetrieverEvaluator
retriever = index.as_retriever(similarity_top_k=10)
r_evaluator = RetrieverEvaluator.from_metric_names(
["hit_rate", "mrr", "ndcg", "precision", "recall"],
retriever=retriever,
)
# 单条评估
result = await r_evaluator.aevaluate(
query="怎么申请退款?",
expected_ids=["node_42", "node_58"], # 黄金集里标好的 node id
)
print(result.metric_dict)
# {"hit_rate": 1.0, "mrr": 0.5, "ndcg": 0.63, ...}
指标含义:
- hit_rate:top_k 里至少命中一个正确节点的比例——最宽松,适合做 baseline
- MRR(Mean Reciprocal Rank):第一个正确节点的倒数排名——衡量排序好坏
- NDCG:带位置衰减的打分——最接近"前排重要"的业务直觉
- precision@k / recall@k:综合看准与全
四、BatchEvalRunner:批量 + 并发
from llama_index.core.evaluation import BatchEvalRunner
runner = BatchEvalRunner(
{"faithfulness": faith, "relevancy": rel, "correctness": correct},
workers=8, # 并发评估
show_progress=True,
)
eval_results = await runner.aevaluate_queries(
query_engine=qe,
queries=golden_questions, # 黄金集 query list
reference=golden_answers, # 对应 reference 答案(Correctness 需要)
)
# 聚合统计
from collections import Counter
for metric, rs in eval_results.items():
pass_rate = sum(r.passing for r in rs) / len(rs)
avg_score = sum(r.score or 0 for r in rs) / len(rs)
print(f"{metric}: pass={pass_rate:.2%} avg={avg_score:.2f}")
跑 200 条黄金集 + 3 个指标 = 600 次 LLM 调用,用 gpt-4o 约 $3-5、5-10 分钟。每次改 retriever 参数就跑一遍,把结果存到 CSV,趋势清清楚楚。
五、黄金集(Golden Set)构建
评估的前提是有标准答案。RAG 项目的"黄金集"通常是 200-500 条高质量 Q&A 对:
方法 A:LLM 自动生成
from llama_index.core.evaluation import DatasetGenerator, QueryResponseDataset
generator = DatasetGenerator.from_documents(
documents=docs[:50], # 不用全量,采样几十篇
llm=OpenAI(model="gpt-4o"),
num_questions_per_chunk=2,
show_progress=True,
)
dataset: QueryResponseDataset = await generator.agenerate_dataset_from_nodes(num=200)
dataset.save_json("./golden_set.json")
# 加载
dataset = QueryResponseDataset.from_json("./golden_set.json")
queries = list(dataset.queries.values())
references = list(dataset.responses.values())
方法 B:从线上 log 提取
# 从 production log 提 1000 条真实 query
real_queries = load_prod_queries(days=7)
# 用强模型生成参考答案
refs = []
for q in real_queries:
r = await gpt4.aquery(q, context=get_full_context(q)) # 喂完整文档
refs.append(r.text)
# 人工审 + 修正,得到真实分布的黄金集
六、RAGAs:更系统化的 4 指标体系
RAGAs 提出的 4 指标是业界事实标准:
| 指标 | 含义 | 要不要 reference |
|---|---|---|
| faithfulness | 答案里每句话是否能从 context 推出 | 否 |
| answer_relevancy | 答案是否紧扣 query | 否 |
| context_precision | 召回的 context 里相关 chunk 的位置(前置得分高) | 是(golden answer) |
| context_recall | golden answer 里的信息在 context 里的覆盖率 | 是 |
from ragas import evaluate
from ragas.metrics import faithfulness, answer_relevancy, context_precision, context_recall
from datasets import Dataset
# 准备数据
data = {
"question": queries,
"answer": [str(qe.query(q)) for q in queries],
"contexts": [[n.text for n in qe.query(q).source_nodes] for q in queries],
"ground_truth": references,
}
ds = Dataset.from_dict(data)
result = evaluate(
ds,
metrics=[faithfulness, answer_relevancy, context_precision, context_recall],
)
print(result)
# {"faithfulness": 0.87, "answer_relevancy": 0.92, "context_precision": 0.78, "context_recall": 0.81}
把这 4 个数画成雷达图跟版本迭代一起看——换 embedding 模型后 context_recall 涨、context_precision 降,就是"召回更多但更杂"。
七、TruLens-Eval
TruLens 的卖点是 "RAG 三角":Context Relevance / Groundedness / Answer Relevance——可视化界面直观:
from trulens_eval import Tru, Feedback
from trulens_eval.feedback.provider import OpenAI as TOpenAI
from trulens_eval import TruLlama
provider = TOpenAI(model_engine="gpt-4o")
f_groundedness = Feedback(provider.groundedness_measure_with_cot_reasons).on(
...
)
# 包装 query engine,所有 query 自动记录
tru_qe = TruLlama(
qe, app_id="rag_v1",
feedbacks=[f_groundedness, f_answer_relevance, f_context_relevance],
)
with tru_qe as r:
for q in queries:
qe.query(q)
Tru().run_dashboard() # localhost:8501 看结果
八、Arize Phoenix:Trace 观测利器
评估告诉你"好不好",观测告诉你"为什么不好"——每一次 query 走了哪些步骤、各段耗时、prompt 长什么样。Phoenix 是 LlamaIndex 官方集成最深的观测平台:
pip install arize-phoenix llama-index-callbacks-arize-phoenix
import phoenix as px
from llama_index.core import set_global_handler
# 1. 启动 Phoenix UI
px.launch_app() # 打开浏览器 http://localhost:6006
# 2. 一行接入 LlamaIndex
set_global_handler("arize_phoenix")
# 3. 正常跑 RAG,每一次 query 会在 Phoenix 里自动落盘
qe.query("退款流程是什么?")
qe.query("2024 年 Q3 营收?")
# 4. 在 UI 看 trace,点开一个 query 能看到:
# - retrieve 召回了哪 5 个 chunk 分数多少
# - synthesize 传给 LLM 的完整 prompt
# - LLM 的每一 token 输出
# - 各阶段耗时饼图
Phoenix 还能直接跑 Evaluator 给 trace 打分——让"哪条答得差"跟"为什么"在一个界面里看。生产上强烈推荐。
九、OpenTelemetry / OpenLLMetry
如果你们公司有统一的 observability stack(Datadog/Grafana Tempo/Jaeger),用 OTel 接入更自然:
from openinference.instrumentation.llama_index import LlamaIndexInstrumentor
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace.export import BatchSpanProcessor
provider = TracerProvider()
provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter(endpoint="http://otel:4317")))
trace.set_tracer_provider(provider)
LlamaIndexInstrumentor().instrument()
# 之后每个 query/retrieve/llm call 都是标准 OTel span
这样延迟/错误率/成本同一套图表看得到,和 API 服务、DB 查询串在一起。
十、Langfuse / Literal AI:SaaS 产品化
想省事、要团队协作、要长期存储 trace——用 Langfuse 或 Literal AI:
from langfuse.llama_index import LlamaIndexInstrumentor
LlamaIndexInstrumentor(
public_key="pk-...", secret_key="sk-...",
host="https://cloud.langfuse.com",
).start()
# 之后所有 LlamaIndex 调用自动上报 Langfuse
# UI 里能看 trace、评估分数、用户反馈、prompt 版本对比、成本
Langfuse 额外好处:支持人工打标(标注员直接在 UI 点"答得好/不好"),这些标签一回到本地就是新黄金集。
十一、在线 A/B:让版本决定谁赢
离线指标再漂亮,上线可能翻车。稳的做法:
- 影子流量(shadow):新 pipeline 跟着老 pipeline 同时跑,不返回给用户,对齐指标
- 分流 A/B:5% 用户走 V2,和 V1 对比用户侧信号(满意度、追问率、停留时长)
- bandit 分流:根据实时 metric 动态调分流比
class ABRouter:
def __init__(self, v1, v2, v2_ratio=0.05):
self.v1, self.v2, self.ratio = v1, v2, v2_ratio
async def query(self, q, user_id):
variant = "v2" if hash(user_id) % 100 < self.ratio * 100 else "v1"
qe = self.v2 if variant == "v2" else self.v1
resp = await qe.aquery(q)
log_event(user_id, variant, q, str(resp)) # 关键:打标
return resp
配合后面的用户反馈按钮("👍/👎"),每天看两个变体的分组指标——这才是真正的 RAG 质量反馈闭环。
十二、成本与延迟监控
质量只是一面——生产上要持续看:
| 维度 | 指标 | 预警阈值示例 |
|---|---|---|
| 延迟 | p50/p95/p99 端到端 | p95 > 5s 预警 |
| 成本 | 每 query 平均 token、每天总花费 | day-over-day +30% 预警 |
| 召回 | top_k 命中率、空召回率 | 空召回 > 3% 排查 |
| 合成 | 截断率、refusal 率 | refusal > 5% 看 prompt |
| 用户反馈 | 👎 率、追问率 | 周环比下滑 |
十三、LLM-as-Judge 的偏差
用 LLM 给 LLM 打分便宜好使,但有系统偏差:
- 位置偏差:Pairwise 比较时,放前面的 A 天然比放后面的 B 赢——解决:A/B 都各放一次取均值
- 自夸偏差:GPT-4 给 GPT-4 生成的答案打分高——解决:评审用不同家族的强模型(比如用 Claude 评 GPT)
- 冗长偏差:长答案分数更高——解决:prompt 里明确说明"不要因为长度打高分"
- 格式偏差:带项目符号的答案得分更高——解决:标准化答案格式再评
十四、反模式
- 只跑 5 条 query 感觉"看起来挺好"就上线:样本太小毫无统计意义。至少 200。
- 没有 baseline:V2 Faithfulness 0.85 是好是坏?不知道。每次都和上一版/最朴素版本对比。
- 用生产 LLM 自己评自己:自夸偏差严重。评审永远用更强或不同家族的模型。
- 只看 hit_rate 不看 MRR:top_10 里命中了但排第 8——相当于给 LLM 喂噪声。
- 黄金集一次生成后再不更新:数据漂移,新知识点永远测不到。每月补 bad case。
- Phoenix/Langfuse 装了不看:观测工具的价值在 weekly review——固定时间翻 trace 才有收益。
- 在线 A/B 没有打标:两个版本流量混在一起,没法归因。variant 一定要写进每条 log。
- 忽视用户反馈:一个 👎 比十个 LLM 评估都值钱。务必在产品里埋点。
- 评估和代码解耦:改 retriever 后忘跑 eval——CI 里加一步 eval regression test。
- 只评单轮 RAG:Agent/Workflow 多步,要拆每一跳独立评。
十五、本章小结
① 三大指标缺一不可:Faithfulness(反幻觉) + Relevancy(回应问题) + Retrieval(hit_rate/MRR)。
② 黄金集 200 条起步,线上 bad case 持续补——比 LLM 生成的更贴真实分布。
③ RAGAs 4 指标(faithfulness/answer_relevancy/context_precision/context_recall)做离线评,Phoenix/Langfuse 做在线 trace,两手抓。
④ 任何新版本上线前跑 regression eval,5% 影子/分流 A/B,结合用户 👍/👎 闭环——这才是 RAG 的工程化。