首页
/ OpenViking RAG 基准评测框架:数据集准备、五阶段评测流水线与 LLM-as-judge 指标体系全解

OpenViking RAG 基准评测框架:数据集准备、五阶段评测流水线与 LLM-as-judge 指标体系全解

2026-09-05 09:22:20作者:秋阔奎Evelyn

OpenViking 仓库中的 benchmark/RAG 模块是一套独立、开箱即用的 RAG(检索增强生成)系统评测框架,用于在统一流水线中衡量 OpenViking 作为自进化上下文数据库在真实问答数据集上的检索与生成能力。本文完整覆盖从数据集下载采样、YAML 配置、OpenViking 接入到评测执行的每一步操作,并结合 pipeline.pyvector_store.pymetrics.py 等源码,解释摄入双模式、Recall/F1 计算细节与 LLM-as-judge 打分的底层实现。读完后你可以直接复现四个数据集的基准实验,并能按 BaseAdapter 接口扩展新数据集。

一、框架定位与目录结构

该框架的入口是 run.py,通过 config/ 下的 YAML 配置驱动,核心代码组织如下(与 README 中的结构一致):

benchmark/RAG/
├── src/                        # 源码
│   ├── pipeline.py             # 评测核心流水线(BenchmarkPipeline)
│   ├── adapters/               # 数据集适配器
│   │   ├── base.py             # 基础适配器类 BaseAdapter
│   │   ├── locomo_adapter.py   # Locomo 适配器
│   │   ├── syllabusqa_adapter.py
│   │   ├── qasper_adapter.py
│   │   └── financebench_adapter.py
│   └── core/                   # 核心组件
│       ├── logger.py           # 日志
│       ├── vector_store.py     # OpenViking 向量库封装(VikingStoreWrapper)
│       ├── llm_client.py       # LLM 客户端封装
│       ├── metrics.py          # 指标计算(F1、Recall)
│       ├── judge_util.py       # LLM-as-judge 打分工具
│       └── monitor.py          # 监控工具
├── config/                     # 配置(主配置 + 4 个数据集配置)
├── scripts/                    # 数据下载/采样/一键准备脚本
├── raw_data/                   # 原始数据集目录(下载后)
├── datasets/                   # 采样后的数据集目录
├── Output/                     # 评测结果目录
└── run.py                      # 主执行脚本

从源码结构看,BenchmarkPipeline 只依赖三个组件:数据集适配器、VikingStoreWrapper(OpenViking 向量库封装)和 LLM 客户端。run.py 负责按配置动态加载适配器、初始化向量库与 LLM 客户端,再组装出流水线对象执行。

二、快速开始

1. 安装依赖

在仓库根目录执行:

cd OpenViking
uv pip install -e ".[benchmark]"
source .venv/bin/activate

benchmark 这个可选依赖组定义在 pyproject.toml 中,包含 langchainlangchain-corelangchain-openaitiktokendatasetspandaspython-docx;而 OpenViking 的 HTTP SDK(openviking-sdk)是主依赖之一。

2. 数据集准备:下载与采样

框架提供完整的"原始数据 → 采样数据"准备工作流:

原始数据源 → Download → raw_data/{dataset_name}/ → Sample → datasets/{dataset_name}/

下载——使用 scripts/download_dataset.py

cd benchmark/RAG

# 下载全部已配置数据集
python scripts/download_dataset.py

# 下载指定数据集
python scripts/download_dataset.py --dataset Locomo

# 已存在也强制重新下载
python scripts/download_dataset.py --dataset Locomo --force

采样——使用 scripts/sample_dataset.py

# 全部数据集(默认全量、不采样)
python scripts/sample_dataset.py

# 指定数据集(全量)
python scripts/sample_dataset.py --dataset Locomo

# 按 QA 数量采样
python scripts/sample_dataset.py --dataset Locomo --sample-size 100

# 按文档数量采样(推荐)
python scripts/sample_dataset.py --dataset Locomo --num-docs 5

# 显式使用全量数据
python scripts/sample_dataset.py --dataset Locomo --full

# 指定随机种子(可复现)
python scripts/sample_dataset.py --dataset Locomo --num-docs 5 --seed 42

