File size: 901 Bytes
ce45eb0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
"""Reciprocal Rank Fusion of the lexical and dense channels.

RRF is rank-based, so it needs no score calibration between channels — which is
why it is robust when one channel is a stub and the other a real model.
"""
from __future__ import annotations
from typing import Dict, List, Tuple

import numpy as np

from ..schema.item import ContextItem
from .dense import dense_rank
from .lexical import bm25_rank


def rrf(rank_lists: List[List[Tuple[str, float]]], k: int = 60) -> Dict[str, float]:
    fused: Dict[str, float] = {}
    for rl in rank_lists:
        for rank, (item_id, _score) in enumerate(rl):
            fused[item_id] = fused.get(item_id, 0.0) + 1.0 / (k + rank + 1)
    return fused


def hybrid_retrieve(query: str, query_emb: np.ndarray,
                    items: List[ContextItem]) -> Dict[str, float]:
    return rrf([bm25_rank(query, items), dense_rank(query_emb, items)])