#!/usr/bin/env python """Multi-hop RAG eval on the HotpotQA-derived corpus (paper-scale, not smoke). Indexes ``data/corpus_hf`` (40 HotpotQA passages) and evaluates every retrieval variant + the full agentic pipeline over ``data/golden/golden_qa_hf.json`` (40 multi-hop questions). Reuses the frozen eval internals so the metrics are computed identically to ``make eval`` — only the corpus and golden set change. Writes ``reports/eval_hotpotqa_report.json`` with full provenance. Numbers are real (Ollama LLM + bge/ollama embeddings) — nothing hand-written. """ from __future__ import annotations import json from auralynq.config import get_settings from auralynq.eval.datasets import GoldenItem from auralynq.eval.provenance import report_provenance from auralynq.eval.report import _agentic, _retrieval_variants from auralynq.pipeline import build_index from auralynq.telemetry import get_logger _log = get_logger("auralynq.bench_hotpotqa") def _load_hf_golden() -> list[GoldenItem]: path = get_settings().data_dir / "golden" / "golden_qa_hf.json" data = json.loads(path.read_text(encoding="utf-8")) return [ GoldenItem( id=i["id"], question=i["question"], answer=str(i.get("answer", "")), supporting=i.get("supporting", []), type=i.get("type", "multi"), ) for i in data.get("items", []) ] def main(limit: int | None = None) -> None: s = get_settings() s.ensure_dirs() corpus = s.data_dir / "corpus_hf" _log.info("bench_hotpotqa.indexing", corpus=str(corpus)) stats = build_index(corpus, rebuild=True) _log.info( "bench_hotpotqa.indexed", **{k: stats[k] for k in ("chunks_indexed", "documents") if k in stats}, ) golden = _load_hf_golden() if limit: golden = golden[:limit] k = s.retrieval.final_k _log.info("bench_hotpotqa.eval_start", n_golden=len(golden), k=k) retrieval = _retrieval_variants(golden, k) agentic = _agentic(golden, k) report = { "version": 1, "dataset": "HotpotQA (distractor) 40-passage subset — multi-hop QA", "config": {"k": k, "n_golden": len(golden)}, "retrieval": retrieval, "agentic": agentic, "provenance": report_provenance( dataset_version=f"golden_qa_hf.json n={len(golden)} (corpus_hf)" ), } out = s.reports_dir / "eval_hotpotqa_report.json" out.write_text(json.dumps(report, indent=2), encoding="utf-8") _log.info("bench_hotpotqa.report_written", path=str(out)) print(f"\nWrote {out}") print(json.dumps({"retrieval": retrieval, "ragas": agentic.get("ragas")}, indent=2)) if __name__ == "__main__": import argparse ap = argparse.ArgumentParser() ap.add_argument("--limit", type=int, default=None, help="cap number of golden items") args = ap.parse_args() main(limit=args.limit)