RAG评估体系:为什么离线高分不等于线上好效果

RAG评估体系:为什么离线高分不等于线上好效果 RAG评估体系:为什么离线高分不等于线上好效果痛点:为什么你的RAG系统离线评估很好,用户却不满意?你见过这样的数据吗?离线评估报告: - Hit@5: 0.92 ✅ - Faithfulness: 0.88 ✅ - Answer Relevancy: 0.85 ✅ - 综合评分: 0.88 ✅ 线上用户反馈: - 用户追问率: 35% ❌ - 转人工率: 12% ❌ - 用户点踩: 18% ❌问题:离线跑得再好,用户不买单也没用。大多数团队只关注一个指标——“答案准确率”,但这远远不够。RAG系统分为两层(检索+生成),评估也要分两层。核心问题:不知道哪层出问题,优化就是瞎蒙。一、RAG评估的核心框架:两层评估 + 两套指标RAG系统的工作流程:用户提问 → 检索层(找知识) → 生成层(写答案) → 用户反馈 ↓ ↓ 检索层指标 生成层指标 ↓ ↓ Hit@K/MRR Faithfulness/Relevancy两层评估:层问题指标检索层找没找到正确的知识?Hit@K、MRR、Context Recall生成层答案是否可靠?Faithfulness、Relevancy、覆盖率线上验证:指标含义目标踩率用户对结果不满意5%追问率用户重复提问10%转人工率用户放弃系统❤️%空回答率"无法处理"频率2%会话解决率问题一次解决80%二、检索层评估:找没找到正确的知识?检索层的问题很简单:正确的知识在不在Top-K里?2.1 Hit@K:找没找到含义:正确的知识是否在Top-K检索结果中。K=5时: Hit@5 = 1 → 正确的知识在Top-5中 ✅ Hit@5 = 0 → 正确的知识不在Top-5中 ❌计算:defhit_at_k(retrieved_docs,relevant_doc_id,k=5):"""计算Hit@K"""top_k=retrieved_docs[:k]return1ifrelevant_doc_idin[doc.idfordocintop_k]else0# 批量评估hit_rate=sum(hit_at_k(docs,relevant_id)fordocs,relevant_idintest_cases)/len(test_cases)实战案例:测试集:100个问题 Hit@5 = 0.85 → 85个问题在Top-5中找到正确答案 Hit@10 = 0.92 → 92个问题在Top-10中找到正确答案 问题:5个问题在Top-5没找到,但在Top-10找到了 → 需要优化排序2.2 MRR:排第几含义:正确的知识排在第几位。排名 | 倒数 1 | 1.0 2 | 0.5 3 | 0.33 5 | 0.2 10 | 0.1计算:defreciprocal_rank(rank):"""计算倒数排名"""return1.0/rankifrank0else0defmrr(retrieved_docs,relevant_doc_id):"""计算MRR"""fori,docinenumerate(retrieved_docs,1):ifdoc.id==relevant_doc_id:returnreciprocal_rank(i)return0# 批量评估mrr_score=sum(mrr(docs,relevant_id)fordocs,relevant_idintest_cases)/len(test_cases)为什么Hit@K和MRR要配合使用?场景Hit@5MRR诊断找到了,排第111.0完美 ✅找到了,排第510.2需要优化排序 ⚠️没找到00需要优化检索 ❌实战案例:场景1: Hit@5=0.9, MRR=0.7 → 大部分找到了,但排名不够靠前 → 优化排序算法,把相关度高的排前面 场景2: Hit@5=0.6, MRR=0.3 → 很多没找到,找到了的排名也靠后 → 优化检索策略,增加召回2.3 Context Recall:覆盖率含义:检索到的知识是否覆盖了所需信息。问题需要3个知识点:A、B、C 检索返回:A、D、E Context Recall = 1/3 = 0.33 ← 只覆盖了1/3计算:defcontext_recall(retrieved_docs,relevant_docs):"""计算上下文召回率"""retrieved_ids={doc.idfordocinretrieved_docs}relevant_ids={doc.idfordocinrelevant_docs}returnlen(retrieved_idsrelevant_ids)/len(relevant_ids)实战案例:问题:如何修复Python列表的浅拷贝问题? 需要的知识点: 1. 浅拷贝 vs 深拷贝的区别 2. copy模块的使用 3. deepcopy函数的参数 检索返回: - 浅拷贝 vs 深拷贝的区别 ✅ - copy模块的使用 ✅ - 列表的其他方法 ❌ Context Recall = 2/3 = 0.67 → 需要补充deepcopy相关内容2.4 Context Precision:精确率含义:检索结果中噪声的比例。检索返回10个文档: 相关:3个 噪声:7个 Context Precision = 3/10 = 0.3 ← 噪声太多计算:defcontext_precision(retrieved_docs,relevant_doc_ids):"""计算上下文精确率"""relevant_count=sum(1fordocinretrieved_docsifdoc.idinrelevant_doc_ids)returnrelevant_count/len(retrieved_docs)实战案例:检索返回10个文档,只有2个相关: - Context Precision = 0.2 → 噪声太多 - 优化:增加相关性阈值,减少返回数量三、生成层评估:答案是否可靠?生成层的问题:答案是否有依据?是否解决了问题?3.1 Faithfulness:忠实度含义:答案是否有检索到的知识支撑。检索知识: - Python的列表是可变对象 - 列表的append方法会修改原列表 答案A: "Python列表是可变对象,append会修改原列表" ✅ 忠实 答案B: "Python列表是不可变对象" ❌ 不忠实(与检索知识矛盾)评估方法:LLM-as-a-Judgedefevaluate_faithfulness