fusion.py 1.1 KB

123456789101112131415161718192021222324252627282930
  1. from __future__ import annotations
  2. from collections import defaultdict
  3. from collections.abc import Mapping, Sequence
  4. from app.core.knowledge.retrieval.contracts import KnowledgeEvidence
  5. def reciprocal_rank_fusion(
  6. ranked: Mapping[str, Sequence[KnowledgeEvidence]],
  7. *,
  8. limit: int = 30,
  9. k: int = 60,
  10. ) -> tuple[KnowledgeEvidence, ...]:
  11. scores: dict[str, float] = defaultdict(float)
  12. evidence_by_id: dict[str, KnowledgeEvidence] = {}
  13. provenance: dict[str, list[str]] = defaultdict(list)
  14. for retriever, values in ranked.items():
  15. for rank, evidence in enumerate(values, start=1):
  16. scores[evidence.chunk_id] += 1.0 / (k + rank)
  17. evidence_by_id.setdefault(evidence.chunk_id, evidence)
  18. if retriever not in provenance[evidence.chunk_id]:
  19. provenance[evidence.chunk_id].append(retriever)
  20. ordered = sorted(scores, key=lambda key: (-scores[key], key))[:limit]
  21. return tuple(
  22. evidence_by_id[key].with_score_and_retriever(
  23. scores[key], "+".join(provenance[key])
  24. )
  25. for key in ordered
  26. )