Ch20: 中文 RAG Benchmark 动手——RAGAS 与 WeKnora 评估闭环
Part 7: 评估实战 前置:Ch08 评估方法论、Ch14 动手实验室 后续:Ch11–Ch15 进阶专题 或 WeKnora 实战 完整方法论:RAG 评估方法论
结论先行:WeKnora 无官方 eval 工具——这正是高价值竞赛贡献。本章带你用 50 条中文金标准 QA + RAGAS 四维 + 消融实验,建立「改代码 → 用数字证明有效」的闭环;产出的脚本与数据集可直接作为 PR 素材。
一、为什么中文 Benchmark 是缺口
英文有 MS MARCO、Natural Questions;中文 RAG 缺少广泛认可的公开集。贡献一个可复现的 weknora-zh-benchmark 对社区与竞赛评审都是硬通货。
RAGAS 四维(复习):
| 指标 | 问什么 |
|---|---|
| Faithfulness | 答案是否忠于检索上下文? |
| Answer Relevancy | 答案是否答所问? |
| Context Precision | 检索到的 chunk 是否相关? |
| Context Recall | 是否漏掉该召回的 chunk? |
二、数据集格式(JSONL)
{
"id": "zh-001",
"question": "WeKnora 三路检索分别是什么?",
"ground_truth": "向量语义检索、BM25 关键词检索、PMI 知识图谱检索,经 RRF 融合。",
"relevant_doc_ids": ["weknora-arch.md"],
"difficulty": "easy",
"query_type": "factual",
"language": "zh"
}
规模建议:
| 阶段 | 条数 | 用途 |
|---|---|---|
| 冒烟 | 10 | 本地脚本调试 |
| 竞赛 PR | 50 | 首版 benchmark |
| 社区 | 200+ | 分 domain 扩展 |
三、构建 50 条的实操流程(4 步)
Step 1:选 corpus
从你已上传 WeKnora 的知识库导出 chunk,或固定 3–5 篇 Markdown(架构 doc、API doc、FAQ)。
Step 2:LLM 辅助生成 + 人工审核
PROMPT = """根据以下文档段落,生成 1 个中文问答对。
要求:问题像真实用户提问;答案仅来自段落;标注引用的段落编号。
段落:{chunk}
输出 JSON:{"question":"","ground_truth":"","chunk_id":""}
"""
人工审核清单:答案是否可验证、问题是否自然、是否泄露多 chunk 推理(hard 题除外)。
Step 3:划分难度
| 类型 | 比例 | 示例 |
|---|---|---|
| factual | 40% | 「支持几个向量库?」 |
| reasoning | 35% | 「为什么用 RRF 不用加权平均?」 |
| multi_hop | 25% | 「图谱路如何补 BM25 盲区?」 |
Step 4:冻结版本
benchmark/v0.1.jsonl + corpus/ + README 说明构建日期与 WeKnora commit。
仓库已含冒烟集:benchmark/smoke-10.jsonl + lab/corpus/ — 可直接 --dry-run 校验:
python3 guide/scripts/eval_weknora_ragas.py \
--benchmark benchmark/smoke-10.jsonl \
--out benchmark/runs/dry.json --dry-run
四、RAGAS 评估脚本(最小可运行)
"""eval_weknora_ragas.py — 需 pip install ragas datasets openai httpx"""
import json, os, httpx
from datasets import Dataset
from ragas import evaluate
from ragas.metrics import faithfulness, answer_relevancy, context_precision, context_recall
WEKNORA_URL = os.environ["WEKNORA_CHAT_URL"] # 你的问答 API
WEKNORA_KEY = os.environ.get("WEKNORA_KEY", "")
def weknora_qa(question: str) -> dict:
r = httpx.post(
WEKNORA_URL,
headers={"Authorization": f"Bearer {WEKNORA_KEY}"},
json={"query": question},
timeout=120,
)
r.raise_for_status()
data = r.json()
return {
"answer": data["answer"],
"contexts": [c["text"] for c in data.get("citations", [])],
}
rows = []
for item in open("benchmark/v0.1.jsonl", encoding="utf-8"):
ex = json.loads(item)
out = weknora_qa(ex["question"])
rows.append({
"question": ex["question"],
"ground_truth": ex["ground_truth"],
"answer": out["answer"],
"contexts": out["contexts"],
})
ds = Dataset.from_list(rows)
result = evaluate(ds, metrics=[faithfulness, answer_relevancy, context_precision, context_recall])
print(result)
注意:按 WeKnora 实际 API 字段调整 citations 解析;Faithfulness 需配置 Judge LLM(OpenAI/DeepSeek)。
五、消融实验设计(证明 PR 有效)
改检索模块后,跑同一 benchmark,对比:
| 实验 | 改动 | 预期 |
|---|---|---|
| baseline | main 分支 | — |
| +RRF k | k=60→40 | Precision↑ 或 Recall↓ |
| +表格 chunk | docparser 修复 | 表格题 Recall↑ |
| +QA 索引 | 摄入增强 | FAQ faithfulness↑ |
报告格式:
| 指标 | baseline | PR-xxx | Δ |
|------|----------|--------|---|
| context_recall | 0.72 | 0.81 | +0.09 |
六、贡献路径(WeKnora)
| 级别 | 交付物 | 工时 |
|---|---|---|
| L2 | scripts/eval_ragas.py + 10 条样例 |
3–5 天 |
| L2+ | 50 条 benchmark/zh-v0.1.jsonl |
+1 周 |
| L3 | CI 回归(PR 自动跑 smoke 10 条) | +1 周 |
| L4 | EvalPlugin + Dashboard | 2–3 周 |
与 Ch10 竞赛速查 L2 路径对齐。
七、常见坑
| 坑 | 处理 |
|---|---|
| Judge LLM 与业务 LLM 不一致 | 固定 eval 用 gpt-4o-mini 或 DeepSeek |
| 检索空 context | RAGAS 分异常 → 脚本先断言 len(contexts)>0 |
| 数据泄漏 | benchmark 问题勿进训练 chunk |
| 波动大 | 每配置跑 3 次取均值 |
章末自检
- 手写 3 条符合 JSONL schema 的中文 QA
- 说清 RAGAS 四维各衡量什么
- 设计一个消融实验验证「表格 chunk 修复」
- 列出 eval PR 的最小可合并交付物(脚本+样例数据)
延伸阅读
- Ch08 评估导读
- RAG 评估方法论
- WeKnora 竞赛贡献指南 §评估
- case-1785(单测写法参照)