EmpathRAG / eval /condition_a.py
Mukul Rayana
feat: eval scripts — BM25 baseline, adversarial probes, BERTScore, RAGAS, Wilcoxon (Day 14)
78fc1e6
Raw
History Blame Contribute Delete
1.61 kB
"""
eval/condition_a.py
Condition A — BM25 sparse retrieval baseline (no emotion components).
Loads all chunk texts from SQLite, builds a BM25 index, retrieves top_k
chunks for a query using keyword overlap only.
Used by run_ablation.py as the Condition A retrieval function.
"""
import sqlite3
import json
from rank_bm25 import BM25Okapi
DB_PATH = "data/indexes/metadata.db"
def load_bm25_index(db_path: str = DB_PATH):
"""Load all chunk texts from SQLite and build BM25 index. Returns (bm25, id_list, text_list)."""
conn = sqlite3.connect(db_path)
rows = conn.execute("SELECT id, text FROM chunks ORDER BY id").fetchall()
conn.close()
ids = [r[0] for r in rows]
texts = [r[1] for r in rows]
tokenized = [t.lower().split() for t in texts]
bm25 = BM25Okapi(tokenized)
return bm25, ids, texts
def retrieve_bm25(query: str, bm25, ids, texts, top_k: int = 5):
"""Retrieve top_k chunks using BM25. Returns list of text strings."""
tokens = query.lower().split()
scores = bm25.get_scores(tokens)
import numpy as np
top_indices = scores.argsort()[::-1][:top_k]
return [texts[i] for i in top_indices]
if __name__ == "__main__":
print("Building BM25 index from SQLite (this takes ~60-90s for 1.67M chunks)...")
bm25, ids, texts = load_bm25_index()
print(f"BM25 index built: {len(texts):,} documents")
# Quick sanity check
results = retrieve_bm25("I feel hopeless and overwhelmed", bm25, ids, texts, top_k=3)
print(f"Sample retrieval (3 chunks):")
for i, r in enumerate(results):
print(f" [{i+1}] {r[:100]}...")