Content hash: 9bd533d9a8b561b575fdd540cfb39c91aaf2f71d9af34ffed7c6452e9658f245
# Score Fusion Methods Compared
## Why naive score combination fails
BM25 scores are unbounded positive floats; cosine similarity is bounded [-1, 1].
Averaging them means the retriever with larger-magnitude scores dominates.
## Methods
### 1. Reciprocal Rank Fusion (RRF) — recommended
```
score(d) = Σ 1 / (k + rank_r(d))
```
- Works on **ranks**, not scores — robust to scale differences
- `k=60` is the standard constant; tune on your data
- Built into Weaviate, Qdrant, OpenSearch 2.19+
### 2. Min-max normalization
```
norm_score = (score - min) / (max - min)
```
- Requires knowing min/max per query, which can be noisy
- Sensitive to outliers (a single high-scoring doc distorts the scale)
### 3. Z-score normalization
```
norm_score = (score - μ) / σ
```
- Assumes normal distribution — rarely holds for retrieval scores
- Negative scores are confusing downstream
### 4. Weighted linear combination
```
final_score = α × bm25_norm + (1-α) × dense_norm
```
- Only works AFTER normalization — still fragile
- α is another hyperparameter to tune per dataset
## When hybrid search helps
| Query type | BM25 | Dense | Hybrid best? |
|-----------|------|-------|-------------|
| Exact product code | ✅ | ❌ | Yes — BM25 carries it |
| Paraphrase question | ❌ | ✅ | Yes — dense carries it |
| Named entity + concept | ⚠️ | ⚠️ | Yes — both contribute |
| Simple factual lookup | ✅ | ✅ | No — either works alone |
| Out-of-domain jargon | ✅ | ❌ | Yes — BM25 backstop |
## Production checklist
- [ ] Pull `retrieval_k` ≥ 2× `final_k` candidates from each retriever
- [ ] Apply metadata filters in BOTH retrievers before fusion
- [ ] Tune RRF `k` on your eval set (60 is a good default)
- [ ] Verify hybrid ≥ best single retriever on your complaint queries