大模型应用实战

企业知识库系列(00):在写第一行代码之前,先把评测数据集做好

横向对比六个开源 RAG 方案,如果用不同的文档、不同的问题去测,结果没有可比性。这篇文章是系列的零号篇——先把统一测试集建好,后续七篇实测都用同一套题打分。文章记录了从'直接用 BEIR'到'用 LLM 合成领域专属题目'的决策过程,以及实际生成 89 题评测集的完整代码。

·约 8 分钟阅读·AI Engineering

为什么要先建测试集

这个系列要做一件事:横向对比六个开源 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: What are the two main categories of configuration settings
   in the LightRAG Docker Deployment?
A: Server Configuration and LLM Configuration
src: [DockerDeployment.md]
 
Q: What is the condition under which query/document asymmetric
   embedding is enabled in LightRAG?
A: query/document asymmetric embedding is enabled only when
   EMBEDDING_ASYMMETRIC=true is explicitly set
src: [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,它会先对所有文档跑 HeadlinesExtractorSummaryExtractor 等 transforms,然后才生成问题。在国内网络环境下,这个过程调用 LLM API 时频繁超时,30 个文档跑了 22 分钟后卡死。

最终放弃 ragas TestsetGenerator,改用直接 LLM 调用。ragas 保留用于后续的评测阶段(计算 Faithfulness / Answer Relevancy 等指标),不用于生成阶段。

问题二:部分文档组合 JSON 解析失败

GLM-4-flash 在某些文档上返回的 JSON 格式不完整(多了解释文字或缺少括号),json.loads 失败,这些题被过滤掉。

受影响的文档:MultiSiteDeployment.mdParserDebugCLI.mdReproduce.mdgraphrag_get_started.mdgraphrag_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 main categories of configuration...",
  "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_base
pip install openai python-dotenv

下一篇,正式开始第一个方案的实测:LightRAG vs QAnything——经典向量 RAG 横评。同一套 89 题,看两个方案在单跳、多跳、边界三个维度的真实表现。


如果这个系列对你有帮助,欢迎关注我的个人主页 dongqi.dev,持续更新 LLM 工程实践内容。

在 PrimeSkills,我们帮助工程团队设计企业知识库的评测体系,包括领域专属测试集构建和持续评测流水线搭建。如果你的团队正在选型 RAG 方案,欢迎联系我们。

primeskills.dev