shareefmx's picture
Deploy PDF Knowledge Assistant to Hugging Face Spaces
b106f38 verified
Raw
History Blame Contribute Delete
5.05 kB
"""RAG Evaluation Benchmark Runner Script.
Runs precision, recall, hit-rate, MRR, and faithfulness evaluations over
benchmark queries using an isolated in-memory index to prevent polluting
the main application vector store.
Usage:
python scripts/evaluate.py
"""
import json
import logging
from pathlib import Path
import sys
import tempfile
# Ensure project root is in sys.path
PROJECT_DIR = Path(__file__).resolve().parent.parent
if str(PROJECT_DIR) not in sys.path:
sys.path.insert(0, str(PROJECT_DIR))
import pymupdf as fitz
from app.config import get_settings, setup_logging
from app.embeddings.embedding_service import get_embedding_service
from app.evaluation.dataset import BENCHMARK_DATASET
from app.evaluation.evaluator import RAGEvaluator
from app.generation.llm import LLMServiceFactory
from app.ingestion.chunker import RecursiveChunker
from app.ingestion.pdf_loader import PDFLoader
from app.rag.pipeline import RAGPipeline
from app.retrieval.retriever import DocumentRetriever
from app.vectorstore.faiss_store import FAISSVectorStore
def create_benchmark_pdf(target_path: Path) -> Path:
"""Creates the standard benchmark PDF corresponding to BENCHMARK_DATASET."""
target_path.parent.mkdir(parents=True, exist_ok=True)
doc = fitz.open()
# Page 1
page1 = doc.new_page()
page1.insert_text(
(50, 72),
"Introduction to Retrieval-Augmented Generation\n\n"
"RAG combines parametric knowledge in neural models with non-parametric external retrieval.\n"
"The primary benefit of external retrieval in RAG is that it prevents hallucinations "
"and provides direct citations for human verification.",
)
# Page 2
page2 = doc.new_page()
page2.insert_text(
(50, 72),
"Methodology and Experimental Setup\n\n"
"The research evaluated FAISS vector indices with sentence-transformer embeddings.\n"
"Evaluation demonstrated a 94% hit rate on domain queries when using recursive character chunking.",
)
doc.save(str(target_path))
doc.close()
return target_path
def main():
settings = get_settings()
setup_logging(settings.log_level)
logger = logging.getLogger("eval_runner")
logger.info("Initializing isolated RAG evaluation benchmark...")
# Initialize isolated components to protect the active user's vector store
embedder = get_embedding_service(settings.embedding_model)
eval_store = FAISSVectorStore(dimension=embedder.dimension)
loader = PDFLoader()
chunker = RecursiveChunker(chunk_size=settings.chunk_size, chunk_overlap=settings.chunk_overlap)
with tempfile.TemporaryDirectory() as temp_dir:
bench_pdf_path = Path(temp_dir) / "sample_research.pdf"
create_benchmark_pdf(bench_pdf_path)
# Ingest benchmark PDF into the isolated evaluation store
pages = loader.load(bench_pdf_path)
chunks = chunker.chunk_documents(pages)
embeddings = embedder.embed_documents([c.text for c in chunks])
eval_store.add_chunks(chunks, embeddings)
eval_retriever = DocumentRetriever(
vector_store=eval_store,
embedding_service=embedder,
similarity_threshold=settings.similarity_threshold,
default_top_k=settings.top_k,
)
llm_service = LLMServiceFactory.create(
provider=settings.llm_provider,
gemini_api_key=settings.gemini_api_key,
openrouter_api_key=settings.openrouter_api_key,
model_name=settings.llm_model,
)
eval_pipeline = RAGPipeline(
retriever=eval_retriever,
llm_service=llm_service,
)
# Run evaluator
evaluator = RAGEvaluator(retriever=eval_retriever, pipeline=eval_pipeline)
report = evaluator.evaluate(BENCHMARK_DATASET, top_k=5)
# Print markdown report
print("\n" + "=" * 60)
print(" ๐Ÿ“Š RAG BENCHMARK EVALUATION RESULTS")
print("=" * 60 + "\n")
print(report.summary_table())
print("\nQuery Breakdown:")
for d in report.query_details:
status = "โœ… HIT" if d["hit"] else "โŒ MISS"
rank_str = f"Rank: #{d['first_rank']}" if d["first_rank"] else "Not in Top-K"
print(f"- [{status}] '{d['question']}' ({rank_str}, Prec: {d['precision']}, Faith: {d['faithfulness']})")
# Save results to data/processed/eval_results.json
output_file = settings.get_processed_path() / "eval_results.json"
with open(output_file, "w", encoding="utf-8") as f:
json.dump(
{
"total_queries": report.total_queries,
"hit_rate": report.hit_rate,
"mrr": report.mrr,
"avg_precision": report.avg_precision,
"avg_faithfulness": report.avg_faithfulness,
"details": report.query_details,
},
f,
indent=2,
)
logger.info("Saved evaluation report to '%s'", output_file)
if __name__ == "__main__":
main()