为什么要先建测试集
这个系列要做一件事:横向对比六个开源 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,它会先对所有文档跑 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 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 方案,欢迎联系我们。