v9.1.1
Menu

Reciprocal Rank Fusion over Hybrid Retrievers

mediumFREEContext & Agent MemoryAI AgentsAI Agents 1

Problem Statement

A hybrid agent memory queries several retrievers at once - a dense vector index, a BM25 lexical index, a graph walk - and each returns its own ranked list. Their scores are on incompatible scales, so you cannot simply average them. Reciprocal Rank Fusion (RRF) solves this by throwing the scores away and fusing on rank alone.

Background

For a document dd appearing at 1-based rank rir_i in ranked list ii, RRF assigns

RRF(d)=∑i : d∈Li1k+ri\text{RRF}(d) = \sum_{i \,:\, d \in L_i} \frac{1}{k + r_i}

The constant kk (conventionally 60) damps the influence of the very top of any single list, so a document ranked #1 by one retriever cannot dominate a document ranked #2 or #3 by all of them. That consensus bias is exactly why RRF is the default fusion step in hybrid memory stores.

Your Task

Implement:

def reciprocal_rank_fusion(rankings, k=60, top_n=None):
  • rankings: list of ranked lists; each inner list holds document ids (strings), best first, no duplicates within a list.
  • k: the RRF damping constant.
  • top_n: if not None, return only the first top_n fused results.

Steps:

  1. Accumulate the RRF score for every document across all lists.
  2. Round each fused score to 6 decimal places.
  3. Sort by rounded score descending; break ties by document id ascending (lexicographic).
  4. Return a list of [doc_id, rounded_score] pairs, truncated to top_n when given.

Input/Output Format

Returns list[list] - each element is [str, float]. Rounding happens before sorting, so two documents whose scores agree to 6 dp are ordered by id.

Sample

lists = [["a", "b", "c"], ["b", "a", "d"]]
print(reciprocal_rank_fusion(lists, k=1))
# [['a', 0.833333], ['b', 0.833333], ['c', 0.25], ['d', 0.25]]

a scores 1/2 + 1/3 = 0.833333 and b scores 1/3 + 1/2 = 0.833333 - a tie, broken by id.

Example

Input
print(reciprocal_rank_fusion([['a','b','c'], ['b','a','d']], k=1))
Output
[['a', 0.833333], ['b', 0.833333], ['c', 0.25], ['d', 0.25]]
Reasoning
  • Input: two ranked lists (best first) and k=1k = 1. Ranks start at 1, so in list 1 a has rank 1, b rank 2, c rank 3; in list 2 b has rank 1, a rank 2, d rank 3.
  • a: contributes 11+1=0.5\frac{1}{1+1} = 0.5 from list 1 and 11+2≈0.333333\frac{1}{1+2} \approx 0.333333 from list 2, total 56=0.8333…\frac{5}{6} = 0.8333\ldots, which rounds to 0.833333.
  • b: the mirror image (13+12\frac{1}{3} + \frac{1}{2}), so it lands on exactly the same rounded score 0.833333.
  • c and d: each appears in only one list, at rank 3, giving 11+3=0.25\frac{1}{1+3} = 0.25. The list where a document is missing adds nothing.
  • Output: [['a', 0.833333], ['b', 0.833333], ['c', 0.25], ['d', 0.25]], highest score first. Both pairs are ties, so alphabetical id order decides: a before b, c before d.
  • Notice that consensus wins: a and b appear in both lists and beat c and d, which only one retriever found.

Constraints

  • 1 <= len(rankings) <= 20, each inner list has at most 1000 ids
  • k >= 1; ranks are 1-based (the first element of a list has rank 1)
  • Round fused scores to exactly 6 decimal places before sorting
  • Ties on the rounded score are broken by ascending document id
  • A document missing from a list contributes nothing from that list

Learn this in

More from AI Agents 1

solution.py

Test results

Sign in to run your code against the test cases.