Spaces:
Paused
Paused
| import sys | |
| import json | |
| import time | |
| import numpy as np | |
| from pathlib import Path | |
| # Add parent directory to path | |
| sys.path.append(str(Path(__file__).resolve().parent.parent)) | |
| from app.main import generate_answer, embed_query, retrieve_chunks | |
| EVAL_OUTPUT_PATH = Path(__file__).resolve().parent.parent / "data" / "eval_results.json" | |
| # Ground Truth Evaluation Dataset | |
| EVAL_DATASET = [ | |
| { | |
| "id": 1, | |
| "query": "What is the procedure for early loan repayment?", | |
| "target_doc": "early_repayment.txt", | |
| "expected_keywords": ["bondora.fi", "repay early", "PIN", "repaid in full"] | |
| }, | |
| { | |
| "id": 2, | |
| "query": "How are customer complaints handled according to the policy?", | |
| "target_doc": "Complaints-procedure.pdf", | |
| "expected_keywords": ["Complaints", "5 business days", "15 days", "Consumer Disputes Board"] | |
| }, | |
| { | |
| "id": 3, | |
| "query": "What support is available during financial hardship or job loss?", | |
| "target_doc": "financial_hardship.txt", | |
| "expected_keywords": ["financial hardship", "payment holiday", "payslips", "reschedule"] | |
| }, | |
| { | |
| "id": 4, | |
| "query": "What is B-Secure and what benefits does it offer?", | |
| "target_doc": "what_is_b_secure.txt", | |
| "expected_keywords": ["B-Secure", "10", "restructure", "principal payment holiday"] | |
| }, | |
| { | |
| "id": 5, | |
| "query": "What are the rules for debt collection?", | |
| "target_doc": "debt_collection_process.txt", | |
| "expected_keywords": ["debt", "collection"] | |
| }, | |
| { | |
| "id": 6, | |
| "query": "What are the total loan amounts and average interest rates by country in the Bondora dataset?", | |
| "target_doc": "bondora_loan_dataset_summary.txt", | |
| "expected_keywords": ["Estonia", "Finland", "Spain", "25.32%"] | |
| }, | |
| { | |
| "id": 7, | |
| "query": "How do I change my monthly payment date?", | |
| "target_doc": "change_payment_date.txt", | |
| "expected_keywords": ["payment date", "1st and 27th"] | |
| }, | |
| { | |
| "id": 8, | |
| "query": "What is the process for closing an account?", | |
| "target_doc": "closing_account.txt", | |
| "expected_keywords": ["closing", "account"] | |
| }, | |
| { | |
| "id": 9, | |
| "query": "What terms apply to automated payments?", | |
| "target_doc": "Terms-and-conditions-for-automated-payments.pdf", | |
| "expected_keywords": ["automated", "payments"] | |
| }, | |
| { | |
| "id": 10, | |
| "query": "Where can I find the conflict policy information?", | |
| "target_doc": "Conflict-policy.pdf", | |
| "expected_keywords": ["Conflict", "policy"] | |
| } | |
| ] | |
| def run_evaluation(): | |
| print("Starting automated RAG evaluation benchmark run...") | |
| hits = 0 | |
| faithfulness_scores = [] | |
| latencies = [] | |
| costs = [] | |
| results = [] | |
| for item in EVAL_DATASET: | |
| q = item["query"] | |
| target = item["target_doc"] | |
| keywords = item["expected_keywords"] | |
| print(f"Evaluating Q{item['id']}: '{q[:40]}...'") | |
| # Run query (bypassing cache for benchmark accuracy) | |
| res = generate_answer(q, use_cache=False) | |
| latencies.append(res["latency_ms"]) | |
| costs.append(res["estimated_cost_usd"]) | |
| # Check Hit@10 | |
| retrieved_docs = [fn for _, fn, _ in res["chunks"]] | |
| hit = any(target.lower() in fn.lower() for fn in retrieved_docs) | |
| if hit: | |
| hits += 1 | |
| # Check Faithfulness / Keyword Coverage | |
| ans = res["answer"] | |
| matched_kw = [kw for kw in keywords if kw.lower() in ans.lower()] | |
| faithfulness = len(matched_kw) / len(keywords) if keywords else 1.0 | |
| faithfulness_scores.append(faithfulness) | |
| results.append({ | |
| "id": item["id"], | |
| "query": q, | |
| "target_doc": target, | |
| "hit_at_10": hit, | |
| "faithfulness_score": round(faithfulness, 2), | |
| "matched_keywords": matched_kw, | |
| "latency_ms": res["latency_ms"], | |
| "cost_usd": res["estimated_cost_usd"], | |
| "retrieved_count": len(retrieved_docs) | |
| }) | |
| time.sleep(0.5) | |
| hit_at_10 = round(hits / len(EVAL_DATASET), 2) | |
| avg_faithfulness = round(float(np.mean(faithfulness_scores)), 2) | |
| p50_latency = round(float(np.percentile(latencies, 50)), 2) | |
| p95_latency = round(float(np.percentile(latencies, 95)), 2) | |
| avg_cost = round(float(np.mean(costs)), 6) | |
| total_cost_1k = round(avg_cost * 1000, 4) | |
| summary = { | |
| "timestamp": time.time(), | |
| "total_test_cases": len(EVAL_DATASET), | |
| "hit_at_10_recall": hit_at_10, | |
| "avg_faithfulness_score": avg_faithfulness, | |
| "p50_latency_ms": p50_latency, | |
| "p95_latency_ms": p95_latency, | |
| "avg_cost_per_query_usd": avg_cost, | |
| "estimated_cost_per_1k_queries_usd": total_cost_1k, | |
| "test_results": results | |
| } | |
| EVAL_OUTPUT_PATH.parent.mkdir(parents=True, exist_ok=True) | |
| with open(EVAL_OUTPUT_PATH, "w", encoding="utf-8") as f: | |
| json.dump(summary, f, indent=2) | |
| print("\n================ EVALUATION BENCHMARK RESULTS ================") | |
| print(f" Hit@10 Recall Rate: {hit_at_10 * 100:.1f}%") | |
| print(f" Avg Faithfulness Score: {avg_faithfulness * 100:.1f}%") | |
| print(f" p50 Latency: {p50_latency} ms") | |
| print(f" p95 Latency: {p95_latency} ms") | |
| print(f" Avg Cost / Request: ${avg_cost:.6f}") | |
| print(f" Cost / 1,000 Queries: ${total_cost_1k:.4f}") | |
| print("==============================================================") | |
| print(f"Saved evaluation results to {EVAL_OUTPUT_PATH}") | |
| if __name__ == "__main__": | |
| run_evaluation() | |