RAG系统评估:检索质量与生成质量的联合评测方法
RAG系统评估:检索质量与生成质量的联合评测方法

一、RAG评估的盲区:检索好≠生成好,生成好≠整体好
RAG(检索增强生成)系统的评估面临一个独特的挑战:系统由检索和生成两个子模块串联组成,单独评估任何一个都无法反映整体质量。检索模块召回了相关文档,但生成模块可能忽略关键信息或产生幻觉;生成模块产出了流畅文本,但可能完全基于模型内部知识而非检索到的文档。
一个典型的评估误区:只评估检索的 Recall@K,发现召回率 90% 就认为系统没问题。但实际用户反馈却很差——因为生成模型虽然看到了正确的文档,却在回答中混入了与文档矛盾的信息。反过来,如果只评估生成文本的流畅度和相关性,可能忽略了检索模块引入的噪声文档对生成质量的负面影响。
RAG 系统的评估必须是联合的:检索质量影响生成质量,生成质量又反过来反映检索的有效性。需要一套端到端的评测框架,同时量化检索精度、生成忠实度和整体答案质量。
二、RAG 联合评测框架设计
flowchart TB
subgraph 评测输入["评测输入"]
I1[问题集<br/>Questions]
I2[参考答案<br/>Ground Truth]
I3[知识库<br/>Corpus]
end
subgraph 检索评估["检索质量评估"]
R1[召回率<br/>Recall@K]
R2[精确率<br/>Precision@K]
R3[MRR<br/>Mean Reciprocal Rank]
R4[上下文相关性<br/>Context Relevance]
end
subgraph 生成评估["生成质量评估"]
G1[忠实度<br/>Faithfulness]
G2[答案相关性<br/>Answer Relevancy]
G3[答案正确性<br/>Answer Correctness]
end
subgraph 联合评估["联合评估指标"]
J1[端到端准确率<br/>E2E Accuracy]
J2[幻觉率<br/>Hallucination Rate]
J3[信息利用率<br/>Context Utilization]
J4[检索-生成一致性<br/>Retrieval-Generation Consistency]
end
I1 --> R1
I3 --> R1
R1 --> G1
R4 --> G1
I2 --> G3
G1 --> J2
G2 --> J4
G3 --> J1
R4 --> J3
关键指标解析:
-
上下文相关性(Context Relevance):检索到的文档与问题的相关程度。高相关性意味着检索模块没有引入噪声。
-
忠实度(Faithfulness):生成答案是否忠实于检索到的文档,而非模型内部知识。这是 RAG 系统最核心的指标——如果答案不忠实于文档,RAG 就失去了意义。
-
答案相关性(Answer Relevancy):生成答案与问题的相关程度。一个忠实于文档但答非所问的答案同样没有价值。
-
信息利用率(Context Utilization):检索到的文档中有多少信息被生成答案实际利用。低利用率意味着检索了过多无关文档,浪费了 Token 预算。
三、RAG 评测框架的 Python 实现
3.1 忠实度评估
import json
from dataclasses import dataclass
@dataclass
class RAGEvalSample:
"""RAG评测样本"""
question: str
retrieved_docs: list[str]
generated_answer: str
reference_answer: str
class FaithfulnessEvaluator:
"""
忠实度评估器
评估生成答案是否忠实于检索文档
方法:将答案拆分为声明,逐条验证是否可被文档支撑
"""
def __init__(self, llm_client):
self.llm = llm_client
def evaluate(self, sample: RAGEvalSample) -> dict:
"""
评估忠实度
返回:忠实度分数(0-1)和每条声明的验证结果
"""
# 第一步:将答案拆分为独立声明
claims = self._extract_claims(sample.generated_answer)
# 第二步:逐条验证声明是否可被文档支撑
verification_results = []
for claim in claims:
supported = self._verify_claim(claim, sample.retrieved_docs)
verification_results.append({
"claim": claim,
"supported": supported,
})
# 计算忠实度分数
supported_count = sum(
1 for r in verification_results if r["supported"])
faithfulness = supported_count / max(len(claims), 1)
return {
"faithfulness": faithfulness,
"total_claims": len(claims),
"supported_claims": supported_count,
"unsupported_claims": len(claims) - supported_count,
"details": verification_results,
}
def _extract_claims(self, answer: str) -> list[str]:
"""将答案拆分为独立声明"""
prompt = f"""
请将以下答案拆分为独立的原子声明。每个声明应是一个可验证的事实陈述。
答案:{answer}
请以JSON数组格式输出,例如:
["声明1", "声明2", "声明3"]
"""
response = self.llm.chat(prompt)
try:
return json.loads(response)
except json.JSONDecodeError:
# 降级:按句号分割
return [s.strip() for s in answer.split("。") if s.strip()]
def _verify_claim(self, claim: str, docs: list[str]) -> bool:
"""验证声明是否可被文档支撑"""
docs_text = "\n".join(f"[文档{i+1}]: {doc}"
for i, doc in enumerate(docs))
prompt = f"""
请判断以下声明是否可被给定的文档支撑。
声明:{claim}
文档:
{docs_text}
请仅回答"是"或"否"。
"""
response = self.llm.chat(prompt).strip()
return response.startswith("是")
3.2 端到端评测流水线
class RAGEvaluationPipeline:
"""RAG端到端评测流水线"""
def __init__(self, llm_client, rag_system):
self.llm = llm_client
self.rag = rag_system
self.faithfulness_eval = FaithfulnessEvaluator(llm_client)
def evaluate_dataset(
self,
dataset: list[dict],
top_k: int = 5,
) -> dict:
"""
对完整数据集执行评测
dataset中每个样本包含:question, reference_answer
"""
results = []
for item in dataset:
# 执行RAG推理
rag_output = self.rag.query(
item["question"], top_k=top_k)
sample = RAGEvalSample(
question=item["question"],
retrieved_docs=rag_output.retrieved_docs,
generated_answer=rag_output.answer,
reference_answer=item["reference_answer"],
)
# 评估各项指标
eval_result = self._evaluate_single(sample)
results.append(eval_result)
# 聚合结果
return self._aggregate(results)
def _evaluate_single(self, sample: RAGEvalSample) -> dict:
"""评估单个样本的所有指标"""
# 检索质量
context_relevance = self._compute_context_relevance(sample)
# 生成质量
faithfulness = self.faithfulness_eval.evaluate(sample)
answer_relevancy = self._compute_answer_relevancy(sample)
# 联合指标
hallucination_rate = 1.0 - faithfulness["faithfulness"]
return {
"question": sample.question,
"context_relevance": context_relevance,
"faithfulness": faithfulness["faithfulness"],
"answer_relevancy": answer_relevancy,
"hallucination_rate": hallucination_rate,
}
def _compute_context_relevance(self, sample: RAGEvalSample) -> float:
"""计算上下文相关性"""
prompt = f"""
请评估以下检索到的文档与问题的相关程度,给出0-1的分数。
问题:{sample.question}
文档:
{chr(10).join(sample.retrieved_docs[:3])}
请仅输出0到1之间的数字。
"""
response = self.llm.chat(prompt).strip()
try:
return float(response)
except ValueError:
return 0.5
def _compute_answer_relevancy(self, sample: RAGEvalSample) -> float:
"""计算答案相关性"""
prompt = f"""
请评估以下答案与问题的相关程度,给出0-1的分数。
问题:{sample.question}
答案:{sample.generated_answer}
请仅输出0到1之间的数字。
"""
response = self.llm.chat(prompt).strip()
try:
return float(response)
except ValueError:
return 0.5
def _aggregate(self, results: list[dict]) -> dict:
"""聚合评测结果"""
n = len(results)
return {
"total_samples": n,
"avg_context_relevance": sum(
r["context_relevance"] for r in results) / n,
"avg_faithfulness": sum(
r["faithfulness"] for r in results) / n,
"avg_answer_relevancy": sum(
r["answer_relevancy"] for r in results) / n,
"avg_hallucination_rate": sum(
r["hallucination_rate"] for r in results) / n,
}
四、RAG 评测的架构权衡
LLM-as-Judge 的可靠性
使用 LLM 评估 LLM 的输出存在"同源偏差"——评估 LLM 可能对同类模型的输出更宽容。缓解方案是使用与生成模型不同的 LLM 作为评估器,并定期用人工评估校准。
评测成本
每个样本的忠实度评估需要多次 LLM 调用(声明提取 + 逐条验证),100 个样本可能需要 500+ 次调用。建议先在小样本上验证评测框架的有效性,再扩展到完整数据集。
参考答案的必要性
某些指标(如答案正确性)需要参考答案,但构建高质量参考答案的成本很高。忠实度和上下文相关性不需要参考答案,更适合日常自动化评测。
适用边界:RAG 联合评测适合知识库更新频繁、需要量化检索与生成协同效果的场景。对于简单的 FAQ 系统,关键词匹配评测即可。
五、总结
RAG 系统的评估必须同时关注检索质量和生成质量,以及两者的协同效果。落地路线建议:
- 忠实度优先:先建立忠实度评估,这是 RAG 系统最核心的质量指标。
- 检索-生成联合:不要单独评估检索或生成,关注端到端的答案质量。
- 幻觉监控:将幻觉率纳入日常监控,超过阈值触发知识库或检索策略的优化。
- 人工校准:定期用人工评估校准自动化指标,确保评测结果与用户感知一致。
更多推荐
所有评论(0)