市面上的 RAG 框架不缺功能,缺的是让你知道检索到底好不好的能力。
我做过的每个 RAG 项目,最后真正花时间的都不是"接上向量库",而是反复回答同一个问题:为什么这条查询召回的文档是错的?
rag-kit 就是围绕这个问题设计的。
设计取舍
开源库最容易犯的错是想服务所有人。rag-kit 明确不做这些事:
- 不做 Agent 编排(交给上层)
- 不内置 UI(评测结果输出 JSON,你自己可视化)
- 不支持十种向量库(只支持三种,但接口统一)
- 不做自动调参(参数是显式的,不藏魔法)
换来的是:每个环节都可观测、可替换、可测试。
核心:检索管线可拆解
大多数框架把检索封装成一个黑盒函数。rag-kit 把它拆成可单独测量的四段:
from ragkit import Pipeline, retrievers, rerankers, evaluators
pipeline = Pipeline(
retriever=retrievers.HybridRetriever(
dense=retrievers.DenseRetriever(
model="bge-m3",
index="hnsw", # 也可以换成 "ivf_pq" 压内存
),
sparse=retrievers.BM25Retriever(k1=1.2, b=0.75),
fusion="rrf", # 倒数排名融合,无需调权重
top_k=50, # 召回宽一点,交给重排收敛
),
reranker=rerankers.CrossEncoder(
model="bge-reranker-v2",
top_k=5, # 重排后只留 5 条进上下文
score_threshold=0.35, # 低于阈值直接丢弃,宁可答"不知道"
),
)
# 关键:每一段的输出都能单独拿出来看
result = pipeline.run("如何在 Kubernetes 里配置 Liveness 探针?", debug=True)
print(result.stages.retriever.hits[:3]) # 召回的原始 50 条
print(result.stages.reranker.dropped) # 被重排丢弃的条目及原因
print(result.stages.final) # 最终进上下文的 5 条第 14-20 行是整个库的核心设计:重排阶段带阈值,低于阈值宁可不答。
这一点在很多框架里被忽略了。它们无条件把 top-k 塞进上下文,于是模型面对一堆弱相关文档,只能硬着头皮编一个答案。加一道阈值,幻觉率会显著下降。
评测:内置但不是负担
评测是 rag-kit 的一等公民,但不强制你用。
from ragkit.evaluators import RetrievalEvaluator
evaluator = RetrievalEvaluator(
pipeline=pipeline,
dataset="qa_golden.jsonl", # 格式:{"query": ..., "relevant_ids": [...]}
)
report = evaluator.run(metrics=["recall@5", "mrr@10", "ndcg@10"])
report.save("reports/2026-08-10.json")输出是一份结构化报告,包含整体指标和每条失败查询的召回明细。后者才是你真正需要的东西——平均值告诉你好不好,失败明细告诉你为什么不好。
构造评测集的最小成本方案
很多人卡在"没有标注数据"。我的做法是让模型生成初版,人工校正:
from ragkit.tools import generate_qa_pairs
# 从文档自动生成问答对,作为评测集初稿
pairs = generate_qa_pairs(
docs="docs/**/*.md",
per_chunk=2,
llm="gpt-4o-mini",
)
pairs.to_jsonl("qa_golden.jsonl")生成的初稿大概有 30% 需要人工修正,但比起从零标注 200 条,这个成本可以接受。
没有评测集的 RAG 优化,本质上是在盲调。你以为改好了,其实只是换了一种错法。
性能数据
在自建的 12 万段落技术文档集上(单卡 RTX 4090,bge-m3 + bge-reranker-v2):
| 配置 | Recall@5 | MRR@10 | P95 延迟 |
|---|---|---|---|
| 纯向量检索 | 0.71 | 0.58 | 120 ms |
| 混合检索(RRF) | 0.83 | 0.66 | 185 ms |
| 混合 + 重排 | 0.89 | 0.78 | 420 ms |
| 混合 + 重排 + 查询改写 | 0.90 | 0.79 | 610 ms |
注意最后一行的边际收益:查询改写只带来 0.01 的 recall 提升,却增加了 45% 的延迟。这个取舍在很多业务上是负收益的。
这就是为什么我把参数全部显式暴露——让你自己权衡,而不是让我替你决定。
路线图
接下来两个版本会做:
- 多模态检索:支持图表与截图的跨模态召回
- 增量索引:目前全量重建索引,大库不友好
- 更多语言的 tokenizer 适配(当前中文分词依赖 jieba)
欢迎提 Issue 和 PR。项目地址见 好物分享 里的开源项目分类。
参与方式
如果你是第一次给开源项目提 PR,建议从这个顺序入手:
- 先提一个 typo 修复,跑通完整的 fork → PR → CI 流程
- 再补一个测试用例,熟悉测试框架
- 最后才是功能改动
别小看第一步。我见过太多人一上来就想改架构,结果卡在 CI 配置上两周,热情耗尽就放弃了。