Vector Retrieval、Hybrid Retrieval 和 Reranker 的工程实践:zglab-rag 的检索取舍
目录
检索系统的职责
检索层只回答“哪些公开 Chunk 值得成为生成候选”,不负责切分文档、不调用 LLM,也不把 private 文档当作高分候选的后备。zglab-rag 把 Vector、Lexical、Hybrid 和 Reranker 放在独立边界,避免某个 实现类型渗入 domain model。
flowchart LR
Q[Query] --> V[VectorRetriever]
Q --> L[LexicalRetriever]
V --> H[HybridRetriever / RRF]
L --> H
V --> RR[Reranker: 可选]
H --> OUT[过滤后的 RetrievalResult]
RR --> OUT
SQLite 内的两条检索路径
SQLite 保存 source、document、chunk、embedding profile 与 index run 等权威 metadata。sqlite-vec
的 vec0 表保存与 chunks.id 对应的 512 维 BGE 向量;VectorRetriever 对 query 做相同 profile
的 embedding 后进行 KNN,再关联 metadata 并强制 public visibility。
Lexical 路径使用 SQLite FTS5 trigram 与 BM25。trigram 对技术词、项目名和中英文片段更友好,但 它的 BM25 分数与向量距离没有可直接比较的共同尺度。Hybrid 因此不相加原始分数,而是先各取候选 rank,再用配置化 Reciprocal Rank Fusion(RRF)合并。
为什么 Hybrid 没有成为默认
Phase 6 在未改变的 47 条计分 Query、3 条 hard negative 上比较 Vector-only、BM25-only 与等权
RRF Hybrid。候选池各为 50;列权重也比较了 title/section/content=1/1/1 与 2/2/1,前者在
Recall@1 与 MRR 上较好。
但等权 RRF、k=60 的首个 baseline 没有超过 Vector baseline。因此项目没有因为“Hybrid 听起来
更完整”而切换默认模式。Hybrid 仍是可评测实现,后续可以探索更多参数、归一化或不同语料,却不能
把尚未证实的复杂度包装成提升。
Reranker 的收益与成本
Reranker 使用 cross-encoder/mmarco-mMiniLMv2-L12-H384-v1,只重新排列 public Vector 的
Top-N,不会把 N 之外的新 Chunk 加入结果。主实验固定 BGE contextual、相同数据集与 Chunking,
测试 candidate K 为 10、20、30。
| Candidate K=20 | Vector | Reranked | 变化 |
|---|---|---|---|
| Recall@1 | 0.5213 | 0.6809 | +0.1596 |
| Recall@5 | 0.7872 | 0.8404 | +0.0532 |
| Recall@20 | 0.9255 | 0.9255 | 0 |
| MRR | 0.6532 | 0.7753 | +0.1221 |
Recall@20 不变是重要不变量:Reranker 只改变候选集合内排序。Candidate 20 的质量优于 10 和 30, 但 CPU 中位重排延迟约 1.74 秒,p95 约 2.51 秒;完整评测进程峰值 RSS 约 1.49 GB。并且 project 分类 MRR 从 0.9333 降到 0.8167,一条人工 Spring 问题也被泛化 README 摘要挤到前面。
flowchart TD
V[Vector Top-N] --> R[Cross Encoder 打分]
R --> S[只重排已有候选]
S --> G{质量收益是否覆盖成本与退化?}
G -->|当前 2C2G: 否| D[保持 Vector 默认]
G -->|特定场景: 是| O[显式 reranked 模式]
最终默认路径
当前生产默认是 Vector Retrieval,而不是 Hybrid 或 Reranked。这个选择不否认后两者的价值:Hybrid 保留为实验入口,Reranker 在质量优先且资源允许时可显式启用。默认路径则需要同时满足公开过滤、低 延迟、可预测内存和已验证质量。
方法论总结
- 不混合没有共同语义的 score;rank fusion 比“随手加权”更可解释。
- Reranker 的 Recall@K 不会增加候选覆盖,只能改善已有候选的排序。
- 全局指标提升仍要查看 category 与人工失败案例。
- 2C2G 的资源预算是架构输入,不是上线后再处理的运维细节。
检索策略的成熟不在于拥有最多开关,而在于每个默认值都能说明它的收益、代价和不适用条件。