mirror of
https://git.pumpin.online/funkpower/catena.git
synced 2026-10-23 03:07:13 +00:00
rrf: rank fusion without the database
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
"""Fuse ranked lists the way the retrieval layer fuses them.
|
||||
|
||||
The pipeline's fusion is written inline in a database query function, so the
|
||||
scoring loop is spelled out again here — but the two constants that decide
|
||||
everything are imported from it, so a change to either shows up as a parity
|
||||
failure rather than as a silently different ranking.
|
||||
|
||||
Reads hit records on stdin and writes `{"id", "rank", "score"}` per line.
|
||||
|
||||
CATENA_PARITY_PYTHONPATH=/path/to/knowledge-mcp/src \
|
||||
python3 oracle/rrf.py < hits.ndjson
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
for path in os.environ.get("CATENA_PARITY_PYTHONPATH", "").split(os.pathsep):
|
||||
if path:
|
||||
sys.path.insert(0, path)
|
||||
|
||||
from knowledge_mcp.retrieval import META_WEIGHT, RRF_K # noqa: E402
|
||||
|
||||
WEIGHTS = {"vec": 1.0, "fts": 1.0, "meta": META_WEIGHT}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
pool: dict[str, dict] = {}
|
||||
for line in sys.stdin.buffer.read().decode("utf-8").split("\n"):
|
||||
if not line.strip():
|
||||
continue
|
||||
hit = json.loads(line)
|
||||
entry = pool.setdefault(hit["id"], {"score": 0.0, "seen": {}})
|
||||
channel = hit["channel"]
|
||||
weight = WEIGHTS.get(channel, 1.0)
|
||||
previous = entry["seen"].get(channel)
|
||||
if previous is not None:
|
||||
entry["score"] -= weight / (RRF_K + previous)
|
||||
entry["seen"][channel] = hit["rank"]
|
||||
entry["score"] += weight / (RRF_K + hit["rank"])
|
||||
|
||||
# Python's dict keeps insertion order and its sort is stable, so ties come
|
||||
# out in the order the ids were first seen.
|
||||
ranked = sorted(pool.items(), key=lambda kv: kv[1]["score"], reverse=True)
|
||||
|
||||
out = sys.stdout
|
||||
for rank, (hit_id, entry) in enumerate(ranked, 1):
|
||||
out.write(json.dumps({"id": hit_id, "rank": rank, "score": entry["score"]}) + "\n")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user