企业知识库系列(00):在写第一行代码之前,先把评测数据集做好
为什么要先建测试集
这个系列要做一件事:横向对比六个开源 RAG 方案(LightRAG、GraphRAG、HippoRAG、HyperGraphRAG、RAG-Anything、gbrain),给出选型建议。

但"横向对比"有一个前提:同一套问题,同一套评分标准。如果每篇文章都用自己的文档、自己出的题,结果没有可比性——你测 LightRAG 用的是 API 文档,测 GraphRAG 用的是论文,这不叫对比,叫各显神通。
所以在跑第一个方案之前,先把测试集建好。这篇文章记录这件事的完整过程。
为什么不直接用 BEIR
第一个直觉是去找现成的标准数据集。RAG 评测领域最常引用的是 BEIR——一个包含 18 个子集的检索基准,覆盖问答、事实核查、医疗文献等。
但 BEIR 有一个根本性问题:它是学术检索任务,和企业知识库的实际使用场景有明显的分布差距。
| 维度 | BEIR | 企业知识库 |
|---|---|---|
| 文档类型 | 学术论文、维基百科 | API 文档、操作手册、技术规范 |
| 问题类型 | 事实核查、论文检索 | "怎么配置"、"为什么报错"、"哪个方案更合适" |
| 拒答能力 | 无(假设答案一定存在) | 必须:文档没有的内容不能瞎编 |
| 语言 | 全英文 | 中英混合 |
用 BEIR 测企业 RAG,就像用高考题去测岗位面试能力——题型对不上,分数没有参考价值。
正确的做法:用与目标场景同分布的文档,合成专属测试集。
文档来源的选择
测试集要能代表"企业技术文档"这个类别。选了两套开源项目的官方文档:
LightRAG 官方文档(13 个 Markdown):
- API 服务配置、Docker 部署、多站点部署
- 文件处理流程、分块策略、解析器开发
- Milvus 配置、离线部署、角色 LLM 配置
graphrag 官方文档(17 个 Markdown):
- 架构设计、默认数据流、输入输出格式
- 索引配置、查询方式、提示词调优
- CLI 使用、可视化工具
这两套文档的特点恰好是企业技术文档的典型形态:有操作步骤、有配置示例、有跨文档的依赖关系。而且 LightRAG 和 graphrag 本身就是后续要测的方案,用它们的文档做测试集,既自洽又有实战代表性。
共 30 个有效文档(过滤了内容少于 200 字符的占位文件)。
三类问题的设计逻辑
测试集需要覆盖三种典型的失败模式:
类型一:单跳事实查询(50%,50题)
答案直接在某一个文档里,不需要跨文档推理。
测的是:检索的基础召回能力——给定问题,能不能找到包含答案的文档片段?
真实样例:
Q: In the LightRAG Docker Deployment, configuration settings are mainly split into which two categories?A: They fall into two core groups: Server Configuration and LLM Configurationsrc: [DockerDeployment.md]Q: Under what condition does LightRAG turn on query/document asymmetric embedding?A: This feature becomes active only when EMBEDDING_ASYMMETRIC=true is explicitly setsrc: [AsymmetricEmbedding.md]
类型二:多跳推理(30%,20题)
这类题的答案,通常要把多个文档里的信息拼接起来才能得出。常见场景包括:跨文档对比、系统集成层面的理解,以及端到端流程的完整追踪。
测的是:图 RAG 和混合检索相对于纯向量检索的增量价值——单文档找到了,但没有拼出完整答案。
真实样例:
Q: How does the configuration of Milvus index parameters through vector_db_storage_cls_kwargs facilitate a multi-site deployment of LightRAG?A: The configuration allows dynamic configuration for different LightRAG instances in a multi-site setup...src: [MilvusConfigurationGuide.md, MultiSiteDeployment.md]Q: Compare the authentication flow in LightRAG API Server with the role-specific LLM configuration approach...src: [LightRAG-API-Server.md, RoleSpecificLLMConfiguration.md]
类型三:边界拒答(20%,19题)
问题与文档主题相关,但答案在文档中不存在。这是企业知识库最容易出问题的地方——很多方案会"幻觉"出一个听起来合理但完全错误的答案。
测的是:方案的拒答能力。正确答案是"文档中没有此信息",而不是编造一个答案。
真实样例:
Q: What is the cost of a premium support plan for RAG system deployments?A: The provided documents do not contain information about this topic.Q: How does the RAG system handle data privacy for users in the EU under GDPR regulations?A: The provided documents do not contain information about this topic.Q: What are the security protocols implemented in the DockerDeployment?A: The provided documents do not contain information about this topic.
用 LLM 合成题目
手工出 89 道题不现实,让 LLM 来做这件事。
核心策略:给 LLM 原始文档,让它按类型生成问题和参考答案,并在 Prompt 里约束输出格式。
单跳题 Prompt 结构:
你是技术文档专家,正在为 RAG 系统构建评测集。给定以下技术文档,生成 {n} 道单跳事实查询题。要求:- 问题必须能从本文档单独回答- 覆盖关键概念、配置项、操作步骤- 输出严格 JSON 格式文档标题:{title}文档内容:{content}输出格式:[{"question": "...", "ground_truth": "...", "question_type": "single_hop"}]
多跳题 Prompt 结构:
给定多个技术文档,生成需要跨文档推理的问题。要求:问题必须用到至少 2 个文档的信息才能完整回答。文档 1:{title_1}n{content_1}文档 2:{title_2}n{content_2}...
边界题 Prompt 结构:
给定以下文档覆盖的主题,生成该领域中文档 没有 覆盖的问题。这类问题测试系统的拒答能力。已覆盖主题:{covered_topics}
生成过程中遇到的问题
问题一:ragas TestsetGenerator 在 transforms 阶段超时
最初尝试用 ragas 官方的 TestsetGenerator,它会先对所有文档跑 HeadlinesExtractor、SummaryExtractor 等 transforms,然后才生成问题。在国内网络环境下,这个过程调用 LLM API 时频繁超时,30 个文档跑了 22 分钟后卡死。
最终放弃 ragas TestsetGenerator,改用直接 LLM 调用。ragas 保留用于后续的评测阶段(计算 Faithfulness / Answer Relevancy 等指标),不用于生成阶段。
问题二:部分文档组合 JSON 解析失败
GLM-4-flash 在某些文档上返回的 JSON 格式不完整(多了解释文字或缺少括号),json.loads 失败,这些题被过滤掉。
受影响的文档:MultiSiteDeployment.md、ParserDebugCLI.md、Reproduce.md、graphrag_get_started.md、graphrag_manual_prompt_tuning.md——这几个文档本身内容以配置示例和命令行操作为主,LLM 倾向于输出代码块而不是 JSON。
实际生成结果:89 题(目标 100 题,差距约 10% 属于可接受范围)。
最终数据集结构
kb-00-testset/├── generate_testset.py# 生成脚本├── .env.example # 环境变量模板├── data/│ ├── raw_docs/# 30 个源文档│ │ ├── AsymmetricEmbedding.md│ │ ├── DockerDeployment.md│ │ ├── ... (LightRAG docs)│ │ ├── graphrag_architecture.md│ │ └── ... (graphrag docs)│ └── output/│ ├── testset.jsonl# 89 题评测集│ └── testset_stats.json # 统计摘要
每条记录格式:
{"question": "What are the two primary configuration categories...","ground_truth": "Server Configuration and LLM Configuration","source_docs": ["DockerDeployment.md"],"question_type": "single_hop"}
数据集统计如下:
{"total": 89,"single_hop": 50,"multi_hop": 20,"boundary": 19,"source_docs_count": 30,"llm_model": "glm-4-flash"}
这个测试集能测什么
后续每篇方案实测文章(LightRAG、GraphRAG、HippoRAG 等),都会用这 89 题打分,统一报告:
| 指标 | 含义 | 工具 |
|---|---|---|
| Context Recall | 检索的相关文档是否都被召回 | RAGAS |
| Context Precision | 召回的文档中有多少是真正相关的 | RAGAS |
| Answer Faithfulness | 生成答案是否忠实于检索到的内容 | RAGAS |
| Answer Relevancy | 答案是否回答了问题 | RAGAS |
| 边界拒答率 | 19道边界题中,正确拒答的比例 | 自定义 |
| P90 检索延迟 | 第90百分位的检索响应时间 | 计时 |
前四个指标来自 RAGAS 框架,第五个是本系列的自定义指标(测的是各方案在"不知道"时有没有诚实地说"不知道"),第六个用于评估生产可用性。
这六个维度组合在一起,才能回答一个完整的选型问题:这个方案在我的场景下够不够用?
运行方式
cd llm-in-action# 复制环境变量cp kb-00-testset/.env.example kb-00-testset/.env# 填入 LLM_API_KEY 等配置# 生成测试集(从任意目录运行)python kb-00-testset/generate_testset.py# 自定义题目数量python kb-00-testset/generate_testset.py --size 50
依赖安装:
conda activate dev_basepip install openai python-dotenv
下一篇,正式开始第一个方案的实测:LightRAG vs QAnything——经典向量 RAG 横评。同一套 89 题,看两个方案在单跳、多跳、边界三个维度的真实表现。
欢迎访问 PrimeSkills —— 一个精心策划的 AI Agent 与技能市场,所有内容均经过真实企业级工作流验证。没有噱头,只有真正有效的东西。
更多实用知识和有趣产品,欢迎访问我的个人主页