
Python后端AI专题32建立第一套 RAG 评测集问题、证据与不可回答样本没有固定评测集“换模型后感觉更聪明”无法复核只收集能回答的问题又会把胡编但流畅的模型选上去。评测数据至少要同时表达用户问题、哪些 chunk 相关、答案必须包含哪些事实、这个问题是否本应拒答。供应商升级测试门答案层用例失败说明Provider普通响应字段/usage 映射v2 HTTP 合同变化Provider空 delta token DONE流解析不兼容Gateway429 可重试、400 不重试、超时取消稳定性策略回归离线 E2E上传到引用回答应用接线回归与真实模型质量无关真实评测v1/v2 同数据集对比质量、成本或延迟变化完整流合同测试使用 MockTransport 返回三帧只得到[七天]content(data: {choices:[{delta:{}}]}\n\ndata: {choices:[{delta:{content:七天}}]}\n\ndata: [DONE]\n\n)真实结果3 passed in 0.25s。样本字段为什么这样设计dataclass(frozenTrue,slotsTrue)classEvaluationCase:id:strquestion:strrelevant_ids:list[str]expected_facts:list[str]unanswerable:boolid 稳定引用失败样本question 保留真实用户表达不只写文档标题relevant_ids 评估检索与生成质量分开expected_facts 做确定性答案基线unanswerable 检验拒答不让空 relevant 被误当标注漏失。相关性最好由至少两位标注者复核若多个 chunk 都能支持答案应全部标记否则 Recall 会错误惩罚合法召回。当前数据集不是三个占位问题现有 8 条覆盖退款期限、产品端口、健康路径、向量错误、验证码、索引状态以及董事长电话/办公室地址两个不可回答问题。示例{id:vector-error,question:出现 E_VECTOR_02 应该怎么办,relevant_ids:[product-manual],expected_facts:[重建,索引版本],unanswerable:false}{id:office-address,question:公司北京办公室地址在哪里,relevant_ids:[],expected_facts:[],unanswerable:true}生产评测还应加入口语改写、错别字、长条件、表格、跨文档冲突、权限和 Injection。数据集要从真实失败日志脱敏沉淀而不是由开发者只写系统擅长的问题。完整加载与校验模块from__future__importannotationsimportjsonfromdataclassesimportdataclassfrompathlibimportPathdataclass(frozenTrue,slotsTrue)classEvaluationCase:id:strquestion:strrelevant_ids:list[str]expected_facts:list[str]unanswerable:booldefload_cases(path:Path)-list[EvaluationCase]:cases:list[EvaluationCase][]seen:set[str]set()forline_number,lineinenumerate(path.read_text(encodingutf-8).splitlines(),start1):ifnotline.strip():continuerawjson.loads(line)caseEvaluationCase(idstr(raw[id]),questionstr(raw[question]),relevant_idslist(raw.get(relevant_ids,[])),expected_factslist(raw.get(expected_facts,[])),unanswerablebool(raw.get(unanswerable,False)),)ifcase.idinseen:raiseValueError(fduplicate case id{case.id}at line{line_number})ifnotcase.unanswerableandnotcase.relevant_ids:raiseValueError(fcase{case.id}requires relevant_ids)ifcase.unanswerableandcase.relevant_ids:raiseValueError(funanswerable case{case.id}cannot have relevant_ids)seen.add(case.id)cases.append(case)ifnotcases:raiseValueError(evaluation dataset is empty)returncasesJSONL 允许逐条追加和 diff加载器拒绝重复 id、可回答却无相关证据、不可回答却标相关证据、空文件。Schema 校验是数据质量第一关不会验证标注事实本身正确。如何避免数据泄漏调参时反复看同一评测集会过拟合。应分 dev/testdev 可用于选 chunk size/Prompttest 只在候选版本确定后运行最终还有线上灰度。真实客户数据需脱敏、获得授权并控制访问不能为了评测把私人问题提交到代码仓库。Fake 评测的 1.0 代表什么当前脚本的 Fake evaluator 根据 expected_facts 直接构造答案因此 1.0 只证明 Runner、数据读取和报表计算接线正确Recall51.0, MRR1.0, nDCG51.0, citation_precision1.0, answer_accuracy1.0它绝不是 KnowFlow 真实语义质量。真实 Provider 必须用同一 EvaluationFunction 接口跑并单独记录模型、Prompt、索引哈希。本篇最终完整模块dataset.py前面的代码片段用于解释本次改动下面是本篇结束时可直接核对和替换的磁盘完整版本。from__future__importannotationsimportjsonfromdataclassesimportdataclassfrompathlibimportPathdataclass(frozenTrue,slotsTrue)classEvaluationCase:id:strquestion:strrelevant_ids:list[str]expected_facts:list[str]unanswerable:booldefload_cases(path:Path)-list[EvaluationCase]:cases:list[EvaluationCase][]seen:set[str]set()forline_number,lineinenumerate(path.read_text(encodingutf-8).splitlines(),start1):ifnotline.strip():continuerawjson.loads(line)caseEvaluationCase(idstr(raw[id]),questionstr(raw[question]),relevant_idslist(raw.get(relevant_ids,[])),expected_factslist(raw.get(expected_facts,[])),unanswerablebool(raw.get(unanswerable,False)),)ifcase.idinseen:raiseValueError(fduplicate case id{case.id}at line{line_number})ifnotcase.unanswerableandnotcase.relevant_ids:raiseValueError(fcase{case.id}requires relevant_ids)ifcase.unanswerableandcase.relevant_ids:raiseValueError(funanswerable case{case.id}cannot have relevant_ids)seen.add(case.id)cases.append(case)ifnotcases:raiseValueError(evaluation dataset is empty)returncases本篇练习检查一组坏标注为 loader 写四个测试重复 idanswerable 无 relevantunanswerable 却有 relevant空文件。再判断以下样本应怎样改问题“退款多久到账”expected_facts 写“七天”相关文档其实只说“七天内申请”。说明为什么这是答案标注错误而不是模型错误。下一篇给出测试答案并手算 RecallK、MRR、nDCG 与引用精度解释为什么不能只看一个总分。