Combine retrieval ranks with reciprocal rank fusion

AI assisted the preparation of this article, and its Python example was tested locally with synthetic fixtures.
A lexical search and a vector search may return different scores on unrelated scales. When both produce ranked candidate IDs, reciprocal rank fusion offers a small baseline that uses positions instead of combining raw scores.
Flow: Ranked lists, then Rank weights, then Sum by ID, then Order results.
Add one contribution per list
This fixture accepts finite lists of unique string IDs, with the first result at rank one. A document missing from a list contributes zero for that list. That is the convention for these truncated lists; the original paper describes complete rankings. The parameter k is a positive integer.
from collections import defaultdict
def fuse(rankings, k):
if type(k) is not int or k <= 0:
raise ValueError("k must be a positive integer")
scores = defaultdict(float)
for ranking in rankings:
if any(not isinstance(item, str) or not item for item in ranking):
raise ValueError("IDs must be nonempty strings")
if len(set(ranking)) != len(ranking):
raise ValueError("duplicate ID in ranking")
for rank, item in enumerate(ranking, 1):
scores[item] += 1 / (k + rank)
return sorted(scores.items(), key=lambda pair: (-pair[1], pair[0]))
For [A, B] and [B, C] with k=1, B gets 1/3 + 1/2, A gets 1/2 and C gets 1/3. Ties in the resulting floating-point scores are ordered by ID. The input order within each list remains meaningful; the function rejects duplicate IDs within one list rather than counting the same document twice there.
The original reciprocal rank fusion paper defines the reciprocal contribution and describes its experiments. Its choice of parameter is not a universal optimum. Pick and document a parameter for the application, and evaluate it with a fixed authorized question set.
Keep boundaries visible
Apply authorization and revision filters before producing either candidate list. Merging globally retrieved results and filtering them later can leak restricted passages to earlier processing stages. A document absent from both input lists cannot be recovered by this function.
Test empty lists, duplicates, invalid k and a tie as well as the worked example. Keep each source ranking in the diagnostic record so a changed merged order can be traced. If one list is unavailable, decide whether to return the other list with a warning or fail the request; the example accepts an empty list without diagnosing its cause.
This is a local arithmetic fixture on CPython 3.13.5. It does not run a retriever or a model. A higher fusion score is not a calibrated probability or evidence that the passage supports an answer. The paper's experimental results do not establish an improvement on a different corpus.