资讯动态

Easy Dataset实战:为RAG知识库构建可量化的Benchmark

发布时间:2026/9/10 18:41:37 来源:尧图企业网站定制
知识库上线之后真正让人头疼的不是“能不能回答”而是“回答得对不对、找没找准”。很多团队把几千份PDF、Word、Wiki文章扔进向量库接上大模型就算完成知识库落地可一旦业务方问出“召回准确率多少、生成准确率多少、哪些场景回答崩溃”当场就哑火了。这个痛我太熟了。这篇文章想分享的就是我用 Easy Dataset 把领域文档整理成一套可量化的知识库 Benchmark 的完整过程从文档解析、评测集生成到检索指标和生成质量的打分全部说清楚。适合同样在做 RAG 知识库、但手头一直没有靠谱评测手段的工程师、算法同学和知识库产品负责人参考——看完你至少能照着搭出一套属于自己的“知识库考卷”。1. 为什么知识库必须要有 Benchmark以及 Easy Dataset 在这里干什么1.1 没有评测的知识库就是一只黑盒先讲一个我踩过的大坑。之前做某个企业内部知识库文档整理得漂漂亮亮向量库也建好了演示 Demo 给领导看的时候问“合同审批流程是什么”这种高频问题回答得确实很顺。可一旦换成问“如果供应商发票金额和合同金额不一致系统会怎么处理”这种略带条件的改写问法系统就抓瞎了要么召回一堆无关章节要么大模型一本正经地编了一个流程。问题出在哪不是向量库有问题也不是大模型不行而是我根本不知道系统在哪个环节、哪个文档子集、哪类问法上表现差。没有评测集知识库就是一个黑盒报错能看见效果好坏全凭感觉。这种感觉在演示时还能糊弄在真实业务上线后就是事故现场。所以我在后来的项目里给自己立了一条规矩知识库在接大模型之前必须先把“考卷”出好。这份考卷就是 Benchmark——一组带有标准答案的提问集合配合一组可重复计算的评估指标。它能回答三个问题系统能不能找到对的文档找到的内容是不是够靠前大模型基于这些内容生成出的答案是不是既忠实又完整1.2 Easy Dataset 在整套链路中的定位Easy Dataset 在这套方案里承担的角色就是把“领域文档”变成“评测数据集”再对接评测脚本跑出可量化结果。一句话概括它是你从文档到分数之间的装配线。我理解它的定位有几个特点。第一它是向量库无关的。不管你的知识库后端是 ES、Milvus、pgvector还是 Dify、RAGFlow 这类平台自带的存储Easy Dataset 只负责准备和评估数据不绑架你的技术选型。第二它是文档格式相对宽松的。我拿它处理过 PDF、DOCX、Markdown、HTML 甚至直接从 Obsidian 仓库里导出的.md文件基本都能用。第三它最终的产物是一个标准化的 JSON 评测集里面包含问题、标准答案、来源文档 ID、难度标签和场景标签。这个评测集可以反复使用也可以按版本号管理起来。我自己对 Easy Dataset 的使用方式比较朴素用它的文档解析和数据生成模块做预处理然后在本地写 Python 脚本调检索接口和 LLM 做打分。下面这篇文章里的所有代码和步骤我默认你用的是这种“数据集工具自有评估脚本”的组合。如果你团队内部有类似的内部工具思路完全可以平移。1.3 评测指标不能乱选我最终留下了四个核心指标评测知识库指标多到能迷眼什么准确率、召回率、F1、BLEU、Rouge、忠实度、相关性……但实际用下来真正能指导优化的指标就那几个。我在项目里保留了四个维度组成了两个阶段第一个阶段是“检索质量评估”只管找到的候选片段有没有用模型还没参与生成。这个阶段我用命中率Hit Rate和 MRRK 两个指标。第二个阶段是“生成质量评估”把检索到的片段和问题一起交给大模型看最终答案靠不靠谱。这个阶段我用忠实度Faithfulness和相关性Relevance两个指标通常用 LLM 作为裁判来打分。这四个指标的区别我直接拿表格说指标所属阶段计算方式解决什么问题命中率 Hit RateK检索Top-K 结果中是否包含正确答案所在文档检查知识库“有没有找到对的地方”MRRK检索首个正确答案排名的倒数均值检查“对的答案是否足够靠前”忠实度 Faithfulness生成LLM 判断答案是否严格基于检索片段防止大模型编造文档里没有的信息相关性 Relevance生成LLM 判断答案是否完整回应问题防止答非所问、漏掉关键点注意Hit Rate 和 MRR 可以在不接大模型的情况下先跑这样你能先定位问题是不是在检索层而不是生成层。把“检索问题”和“生成问题”分开排查效率会高非常多。2. 领域文档接入与评测样本准备2.1 文档接入前先把家底翻一遍用 Easy Dataset 之前别急着灌数据。我建议你先做一次文档梳理因为评测数据集的采样质量直接受源文档质量影响。你拿一堆扫描版 PDF 去生成测试问题到时候解析出来全是乱码后面的工作全部浪费。我通常按三步做文档梳理列出全部要接入知识库的文档目录给每个文档分配唯一 ID。如果文档本来就在 Confluence、Notion 或 Obsidian 里面这一步可以做成脚本批量抓取。按文档类型分组。比如“规章制度”“操作手册”“FAQ”“技术方案”不同类型在后续评测数据生成时问法差异很大。剔除明显过时、被替代或完全不适用于当前业务的文档。这一步看似笨但能避免评测集里混入大量“过时答案”干扰评测结果。这里要特别说一句如果你用的是 Obsidian 搭建的知识库你导出的大量 Markdown 文件往往带着双链、标签和 Callout解析时要注意剔除这些语法否则后面分块时它们会变成碎片噪音。我当时专门写了一个小脚本把[[...]]双链都转成纯文本再把 YAML frontmatter 里的 metadata 单独读出来当文档属性保存效果很干净。2.2 分块参数不是玄学我这样确定 chunk 大小和 overlap知识库 Benchamrk 的评测集本质上是从文档分块后生成的。所以分块参数直接影响评测结果。很多朋友上来就问“chunk_size 用 512 行不行”我一般会反问你的文档结构是怎样的分块的核心原则是“保持语义完整”。如果一层一层按固定字数硬切很可能会把一段流程说明从中间切开导致上半截和下半截语义断裂。我建议按“结构优先、长度兜底”的方式分块对 Markdown/HTML 类文档优先按标题层级切一级标题下内容过多时再往下钻。对 PDF/Word 等无明确标题结构的内容按段落切段落过长时再用滑动窗口兜底。经验参数chunk_size 取 400~800overlap 取 80~150。具体数值取决于你的文档平均段落长度没有万能值。我举个例子用 Python 写一个按 Markdown 标题切块的简易逻辑import re def split_markdown_by_heading(text, max_chunk_size800, overlap100): # 先按 H2/H3 标题切段 sections re.split(r(?^#{2,3} ), text, flagsre.MULTILINE) chunks [] buffer for section in sections: # 极端情况单个 section 超长则按句号兜底切分 if len(section) max_chunk_size: if buffer: chunks.append(buffer) buffer sentences re.split(r(?[。]), section) temp for sent in sentences: if len(temp) len(sent) max_chunk_size: chunks.append(temp) temp temp[-overlap:] sent else: temp sent if temp: chunks.append(temp) else: if len(buffer) len(section) max_chunk_size: chunks.append(buffer) buffer buffer[-overlap:] section else: buffer section if buffer: chunks.append(buffer) return chunks这样处理的逻辑是标题能保住的语义尽量保只有超长时才做兜底切分窗口重叠 100 字左右保证跨切片的上下文衔接。你在自己项目里可以按实际情况把数字调一调但原则别丢。2.3 评测样本从哪里来选多少条合适评测样本不是把每个块都变成一个测试问题那样既浪费又让评测集非常臃肿单次评测成本高到跑不动。我的做法是抽样。首先按“业务场景”分层比如合同管理、报销流程、员工入职、客户售后等等每个场景保证覆盖一定数量的问题。其次按“文档类型”分层确保制度类、操作类、FAQ 类都有样本。最后按“来源文档热度”加权被用户问得最多的文档多出些题冷门文档少出些题。样本量我给一个经验值最小可用评测集 50 条认真一点做到 100~150 条体系化建设按核心场景各 50 条起步。少于 50 条的评测集指标波动太大没法稳定指导优化多于 200 条每次跑评测的时间和成本都会让你不想频繁执行。心得我的习惯是把评测集按难度再标一个级别。简单问题答案在某一个段落里直接能找到、中等问题需要把多个段落拼起来、困难问题需要跨文档推理或反向问法。没有困难问题的评测集测出来的分再高上线也容易翻车。3. 使用 Easy Dataset 构建 Golden Set 评测集3.1 三种生成评测问答对的方式一旦文档分块完成接下来就是最核心的一步生成评测问答对。我试过三种方式各有优劣。第一种是纯人工编写。质量最高、最贴近业务真实问法但成本也是最高的。适合少量高价值文档比如几份核心制度、SOP。第二种是规则抽取。从文档里把带关键词条目的内容直接抽出来比如“员工假期种类有哪些”“报销标准是什么”这类规则型问题。优点是快缺点是问法机械、覆盖不广。第三种是用 LLM 辅助生成。这是 Easy Dataset 最顺手的方式把分块后的片段喂给大模型让它生成问题-答案对。优点是能大规模批量产出且问法多样缺点是可能产出与原文不一致的“幻觉答案”必须做校验。我最终采用的组合是大面积用 LLM 辅助生成人工只负责战略级核心文档以及最终的抽样复核。比例大概在 8:2。这样既保证覆盖度也控制成本和风险。LLM 辅助生成时我的提示词大概长这样你是一名知识库内容专家请基于以下文档片段生成 3 个评测问答对。 要求 1. 问题必须能在该片段中找到明确依据不要引入片段之外的信息。 2. 三个问题的难度依次为简单、中等、困难。简单问题直接定位单一句子中等问题需要组合片段内多个信息困难问题需要考察反向推导或条件判断。 3. 答案必须完整且包含原文中对应的关键事实。如果片段中没有答案请标注 “信息不足”。 输出格式为 JSON [ {question: ..., answer: ..., difficulty: easy|medium|hard} ] 片段内容 {chunk_text}这个提示词每次生成的量不大但质量稳定也容易检查。批量处理时我会把这些生成结果合到一起再进入下一轮校验。3.2 Golden Set 的 JSON 结构Easy Dataset 生成的评测集我用的是一个带元信息的 JSON 结构。每条数据大致长这样[ { query: 供应商发票金额与合同金额不一致时系统如何处理, answer: 系统会生成差异提示并将该发票标记为待人工审核。, source_doc_id: procurement_manual_2024_0312, source_chunk_id: chunk_0421, difficulty: hard, scenario: 采购流程, doc_type: 操作手册 } ]这里有几个字段值得解释一下。source_chunk_id是检索评估的“标准答案定位锚点”跑检索指标时就看 Top-K 结果里有没有包含这个 chunk。difficulty用来分难度统计判断系统在哪个难度档位掉链子。scenario用来分析不同业务场景的差距这个字段非常有用比如我发现很多知识库系统整体分数不错拉出 scenario 维度一看才发现“员工入职”场景全挂。3.3 正负样本和难度比例要刻意设计评测集里的正负样本我理解成两类。一类是“有答案的标准问题”另一类是“故意刁难的问题”。前者比较好理解就是上面 JSON 里的正常数据。后者我建议加入一些“边界测试题”比如与知识库相关但文档里明确没提到的问题观察系统是否乱答与某文档语义高度相似但答案在另一文档的问题观察检索是否被干扰两个条件同时约束的问题观察切片是否能完整覆盖所有条件。这些边界题的用处是防止评测分数虚高。很多知识库只答简单题拿高分的案例一碰真实业务就露馅就是因为评测集里没有“坏样本”。我在实际项目中的比例是简单 40%、中等 40%、困难 15%、边界题 5%。你可以根据业务形态调整但边界题别归零。3.4 生成后的三重校验别把幻觉答案带进评测集LLM 生成问答对最大的坑就是它可能自己脑补答案。这种数据放进评测集跑出来的指标就是假分数。我每次生成完都要做三层校验第一层是“原文可溯”。逐条检查标准答案是否能在对应 chunk 里找到依据。我通常让 Easy Dataset 输出时就把 answer 中每个关键句对应的原文高亮出来没有依据的直接标记为待删。第二层是“答案唯一性”。同一问题如果出现在多个文档里答案是否冲突。冲突的要么改题要么补上“来源文档限定”的上下文。第三层是“人工抽检”。随机抽 10%~20% 的数据人工读一遍 question 和 answer 是否符合真实用户问法。这一步不能省因为 LLM 生成的问法经常会“过于书面化”。提醒很多项目死在评测集自带 bug比如答案本来就是错的系统答对了反而被扣分。这类问题很隐蔽建议每轮评测前都抽查一遍评测集尤其是当分数突然大幅波动时先怀疑评测集再怀疑系统。4. 运行 Benchmark从检索指标到生成质量打分4.1 第一步先跑检索层指标用 Top-K 判断“找没找到”拿到评测集以后我第一步不是直接接大模型而是先单独跑检索层。因为知识库的效果瓶颈七八成都在检索而不是生成。如果检索层已经把正确答案排到了第 20 位大模型再强也没有用。我写了一个轻量脚本逐条把评测集中的query发到知识库检索接口取回 Top-K 的 chunk_id 列表然后和标准答案的source_chunk_id做对比。记录三个内容是否命中、首个命中的排名、命中的 chunk 数量。def evaluate_retrieval(eval_set, retriever, k5): hit_count 0 rr_sum 0.0 for item in eval_set: query item[query] target_chunk item[source_chunk_id] retrieved retriever.retrieve(query, top_kk) # 返回chunk_id列表 # Hit Rate if target_chunk in retrieved: hit_count 1 # MRR取首个命中的排名倒数 rank retrieved.index(target_chunk) 1 rr_sum 1.0 / rank else: # 未命中的样本也可以记录到报告供人工审阅 log_miss(query, target_chunk, retrieved) hit_rate hit_count / len(eval_set) mrr rr_sum / len(eval_set) return {hit_rate: hit_rate, mrr: mrr}关于 K 值的选取我一般用 K5。K1 太严格K10 太宽松K5 是一个在真实场景里比较合理的“用户愿意翻几下”的上限。跑完这一层你就知道当前知识库的底子大概在什么位置。在我做过的项目里初始版本 Hit Rate5 经常只有 50%~60%这时候先别着急调模型优先调分块、换 embedding、加粗召回把 Hit Rate 提到 75% 以上再说。4.2 第二步用 LLM 打分评估生成质量检索指标合格后才轮到生成质量评估。做法是拿检索回来的 Top-K 片段作为上下文交给大模型生成答案再用另一个 Judge 模型给答案打分。打分维度我固定用“忠实度”和“相关性”两项避免维度太多导致评分不稳定。忠实度看答案有没有超出给定上下文胡编相关性看答案有没有完整回应问题。两个维度各 1~5 分我做了一个基础的分值解释分值忠实度 (Faithfulness)相关性 (Relevance)5答案所有信息都来自检索片段且没有改动原意答案完整覆盖问题所有要点3大部分信息有依据但有少量推测或遗漏回答相关但漏掉关键点1答案大面积脱离上下文明显编造答非所问与问题不相关Judge 的提示词我也不写得太复杂太复杂的 prompt 反而容易波动。一个稳定可用的版本参考如下你是知识库输出质量的评测员。请根据“用户问题”和“模型回答”和“参考片段”对回答打分。 评分维度 - faithfulness: 1-5越忠于参考片段分越高。 - relevance: 1-5越完整回应问题分越高。 请仔细对照参考片段如果回答出现片段中不存在的信息faithfulness 必须给 3 分以下。 输出 JSON{faithfulness: 4, relevance: 3, reason: 简要原因}这里要特别强调Judge 模型和生成模型最好别是同一个模型。如果用同一个模型既生成又给自己打分即便不能说是作弊分数也会明显偏高因为模型天然认为自己输出得合理。我实际项目中生成模型用一个Judge 模型换另一个或者至少用不同温度参数结果会客观不少。4.3 一份 Benchamrk 报告应该长什么样跑完一轮评测后我习惯把结果汇总成一张报告表方便自己分析也方便向上汇报。报告里至少包含这几列问题 ID场景难度检索是否命中命中排名忠实度相关性失败类型Q-001采购流程hard是254无Q-002采购流程hard否---检索未命中Q-003员工入职medium是132生成遗漏有了这张表问题定位就非常直接。“检索未命中”去看 embedding 和分块“生成遗漏”去看上下文截断和 prompt“忠实度低”多半是检索到了不相关内容导致大模型被带偏。另外我会按 scenario 和 difficulty 两个维度分别统计平均分。这样做的好处是当你需要和业务方对需求时可以直接告诉他们“采购场景只有 60 分但售后场景已经 85 分了下一轮优化先投入采购文档的数据清洗”有数字支撑的沟通远比“我觉得还可以再优化”有说服力。4.4 拿到报告之后怎么倒推优化方向评测报告不是跑完就完事的它的价值在于指导下一轮迭代。我总结了一套比较粗糙但实用的优化决策逻辑如果 Hit Rate 低先看未命中的样本集中在哪些文档。如果集中在格式复杂的扫描 PDF优先处理 OCR 和文本提取如果集中在文档语义相近的场景考虑换 embedding 模型或者用混合检索BM25 关键词 向量召回兜底。如果 Hit Rate 高但 MRR 低说明正确答案总是排得太后。这时候可以引入 rerank 模型或者调整向量检索的 Top-K 召回数量让 retriever 多召回一些候选再由重排模型把正确答案提到前面。如果检索指标都高但忠实度低问题大概率出在大模型的 prompt 上。可能上下文塞得太多导致模型注意力分散或者 prompt 没有强调“严格基于上下文回答”。把 prompt 改成强约束版本能立竿见影。如果相关性低而忠实度不低说明模型“答得都有依据但没答到点子上”。常见原因是检索片段缺失关键段落或者模型没有理解问题的完整意图。这时候要去检查剔除的关键片段必要时调整分块策略或者扩展检索召回范围。5. 实际跑评测时踩过的坑和排查记录5.1 高频问题速查表我把这一年多折腾知识库 Benchmark 的踩坑记录整理成了一张速查表供你遇到问题时先照着排查现象可能原因解法检索命中率极低40%文档解析质量差大量乱码或失效文本重新处理源文档优先处理 PDF 扫描件和表格命中率还行但没有一次排到 Top-1embedding 模型区分度不够换更适配领域的 embedding 模型或引入混合检索Judge 打分普遍给 5 分不够区分Judge 模型太弱或 prompt 太松换更强的 Judge 模型细化评分标准加入反面案例说明忠实度低但检索正常上下文拼接混乱模型被无关片段干扰优化 prompt 约束或限制输入片段数量系统升级后分数突然暴跌评测集里混入旧版本数据检查评测集版本一致性建立数据版本管理知识库新增文档后分数异常切片 ID 变化导致 Golden Set 锚点失效重新生成评测集或建立文档 ID 与 chunk 的映射表5.2 评测集的数据泄漏问题比你想象的更容易发生这是我在做评测时最警惕的问题。数据泄漏简单说就是评测集里的问题模型在训练或微调时可能已经见过了或者检索到的片段本身就来自评测集生成时用到的同一批文本。举一个实际例子。我最初用 LLM 生成问答对的时候把一批操作手册的全文喂给了生成模型生成出来的答案和原文几乎一字不差。后来跑评测时知识库系统检索到的片段和标准答案高度重叠忠实度和命中率高得离谱。一开始我还高兴后来冷静一想这不是系统强是因为评测集和知识库用的同一批文档而且答案是从文档里直接抽出来的检索系统稍微沾点边就能“命中”。解决思路是评测集尽量与训练知识库的文档集分离。如果你构建的是内部知识库评测集至少要做到测试文档和辅助检索知识库不是同一份或者明确评测目标是“在这批新文档中检索”而不是命中原有知识库。另外建议评测集里的 question 做一定程度的改写不要直接用文档原句减少“复制粘贴式命中”的假象。5.3 一个真实案例从 52% 到 81%到底动了哪里最后讲一个让我映像很深的真实项目。某企业内部知识库初始评测结果 Hit Rate5 只有 52%MRR 只有 0.31忠实度平均分 3.6属于典型的“帮用户找到了一点东西但完全不够用”。排查后我发现三个问题第一文档解析阶段表格全部被拆散大量含有关键数据的 Excel 转 PDF 变成了一堆分隔符第二切片时没有按文档原有结构走导致很多标准操作流程被切得七零八落第三评测集里困难问题占比较高系统对跨段落组合完全没有能力。针对这三个问题我依次做了处理先把 PDF 表格区域单独提取并转成 Markdown 表格再重新按标题层级分块最后给系统接了一个 lightweight rerank 模型。跑第二轮评测后Hit Rate5 涨到 68%MRR 到 0.44第三轮调完 embedding 和分块参数后Hit Rate 稳定在 81% 左右忠实度平均分也到了 4.4。整个过程最有价值的不是那三次优化本身而是每一次优化都能通过同一套评测集看到可量化的变化幅度。这让团队可以非常理性地判断投入某一步优化到底值多少钱而不是凭感觉反复调参。现在我的习惯是知识库每更新一个版本都先用同一套 Golden Set 跑一轮回归测试分数不达标绝不上线。这套方法从长期来看节省的时间和返工成本远超当初构建评测集花的功夫。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价