三种采样策略:

  1. 文档级采样(推荐)--num-docs N 先采 N 篇文档,保留文档内全部 QA;
  2. QA 级采样--sample-size N 随机选文档直至 QA 总数达到 N;
  3. 全量--full 或不加采样参数。

一键准备——scripts/prepare_dataset.py 把下载和采样串成一步:

# 全部数据集(全量)
python scripts/prepare_dataset.py

# 准备 Locomo 并采样 5 篇文档
python scripts/prepare_dataset.py --dataset Locomo --num-docs 5

# 已有数据,跳过下载只采样
python scripts/prepare_dataset.py --dataset Locomo --num-docs 5 --skip-download

# 只下载、跳过采样
python scripts/prepare_dataset.py --dataset Locomo --skip-sampling

3. 更新配置中的 dataset_path

数据准备完成后,需在各数据集配置文件的 paths.dataset_path 中指向采样产物,示例如下:

  • Locomo

    dataset_name: "Locomo"
    paths:
      dataset_path: "datasets/Locomo/locomo10.json"
    
  • SyllabusQA / Qasper(多文件数据集填目录路径,适配器会自动发现并加载目录内所有相关文件):

    dataset_name: "SyllabusQA"
    paths:
      dataset_path: "datasets/SyllabusQA"
    
  • FinanceBench

    dataset_name: "FinanceBench"
    paths:
      dataset_path: "datasets/FinanceBench/financebench_open_source.jsonl"
    

一个容易踩坑的点:dataset_path 等路径是相对 benchmark/RAG 目录解析的。run.py 中的 resolve_path 会把相对路径规范化为以 benchmark/RAG 为基准的绝对路径,且路径字符串支持 {dataset_name}{retrieval_topk} 两个占位符,会被自动替换(见下文配置详解)。

4. 配置 LLM

