ML-Chatbot / audit_tool.py
kmanche4675
chore: clean up repo, add benchmark logs, and ignore dev scripts
333621f
Raw
History Blame Contribute Delete
5.25 kB
"""
Automated audit script for Inframat-X RAG chatbot.
Evaluates Hit Rate@8 (At least one correct document found).
"""
import os
import re
import json
import time
import pandas as pd
from datetime import datetime
from typing import Tuple, Optional, Callable
def load_sources_map(csv_path="sources.csv"):
if not os.path.exists(csv_path):
return {}
df = pd.read_csv(csv_path).fillna("")
df.columns = df.columns.str.strip()
src_map = {}
for _, r in df.iterrows():
raw_key = str(r.get("source_key", "")).strip().lower()
fname = os.path.basename(raw_key).lower().strip()
raw_name = str(r.get("name", "")).strip().lower()
raw_id = str(r.get("id", "")).strip()
clean_id = raw_id.replace("PAPER_", "").replace("paper_", "").lstrip("0")
if not clean_id: clean_id = "0"
if fname: src_map[fname.replace('.pdf', '')] = clean_id
if raw_name: src_map[raw_name.replace('.pdf', '')] = clean_id
src_map[raw_id.lower()] = clean_id
return src_map
def extract_retrieved_ids(full_output: str) -> list:
if not full_output:
return []
sources_match = re.search(r'\*\*Sources:\*\*(.*)', full_output)
if sources_match:
ids = re.findall(r'\[(\d+)\]', sources_match.group(1))
return list(set(ids))
ref_section = re.search(r'### References\s*\n(.*?)(?:\n\s*\n|$)', full_output, re.DOTALL)
if ref_section:
ids = re.findall(r'\[(\d+)\]', ref_section.group(1))
return list(set(ids))
return []
def calculate_hit_rate(retrieved_ids: list, gold_docs: list, sources_map: dict) -> float:
"""
Checks if AT LEAST ONE expected document was successfully retrieved.
Returns 1.0 (Success) or 0.0 (Fail).
"""
if not gold_docs:
return 0.0
expected_ids = set()
for g in gold_docs:
g_clean = g.lower().replace('.pdf', '').strip()
if g_clean in sources_map:
expected_ids.add(sources_map[g_clean])
else:
nums = re.findall(r'\d+', g_clean)
if nums:
expected_ids.add(nums[-1].lstrip('0') or '0')
# YOUR LOGIC: Did we find at least one?
for e in expected_ids:
if e in retrieved_ids:
return 1.0 # 100% Success for this question
return 0.0 # 0% Success
def run_audit(
rag_reply_func,
gold_csv_path: str = "gold.csv",
output_base_dir: Optional[str] = None,
progress_callback: Optional[Callable[[str, int, int], None]] = None,
k_retrieval: int = 10
) -> Tuple[str, str]:
if not os.path.exists(gold_csv_path):
return f"❌ Error: Could not find {gold_csv_path}.", ""
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
if output_base_dir is None:
output_base_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), f"Audit_{timestamp}")
os.makedirs(output_base_dir, exist_ok=True)
df = pd.read_csv(gold_csv_path)
total_questions = len(df)
jsonl_path = os.path.join(output_base_dir, "rag_logs.jsonl")
sources_map = load_sources_map("sources.csv")
total_hit_rate = 0.0
processed_count = 0
if progress_callback: progress_callback("Gold Set Benchmark", 0, total_questions)
with open(jsonl_path, "w", encoding="utf-8") as log_file:
for idx, row in df.iterrows():
question = row['question']
raw_gold = str(row['relevant_docs']).split(';')
gold_docs = [p.strip() for p in raw_gold if p.strip()]
raw_output = rag_reply_func(question, k=k_retrieval)
retrieved_ids = extract_retrieved_ids(raw_output)
# Use the new Hit Rate logic
hit_score = calculate_hit_rate(retrieved_ids, gold_docs, sources_map)
total_hit_rate += hit_score
processed_count += 1
log_entry = {
"question_id": idx + 1,
"question": question,
"gold_documents_raw": gold_docs,
"retrieved_ids": retrieved_ids,
"hit_score": hit_score
}
log_file.write(json.dumps(log_entry) + "\n")
if progress_callback: progress_callback("Gold Set Benchmark", processed_count, total_questions)
time.sleep(3)
average_hit_rate = total_hit_rate / processed_count if processed_count > 0 else 0.0
summary_path = os.path.join(output_base_dir, "benchmark_summary.txt")
with open(summary_path, "w", encoding="utf-8") as f:
f.write("INFRAMAT-X RAG BENCHMARK REPORT\n")
f.write(f"Run completed at: {timestamp}\n")
f.write(f"Questions processed: {processed_count}\n")
f.write(f"Average Hit Rate@10: {average_hit_rate:.4f}\n")
summary_str = (
f"✅ Benchmark finished!\n"
f"📁 Logs saved to: {jsonl_path}\n"
f"📊 Average Hit Rate@10: {average_hit_rate:.4f}\n"
)
import shutil
zip_path = shutil.make_archive(output_base_dir, 'zip', output_base_dir)
return summary_str, zip_path