编辑 config/*.yaml 中的 llm 段。该配置同时用于两个环节:

  • 答案生成:基于检索到的上下文回答问题;
  • LLM-as-judge 评测:用 LLM 对生成答案与标准答案的匹配程度打分。

5. 配置 OpenViking

如需自定义 OpenViking 的数据摄入与检索行为(嵌入模型、VLM、日志级别等),可在 benchmark/RAG 目录放置 ov.conf 文件覆盖默认设置;ov.conf.example 展示了其结构(storagelogembedding.densevlm 四个主要段,例如 embedding.dense 中可配置 modelapi_keyapi_basedimensionproviderinputbatch_size 等)。配置格式可参考仓库根目录的 examples/ov.conf.example

run.py 的启动逻辑看,它会在启动时检测 benchmark/RAG/ov.conf 是否存在,存在则自动将其注入环境变量 OPENVIKING_CONFIG_FILE,供 OpenViking SDK 读取,无需手动导出。

6. 运行评测

cd benchmark/RAG

# 完整评测(数据摄入 → 答案生成 → 评测 → 数据删除)
python run.py --config config/locomo_config.yaml

# 只跑摄入 + 答案生成
python run.py --config config/locomo_config.yaml --step gen

# 只跑评测(依赖上一步生成的答案文件)
python run.py --config config/locomo_config.yaml --step eval

# 只跑数据删除
python run.py --config config/locomo_config.yaml --step del

--step 的取值在 run.py 中定义(all/gen/eval/del,默认 all),all 会依次执行 gen、eval、del 三个阶段。

三、支持的数据集

数据集 类型 文档数 QA 数 特点
Locomo 多轮对话 10 1540 长对话理解,4 类问题(事实、时间、推理、理解)
SyllabusQA 教学大纲 39 5078 教育领域,6 类问题(单事实、多事实、单推理、多推理、摘要、是否)
Qasper 学术论文 1585 5049 研究领域,1585 篇 NLP 论文,3 种答案类型(抽取式、自由式、是否)
FinanceBench 金融 84 150 金融领域,150 条开源 QA,3 类问题(领域相关、指标生成、新颖生成)

每个数据集对应 config/ 目录下的独立配置文件:

python run.py --config config/locomo_config.yaml        # Locomo
python run.py --config config/syllabusqa_config.yaml    # SyllabusQA
python run.py --config config/qasper_config.yaml        # Qasper
python run.py --config config/financebench_config.yaml  # FinanceBench

也可以复制一份配置按需修改:

cp config/locomo_config.yaml config/my_custom_config.yaml
python run.py --config config/my_custom_config.yaml

四、评测流水线:五个阶段的源码级拆解

评测过程分为 5 个阶段:数据准备 → 数据摄入 → 答案生成 → 评测 → 数据删除。下面结合 pipeline.py 说明每一阶段的实际行为。

4.1 数据准备与摄入(directory / per_file 双模式)

run_generation() 中(pipeline.py):

  1. skip_ingestion: true,直接复用已配置的 OpenViking Server 中现有索引,摄入指标记零;
  2. 否则调用 adapter.data_prepare(doc_dir) 把原始数据集转换为 OpenViking 可消费的文档(Markdown 等),再由 VikingStoreWrapper.ingest() 写入。

VikingStoreWrapper.ingest 内部通过 OpenViking HTTP SDK 的 SyncHTTPClient.add_resource(path=..., wait=True, options={"telemetry": True}) 完成同步摄入,并从返回的 telemetry.summary.tokens 中提取 LLM 输入/输出 token 与 embedding token 总量,这就是 benchmark_metrics_report.json 中 "Insertion Efficiency" 各字段的来源。两种摄入模式的差异在源码中一目了然:

  • directory:取所有文档路径的公共祖先目录,一次性 add_resource 整个目录,即"整个目录作为一篇文档"处理,摄入调用最少;
  • per_file:逐文件调用 add_resource,每个文件视为独立文档。

4.2 答案生成:检索、上下文构建与并发

每个 QA 任务在 BenchmarkPipeline._process_generation_task 中执行,关键细节:

  • 检索指令增强:若配置了 retrieval_instruction,最终查询为 f"{retrieval_instruction} {qa.question}";否则直接用原始问题;
  • 检索目标VikingStoreWrapper.retrievetarget_uri="viking://resources" 调用 SDK 的 find(query, target_uri, limit=topk)
  • 上下文选择策略:从源码看,返回结果中 level == 2 的条目会 read_resource(uri) 读取完整正文,其余条目仅拼接 abstract + overview 作为摘要上下文——即"命中高层条目时读原文,命中概览层时读摘要",且每个上下文块截断至 8000 字符再进入 Prompt;
  • 召回率即算:生成阶段就同步用 MetricsCalculator.check_recall(retrieved_texts, qa.evidence) 计算检索召回;
  • Token 统计:输入 token 计为"完整 Prompt + 问题"的 tiktoken(cl100k_base)编码长度,输出 token 为最终答案长度;
  • 并发max_workers 控制 ThreadPoolExecutor 线程池大小,生成阶段任一任务失败会汇总后抛出 RuntimeError,保证结果完整性可判读。

4.3 评测:F1、Recall 与 LLM-as-judge

_process_evaluation_taskpipeline.py)对每条生成结果计算 F1 与 Accuracy:

  • F1:对每个 gold answer 分别计算并取最大值,以兼容 Qasper 这类多标注者数据集;
  • Accuracy:把全部 gold answers 一次性交给 LLM judge 综合打分。

指标计算细节metrics.py):

  • F1 前先做标准化:去标点、小写化、去冠词(a/an/the/and)、压缩空白,再做词级 token 计数的 precision/recall/F1;
  • Recall 采用"严格子串匹配 + 动态 token 软匹配"双层策略(check_recall,默认 soft_threshold=0.8min_soft_match_tokens=4):evidence 作为完整子串出现在拼接后的检索文本中直接命中;否则对 token 数 ≥ 4 的长 evidence 计算 token 覆盖率,覆盖率 ≥ 0.8 记为命中,短 evidence(如 ID、实体)则要求严格匹配,避免软匹配误判。

LLM-as-judgejudge_util.py)按数据集路由到两套 Prompt:

  • Locomo 使用 Locomo_0or4 模板,只允许打 0 或 4 分(命中即 4,其余钳制为 0),并要求宽容判定("只要触及 gold answer 同一主题即算正确"、时间题允许相对时间表述);
  • 其他数据集使用 Generic_0-4 模板,按 0–4 五级 rubric 打分(4=完全正确、3=基本正确、2=部分正确、1=较差、0=错误/幻觉),多 gold answer 以 | 分隔,命中其一即可。

输出解析带降级保护:优先 json.loads,失败则用正则提取 "score": N,再退化为任意独立整数 0–4,仍失败记 0 分。另外框架内置拒答一致性判定:若生成答案与 gold answer 同时命中拒答关键词("not mentioned"、"no information" 等,见 check_refusal),直接判 F1=1.0、Accuracy=4,prompt 类型标记为 Heuristic_Refusal_Check

4.4 数据删除

run_deletion() 调用 VikingStoreWrapper.clear(),即 client.rm("viking://resources", recursive=True),把本次实验写入 OpenViking 的资源整体清理,并把删除耗时写入报告的 "Deletion Efficiency" 段。注意:OpenViking 的存储与向量索引归属由被配置的 OpenViking Server 持有,benchmark 进程本身不管理存储位置。

五、配置参数详解

config/config.yaml 是主配置模板,各段关键参数如下:

基础配置

参数 说明
dataset_name 当前评测的数据集名,会参与路径占位符替换

适配器配置

参数 说明
adapter.module 适配器的 Python 模块路径,如 src.adapters.locomo_adapter
adapter.class_name 适配器类名,如 LocomoAdapter

执行配置execution

参数 说明 模板默认值
max_workers QA 任务并发线程数 10
ingest_workers 文档摄入并发线程数 10
retrieval_topk 检索返回条数 top-k 5
max_queries 限制处理的查询条数,null 表示全部 null
skip_ingestion 是否跳过摄入、复用已有索引 false
ingest_mode 摄入模式:directoryper_file per_file
retrieval_instruction 检索指令前缀,默认为空(直接用原始问题) ""

路径配置paths,均支持 {dataset_name}{retrieval_topk} 占位符)

参数 说明
dataset_path 数据集文件或目录路径
doc_output_dir 预处理文档输出目录
output_dir 评测结果目录
log_file 日志文件路径

LLM 配置llm

参数 说明
llm.model 模型名
llm.temperature 生成温度,0 为确定性输出
llm.base_url API 基地址
llm.api_key API 密钥(注意保密,勿提交到版本库;run.py 也支持 api_key_env_var 指定环境变量名)

locomo_config.yaml 为例,输出目录采用 Output/{dataset_name}/experiment_test_top_{retrieval_topk} 命名,retrieval_topk 变化会自动生成不同的实验目录,便于对比实验。

六、输出文件与结果解读

评测结果保存在 Output/{dataset_name}/experiment_{experiment_name}/ 下:

Output/
└── {dataset_name}/
    └── experiment_{experiment_name}/
        ├── generated_answers.json        # LLM 生成的答案(含检索上下文与耗时)
        ├── qa_eval_detailed_results.json # 逐题评测明细(含 judge 推理)
        ├── benchmark_metrics_report.json # 聚合指标报告
        ├── docs/                         # 预处理文档(skip_ingestion=false 时)
        └── benchmark.log                 # 运行日志

1. benchmark_metrics_report.json(汇总报告),由 _update_report 分阶段增量合并写入,示例:

{
    "Insertion Efficiency (Total Dataset)": {
        "Total Insertion Time (s)": 131.98,
        "Total Input Tokens": 142849,
        "Total Output Tokens": 52077,
        "Total Embedding Tokens": 95626
    },
    "Query Efficiency (Average Per Query)": {
        "Average Retrieval Time (s)": 0.17,
        "Average Input Tokens": 3364.46,
        "Average Output Tokens": 15.5
    },
    "Dataset": "Locomo",
    "Total Queries Evaluated": 100,
    "Performance Metrics": {
        "Average F1 Score": 0.318,
        "Average Recall": 0.724,
        "Average Accuracy (Hit 0-4)": 2.36,
        "Average Accuracy (normalization)": 0.59
    }
}

字段含义:Insertion Efficiency 为整库摄入耗时与 token 消耗;Query Efficiency 为每查询平均检索耗时与 token 数;Performance Metrics 为核心分数(Accuracy 为 0–4 分制,normalization 是除以 4 后的归一化值,见 pipeline.py)。

2. generated_answers.json(生成答案明细),单条示例:

{
  "_global_index": 0,
  "sample_id": "conv-26",
  "question": "Would Caroline pursue writing as a career option?",
  "gold_answers": ["Likely no; though she likes reading, she wants to be a counselor"],
  "category": "3",
  "evidence": ["D7:5", "D7:9"],
  "retrieval": {
    "latency_sec": 0.288,
    "uris": ["viking://resources/...", "viking://resources/..."]
  },
  "llm": { "final_answer": "Not mentioned" },
  "metrics": { "Recall": 1.0 },
  "token_usage": {
    "total_input_tokens": 2643,
    "llm_output_tokens": 2
  }
}

关键字段:_global_index 为查询唯一标识;retrieval.uris 是被检回的文档 URI;metrics.Recall 为 0–1 的检索召回分;token_usage 记录该题 token 消耗。

3. qa_eval_detailed_results.json(评测明细),含 judge 推理过程,单条示例:

{
  "_global_index": 18,
  "question": "When did Melanie sign up for a pottery class?",
  "gold_answers": ["2 July 2023"],
  "llm": { "final_answer": "2 July 2023 (mentioned in the conversation on 3 July 2023)" },
  "metrics": { "Recall": 1.0, "F1": 0.375, "Accuracy": 4 },
  "llm_evaluation": {
    "prompt_used": "Locomo_0or4",
    "reasoning": "The generated answer explicitly includes the exact date 2 July 2023 that matches the gold answer...",
    "normalized_score": 4
  }
}

metrics.F1 为答案 F1(0–1),metrics.Accuracy 为 LLM judge 分(0–4,4 为满分),llm_evaluation.reasoning 是判分理由,prompt_used 表明命中的评测模板类型。

4. benchmark.log:带时间戳的详细执行日志(含每题的检索 URI、答案与 judge 推理,见 pipeline.py 打印的逐题块)。5. docs/:预处理后的 Markdown 文档。

七、基准结果参考与实验复现

以下为 top-5 检索下的官方基准结果(top-5 检索,供参考对比):

数据集 评测查询数 平均 F1 平均 Recall 平均 Accuracy (0–4) 归一化 Accuracy
FinanceBench 12 0.224 0.694 2.5 0.625
Locomo 80 0.254 0.592 2.4 0.600
Qasper 60 0.293 0.614 2.12 0.529
SyllabusQA 90 0.344 0.675 2.54 0.636

测试配置:LLM 模型 doubao-seed-2-0-pro-260215;Temperature 0(确定性);Retrieval Top-K 5;Max Workers 8;Ingest Workers 8;Ingest Mode directory;Retrieval Instruction 为空;指标为 Recall、F1、Accuracy(0–4)。四个数据集使用同一套 LLM 与执行配置,仅适配器与路径随数据集变化。

复现步骤

cd OpenViking/benchmark/RAG

# 1. 安装依赖
uv pip install -e ".[benchmark]"
source .venv/bin/activate

# 2. 下载全部数据集
python scripts/download_dataset.py

# 3. 一键采样(与官方基准相同参数)
python scripts/run_sampling.py

# 4. 在 config/ 各配置文件中填写 llm.api_key

# 5. 逐数据集运行评测
python run.py --config config/locomo_config.yaml
python run.py --config config/syllabusqa_config.yaml
python run.py --config config/qasper_config.yaml
python run.py --config config/financebench_config.yaml

# 6. 查看结果:Output/{dataset_name}/experiment_test_top_5/

run_sampling.py 的固定采样参数(均使用 seed=42 保证可复现):Locomo 3 文档 80 QA、SyllabusQA 7 文档 90 QA、Qasper 8 文档 60 QA、FinanceBench 3 文档 12 QA。

八、进阶配置

8.1 检索指令(retrieval_instruction)

可在配置中设置检索指令前缀,拼接在每条查询之前(见 pipeline.py):

# Instruction for retrieval, empty by default
# Recommended format: "Target_modality: xxx.\nInstruction:xxx.\nQuery:"
retrieval_instruction: "Target_modality: text.\nInstruction:Locate the part of the conversation where the speakers discuss.\nQuery:"

推荐格式三段:Target_modality: xxx.(目标模态)、Instruction: xxx.(检索指令)、Query:(真实查询起始标记)。留空时系统直接用原始问题检索。

8.2 按题型定制 Prompt

框架在适配器中维护"数据集 × 题型"两级 Prompt。以 locomo_adapter.pyCATEGORY_INSTRUCTIONS 字典为例,Locomo 四类问题各有独立指令:

  • Category 1(事实抽取):要求从对话中抽取精确事实,尽量用原文措辞,多项以逗号分隔;
  • Category 2(时间相关):关注对话中的 DATE 标签,必要时计算相对时间(如"10 years ago"),使用上下文中的精确日期;
  • Category 3(推理):只基于上下文事实推理,明确给出结论(如 "Likely yes"/"Probably no"),只输出最终结论,不解释推理依据,不虚构信息
  • Category 4(理解/含义):关注说话者的言外之意与象征含义,尽量复用上下文措辞。

其余数据集的题型划分:SyllabusQA 6 类(single factual、multi factual、single reasoning、multi reasoning、summarization、yes/no);Qasper 3 类(extractive、free_form、yes_no);FinanceBench 3 类(domain-relevant、metrics-generated、novel-generated),分别在 syllabusqa_adapter.pyqasper_adapter.pyfinancebench_adapter.py 中维护。

定制流程:打开对应适配器 → 定位 CATEGORY_INSTRUCTIONS 字典 → 修改目标题型文本 → 重跑评测。

九、扩展新数据集

base.py 定义的接口扩展即可:

  1. src/adapters/ 新建适配器类,继承 BaseAdapter
  2. config/ 新建对应配置文件,填写 adapter.module / adapter.class_name 与数据路径;
  3. 实现必需方法:
    • data_prepare(doc_dir):把原始数据集转成 OpenViking 友好格式,返回 List[StandardDoc](sample_id 与文档路径映射);
    • load_and_transform():加载并转为 List[StandardSample](含 StandardQA 的 question、gold_answers、evidence、category 等标准字段);
    • build_prompt(qa, context_blocks):基于检索上下文构造最终 Prompt,返回 (full_prompt, meta)
    • post_process_answer(qa, raw_answer, meta):对 LLM 原始输出做后处理(基类默认仅去首尾空白)。

十、与 OpenViking 的集成方式与常见问题

集成方式:通过 OpenViking Python HTTP SDK(SyncHTTPClient)完成摄入与检索;连接经 benchmark/RAG 下的 ov.conf 或 SDK 环境变量配置(run.py 会自动注入 OPENVIKING_CONFIG_FILE);SDK 动态加载 OpenViking 最新版本能力,评测对象始终是运行中的 OpenViking Server。

FAQ 要点

  • 已有索引时跳过摄入:配置 skip_ingestion: true
  • 只跑评测:先 --step gen 生成答案,再 --step eval 打分;
  • API key 报错:确认 llm.api_key 已填写且保密,勿提交到版本库;
  • 限制查询量做小样验证:设置 max_queries: 10 之类的上限;
  • directory vs per_file:前者把整个目录当作一篇文档,后者每文件一篇;
  • 自定义检索指令:按 Target_modality / Instruction / Query 三段格式设置 retrieval_instruction
  • 结果位置:由 output_dir 决定,默认 Output/{dataset_name}/experiment_{experiment_name}/

十一、适用前提与小结

使用本框架的前提:一个可访问的 OpenViking Server(通过 ov.conf 或 SDK 环境变量指向)、一个 OpenAI 兼容接口的 LLM API(同时承担答案生成与 judge 打分,token 成本随数据集规模线性增长,建议先用 max_queries 小样验证),以及 .[benchmark] 依赖组。整套框架把"摄入效率—查询效率—答案质量"三条评测线统一在一份 JSON 报告与逐题明细中,并通过适配器抽象把数据集差异隔离在 src/adapters/ 内,是评估 OpenViking 在长对话、教育、学术、金融等多类知识场景下 RAG 表现的可复现工具链。

登录后查看全文
热门项目推荐
相关项目推荐