Mokhtar
feat(production): complete phase 1-4 refactor with hybrid retrieval CAG architecture, E5 dense embeddings, grounding validator, precomputed cold-start index, and 24/24 passing system tests
d54db1a Download scripts/run_audit_diagnostics.py from mokh2x/HNU-Final: direct link, hf CLI and curl.
- Browser
- Download file 19 kB
-
https://huggingface.co/spaces/mokh2x/HNU-Final/resolve/main/scripts/run_audit_diagnostics.py
- Command line
-
hf download hf://spaces/mokh2x/HNU-Final/scripts/run_audit_diagnostics.py
-
curl -L -o run_audit_diagnostics.py https://huggingface.co/spaces/mokh2x/HNU-Final/resolve/main/scripts/run_audit_diagnostics.py
19 kB
| # scripts/run_audit_diagnostics.py โ Comprehensive Audit & Verification Script | |
| import os | |
| import sys | |
| import json | |
| import time | |
| from pathlib import Path | |
| sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) | |
| from knowledge.loader import KnowledgeLoader | |
| from knowledge.chunker import MarkdownChunker | |
| from knowledge.index import HybridIndex | |
| from knowledge.retrieval import HybridRetriever | |
| from knowledge.context import ContextBuilder | |
| from routing.query_router import QueryRouter | |
| from routing.provider_router import ProviderRouter | |
| from policies.university_rules import PolicyEngine, COORDINATION_DISCLAIMER | |
| from policies.language import LanguageEngine | |
| from team.developers import DeveloperRegistry | |
| from cache.manager import CacheManager | |
| from cache.keys import HNU_KNOWLEDGE_VERSION | |
| BASE_DIR = Path(__file__).resolve().parent.parent | |
| DATA_DIR = BASE_DIR / "university_data_arabic" | |
| def audit_section_2_inventory(): | |
| print("\n==================================================") | |
| print("2. VERIFY DOCUMENT COVERAGE (INVENTORY)") | |
| print("==================================================") | |
| loader = KnowledgeLoader(data_dir=str(DATA_DIR)) | |
| docs = loader.load_documents() | |
| summary = loader.get_summary() | |
| md_files = list(DATA_DIR.rglob("*.md")) | |
| txt_files = list(DATA_DIR.rglob("*.txt")) | |
| other_files = [p for p in DATA_DIR.rglob("*") if p.is_file() and p.suffix not in [".md", ".txt"]] | |
| print(f"MD files found: {len(md_files)}") | |
| print(f"TXT files found: {len(txt_files)}") | |
| print(f"Other supported/ignored files: {len(other_files)}") | |
| print(f"Total source files: {len(md_files) + len(txt_files) + len(other_files)}") | |
| print(f"Total indexed: {summary['indexed_count']}") | |
| print(f"Total skipped: {summary['skipped_count']}") | |
| print(f"Reasons for skipped files: {summary['skipped_files']}") | |
| print("\nExact indexed filenames list:") | |
| indexed_names = sorted([d.metadata.filename for d in docs]) | |
| for idx, name in enumerate(indexed_names, 1): | |
| print(f" {idx:02d}. {name}") | |
| data_dir_files = list((DATA_DIR / "data").glob("*")) | |
| print(f"\nuniversity_data_arabic/data/ files count: {len(data_dir_files)}") | |
| assert "extracted_table.md" in indexed_names, "extracted_table.md MUST be indexed!" | |
| assert "website_developers.md" in indexed_names, "website_developers.md MUST be indexed!" | |
| print("โ Full recursive document coverage verified!") | |
| return docs, indexed_names | |
| def audit_section_3_chunking(docs): | |
| print("\n==================================================") | |
| print("3. VERIFY CHUNK COVERAGE") | |
| print("==================================================") | |
| chunker = MarkdownChunker() | |
| chunks = chunker.chunk_all(docs) | |
| chunk_sizes = [len(c.text) for c in chunks] | |
| avg_size = sum(chunk_sizes) / len(chunk_sizes) if chunk_sizes else 0 | |
| min_size = min(chunk_sizes) if chunk_sizes else 0 | |
| max_size = max(chunk_sizes) if chunk_sizes else 0 | |
| print(f"Total documents: {len(docs)}") | |
| print(f"Total chunks: {len(chunks)}") | |
| print(f"Average chunk size: {round(avg_size, 1)} chars") | |
| print(f"Minimum chunk size: {min_size} chars") | |
| print(f"Maximum chunk size: {max_size} chars") | |
| print("\nRandom Chunk Samples:") | |
| samples = [chunks[0], chunks[len(chunks)//2], chunks[-1]] | |
| for idx, sample in enumerate(samples, 1): | |
| print(f"\n--- Sample {idx} ---") | |
| print(f"Document: {sample.filename}") | |
| print(f"Section: {sample.section}") | |
| print(f"Heading Context: {sample.heading_context}") | |
| print(f"Chunk Text Preview:\n{sample.text[:200]}...") | |
| return chunks | |
| def audit_section_4_multi_fact(retriever): | |
| print("\n==================================================") | |
| print("4. TEST MULTI-FACT RETRIEVAL") | |
| print("==================================================") | |
| test_queries = [ | |
| ("A. single fact", "ู ุตุงุฑูู ูููุฉ ุทุจ ุงูุฃุณูุงู ูุงู ุ"), | |
| ("B. two facts from same doc", "ู ุตุงุฑูู ุทุจ ุงูุฃุณูุงู ูุงู ููู ูููุน ุชูุณูุทุ"), | |
| ("C. two facts from diff docs", "ู ุง ูู ู ุตุงุฑูู ุงูุทุจ ุงูุจุดุฑู ูู ุง ูู ุงูุฃูุฑุงู ุงูู ุทููุจุฉ ููุชูุฏูู ุ"), | |
| ("D. list questions", "ู ุง ูู ุงูุจุฑุงู ุฌ ุงูุฏุฑุงุณูุฉ ุงูู ุชุงุญุฉ ูู ูููุฉ ุงูููุฏุณุฉุ"), | |
| ("E. 'all' questions", "ูุงุช ูู ุงูู ุตุงุฑูู ููู ุงููููุงุช ุจุงููุงู ู"), | |
| ("F. comparison questions", "ูุงุฑู ุจูู ู ุตุงุฑูู ุทุจ ุงูุฃุณูุงู ูู ุตุงุฑูู ุงูููุฏุณุฉ"), | |
| ("G. procedural questions", "ู ุง ูู ุฎุทูุงุช ูุณุฏุงุฏ ุงูู ุตุฑููุงุช ุงูุฏุฑุงุณูุฉ ููุทูุงุจ ุงูุฌุฏุฏุ") | |
| ] | |
| for cat, q in test_queries: | |
| print(f"\nCategory: {cat}") | |
| print(f"Query: '{q}'") | |
| chunks, diag = retriever.retrieve(q, intent="multi_fact" if "all" in cat or "two" in cat else "general_information") | |
| unique_docs = set([c.filename for c in chunks]) | |
| print(f"Retrieved chunk IDs: {[c.chunk_id for c in chunks[:5]]}") | |
| print(f"Document names: {list(unique_docs)}") | |
| print(f"Sections: {list(set([c.section for c in chunks[:5]]))}") | |
| print(f"Unique relevant sources: {len(unique_docs)}") | |
| print(f"Latency: {diag['latency_ms']}ms") | |
| def audit_section_5_developers(): | |
| print("\n==================================================") | |
| print("5. TEST DEVELOPER ENTITY COMPLETENESS") | |
| print("==================================================") | |
| devs = DeveloperRegistry.get_all() | |
| print(f"Total developer entities: {len(devs)}") | |
| assert len(devs) == 7, "Must be exactly 7 developers!" | |
| for idx, d in enumerate(devs, 1): | |
| links = ", ".join([f"{c['type']}: {c['url']}" for c in d.contact_methods]) if d.contact_methods else "None" | |
| print(f" {idx}. {d.name} ({d.name_ar}) | Role: {d.role} | Contacts: {links}") | |
| test_dev_queries = [ | |
| "ู ูู ุงูู ุทูุฑูู ุงูุณุจุนุฉุ", | |
| "ู ูู ู ุณุคูู ุนู backendุ", | |
| "ู ูู ุนู ู ุงูุดุงุช ุจูุชุ", | |
| "ู ูู ู ุณุคูู ุนู deploymentุ", | |
| "ู ูู ู ุณุคูู ุนู frontendุ", | |
| "ุฅุฒุงู ุฃุชูุงุตู ู ุน Mohammed Mokhtarุ" | |
| ] | |
| print("\nDeveloper Queries Evaluation:") | |
| for q in test_dev_queries: | |
| if "ุงูุณุจุนุฉ" in q or "ุงูู ุทูุฑูู" in q and "ู ูู" in q: | |
| res = DeveloperRegistry.get_all() | |
| elif "Mokhtar" in q: | |
| res = DeveloperRegistry.search_by_name("Mokhtar") | |
| elif "backend" in q: | |
| res = DeveloperRegistry.search_by_responsibility("backend") | |
| elif "frontend" in q: | |
| res = DeveloperRegistry.search_by_responsibility("frontend") | |
| elif "deployment" in q or "cloud" in q: | |
| res = DeveloperRegistry.search_by_responsibility("cloud") | |
| elif "ุงูุดุงุช ุจูุช" in q or "chatbot" in q: | |
| res = DeveloperRegistry.search_by_responsibility("chatbot") | |
| else: | |
| res = DeveloperRegistry.get_all() | |
| dev_names = [d.name for d in res] | |
| print(f"Query: '{q}' -> Matched Devs: {dev_names}") | |
| def audit_section_6_coordination(): | |
| print("\n==================================================") | |
| print("6. TEST COORDINATION POLICY") | |
| print("==================================================") | |
| coord_queries = [ | |
| "ุงูุญุฏ ุงูุฃุฏูู ููููุฉ ุงูุทุจ ูุงู ุ", | |
| "ุทุจ ู ุญุชุงุฌ ู ุฌู ูุน ูุงู ุ", | |
| "What is the minimum score for medicine?", | |
| "ูู ุฏู ุชูุณูู ุฌุงู ุนุฉ ุญููุงู ุงูุฃูููุฉ ุงูุฏุงุฎููุ" | |
| ] | |
| for q in coord_queries: | |
| is_coord = PolicyEngine.is_coordination_query(q) | |
| formatted = PolicyEngine.apply_coordination_rules("ุจูุงู ุงูุชูุณูู ุงูุฎุงุต ุจุงูุทุจ ูู 80%.", q) | |
| disclaimer_present = COORDINATION_DISCLAIMER in formatted | |
| print(f"Query: '{q}' | Is Coord Query: {is_coord} | Disclaimer Present: {disclaimer_present}") | |
| def audit_section_7_language(): | |
| print("\n==================================================") | |
| print("7. TEST LANGUAGE BEHAVIOR") | |
| print("==================================================") | |
| lang_queries = [ | |
| "What are the fees for dentistry?", | |
| "ู ุง ูู ู ุตุฑููุงุช ูููุฉ ุทุจ ุงูุฃุณูุงูุ", | |
| "ู ุตุงุฑูู ุทุจ ุงูุฃุณูุงู ูุงู ุ", | |
| "ุงูู minimum score for medicine?" | |
| ] | |
| for q in lang_queries: | |
| detected = LanguageEngine.detect_language(q) | |
| print(f"Query: '{q}' -> Lang: {detected['language']} | Style: {detected['style']}") | |
| def audit_section_8_cache(): | |
| print("\n==================================================") | |
| print("8. CACHE AUDIT") | |
| print("==================================================") | |
| cache = CacheManager(cache_file_path=BASE_DIR / "cache" / "test_responses.json") | |
| # Store query | |
| q1 = "ู ุตุงุฑูู ูููุฉ ุงูุทุจ" | |
| q2_en = "What are the fees for medicine?" | |
| cache.put(q1, "ุฅุฌุงุจุฉ ุนุฑุจูุฉ", language="ar") | |
| cache.put(q2_en, "English response", language="en") | |
| hit_ar = cache.get(q1, language="ar") | |
| hit_en = cache.get(q2_en, language="en") | |
| print(f"Arabic Query Hit: {hit_ar is not None}") | |
| print(f"English Query Hit: {hit_en is not None}") | |
| print(f"Knowledge Version: {HNU_KNOWLEDGE_VERSION}") | |
| def build_section_11_evaluation_questions(): | |
| print("\n==================================================") | |
| print("11. BUILD REALISTIC EVALUATION SET (40 QUESTIONS)") | |
| print("==================================================") | |
| questions = [ | |
| # 10 Simple Factual | |
| {"question": "ุฃูู ุชูุน ุฌุงู ุนุฉ ุญููุงู ุงูุฃูููุฉุ", "category": "simple_factual", "expected_topics": ["location", "address"]}, | |
| {"question": "ู ุง ูู ุงูุจุฑูุฏ ุงูุฅููุชุฑููู ุงูุฑุณู ู ููุชูุงุตู ู ุน ุงูุฌุงู ุนุฉุ", "category": "simple_factual", "expected_topics": ["contact", "email"]}, | |
| {"question": "ู ุง ูู ุงูุฃูุฑุงู ุงูู ุทููุจุฉ ููุชูุฏูู ูู ุงูุฌุงู ุนุฉุ", "category": "simple_factual", "expected_topics": ["documents", "admission"]}, | |
| {"question": "ู ุง ูู ุฑุงุจุท ู ููุน ุงูุชูุฏูู ุงูุฅููุชุฑูููุ", "category": "simple_factual", "expected_topics": ["link", "admission"]}, | |
| {"question": "ูู ุชู ูุญ ุงูุฌุงู ุนุฉ ุฏุฑุฌุงุช ุนูู ูุฉ ู ุนุชู ุฏุฉุ", "category": "simple_factual", "expected_topics": ["accreditation"]}, | |
| {"question": "ู ุง ูู ูููุงุช ุงููุทุงุน ุงูุทุจู ูู ุงูุฌุงู ุนุฉุ", "category": "simple_factual", "expected_topics": ["faculties", "medicine"]}, | |
| {"question": "ู ุง ูู ูุธุงู ุงูุฏุฑุงุณุฉ ุจุฌุงู ุนุฉ ุญููุงู ุงูุฃูููุฉุ", "category": "simple_factual", "expected_topics": ["credit_hours"]}, | |
| {"question": "ู ุง ูู ุงูุฃูุฑุงู ุงูู ุทููุจุฉ ููุทูุงุจ ุงููุงูุฏููุ", "category": "simple_factual", "expected_topics": ["foreign_students"]}, | |
| {"question": "ูู ุชุชููุฑ ูุณุงุฆู ู ูุงุตูุงุช ููุฌุงู ุนุฉุ", "category": "simple_factual", "expected_topics": ["transportation"]}, | |
| {"question": "ู ุง ูู ุงูุณุนุฉ ุงูุทูุงุจูุฉ ููููุฉ ุงูููุฏุณุฉุ", "category": "simple_factual", "expected_topics": ["capacity"]}, | |
| # 8 Multi-fact | |
| {"question": "ู ุง ูู ู ุตุงุฑูู ูููุฉ ุทุจ ุงูุฃุณูุงู ููู ูููุน ุชูุณูุทุ", "category": "multi_fact", "expected_topics": ["tuition", "installment"]}, | |
| {"question": "ู ุง ูู ุดุฑูุท ุงูุชูุฏูู ูู ุง ุงูุฃูุฑุงู ุงูู ุทููุจุฉ ููุทูุงุจ ุงูุฌุฏุฏุ", "category": "multi_fact", "expected_topics": ["conditions", "documents"]}, | |
| {"question": "ู ุง ูู ู ุตุงุฑูู ุงูููุฏุณุฉ ูุงูุญุฏ ุงูุฃุฏูู ููุชูุณููุ", "category": "multi_fact", "expected_topics": ["tuition", "coordination"]}, | |
| {"question": "ู ุง ูู ุจุฑุงู ุฌ ูููุฉ ุนููู ุงูุญุงุณุจ ูู ุง ูู ู ุตุฑููุงุชูุงุ", "category": "multi_fact", "expected_topics": ["programs", "tuition"]}, | |
| {"question": "ู ุง ูู ุฎุทูุงุช ูุณุฏุงุฏ ุงูู ุตุฑููุงุช ูููููุฉ ุชุฃููุฏ ุงูุชูุฏูู ุ", "category": "multi_fact", "expected_topics": ["payment", "confirmation"]}, | |
| {"question": "ู ุง ูู ุงูู ูุญ ุงูู ุชุงุญุฉ ูู ุง ูู ูุณุจุฉ ุงูุฎุตู ููุทูุงุจ ุงูู ุชูููููุ", "category": "multi_fact", "expected_topics": ["scholarships", "discounts"]}, | |
| {"question": "ู ุง ูู ู ุตุงุฑูู ุงูุทุจ ุงูุจุดุฑู ูููููุฉ ุงูุชูุงุตู ู ุน ุดุคูู ุงูุทูุงุจุ", "category": "multi_fact", "expected_topics": ["medicine_tuition", "student_affairs"]}, | |
| {"question": "ู ุง ูู ุงููููุงุช ุงูู ุชุงุญุฉ ูู ุง ูู ู ุตุฑููุงุช ูู ูููุฉุ", "category": "multi_fact", "expected_topics": ["faculties", "all_tuition"]}, | |
| # 5 Tuition | |
| {"question": "ู ุตุงุฑูู ูููุฉ ุงูููุฏุณุฉ ูุงู ุ", "category": "tuition", "expected_topics": ["engineering_tuition"]}, | |
| {"question": "ู ุตุฑููุงุช ูููุฉ ุงูุนูุงุฌ ุงูุทุจูุนู ูุงู ุ", "category": "tuition", "expected_topics": ["physical_therapy_tuition"]}, | |
| {"question": "ูู ุชูููุฉ ุฏุฑุงุณุฉ ุนููู ุงูุญุงุณุจ ูุชูููููุฌูุง ุงูู ุนููู ุงุชุ", "category": "tuition", "expected_topics": ["cs_tuition"]}, | |
| {"question": "ู ุง ูู ู ุตุงุฑูู ูููุฉ ุงูุทุจ ูุงูุฌุฑุงุญุฉุ", "category": "tuition", "expected_topics": ["medicine_tuition"]}, | |
| {"question": "ู ุง ูู ู ุตุงุฑูู ูููุฉ ุงูุนููู ุ", "category": "tuition", "expected_topics": ["science_tuition"]}, | |
| # 5 Admission/Coordination | |
| {"question": "ุงูุญุฏ ุงูุฃุฏูู ููููุฉ ุงูุทุจ ูุงู ุ", "category": "admission_coordination", "expected_topics": ["medicine_minimum"]}, | |
| {"question": "ุชูุณูู ูููุฉ ุงูููุฏุณุฉ ูู ุจุงูู ุฆุฉุ", "category": "admission_coordination", "expected_topics": ["engineering_coordination"]}, | |
| {"question": "ู ุง ูู ุงูุญุฏ ุงูุฃุฏูู ูููุจูู ุจูููุงุช ุงููุทุงุน ุงูุทุจูุ", "category": "admission_coordination", "expected_topics": ["medical_sector_minimum"]}, | |
| {"question": "ู ุง ูู ุชูุณูู ูููุฉ ุนูุงุฌ ุทุจูุนูุ", "category": "admission_coordination", "expected_topics": ["physical_therapy_minimum"]}, | |
| {"question": "ูู ุชูุณูู ุงูุฌุงู ุนุฉ ุงูุฃูููุฉ ูุฎุชูู ุนู ุงูุชูุณูู ุงูุญููู ูุ", "category": "admission_coordination", "expected_topics": ["coordination_disclaimer"]}, | |
| # 5 Developer/Team | |
| {"question": "ู ูู ุงูู ุทูุฑูู ุงูุณุจุนุฉ ุจุชูุน ุงูู ููุน ุงูุฑุณู ูููุ", "category": "developer_team", "expected_topics": ["7_developers"]}, | |
| {"question": "ู ูู ู ุณุคูู ุนู ุชุทููุฑ ุงูู backend ูุงูุดุงุช ุจูุชุ", "category": "developer_team", "expected_topics": ["backend_chatbot_dev"]}, | |
| {"question": "ู ูู ู ุณุคูู ุนู ุชุตู ูู ูุชุทููุฑ ุงูู frontendุ", "category": "developer_team", "expected_topics": ["frontend_dev"]}, | |
| {"question": "ู ูู ู ุณุคูู ุนู ุงูุณูุฑูุฑุงุช ุงูู Cloud ูุงูู deploymentุ", "category": "developer_team", "expected_topics": ["cloud_deployment_dev"]}, | |
| {"question": "ุฅุฒุงู ุฃุชูุงุตู ู ุน ุงูู ุทูุฑ Mohammed Mokhtarุ", "category": "developer_team", "expected_topics": ["mokhtar_contact"]}, | |
| # 4 English | |
| {"question": "What are the fees for dentistry at Helwan National University?", "category": "english", "expected_topics": ["dentistry_tuition"]}, | |
| {"question": "What is the minimum score required for medicine?", "category": "english", "expected_topics": ["medicine_minimum"]}, | |
| {"question": "Where is the university located and how to reach it?", "category": "english", "expected_topics": ["location"]}, | |
| {"question": "Who are the developers of the official website?", "category": "english", "expected_topics": ["developers"]}, | |
| # 3 Egyptian Arabic | |
| {"question": "ู ุตุงุฑูู ุฃุณูุงู ูุงู ููู ูููุน ุงุฏูุน ุนูู ุฃูุณุงุทุ", "category": "egyptian_arabic", "expected_topics": ["dentistry_tuition", "installment"]}, | |
| {"question": "ูู ุงูุทุจ ุงูุจุดุฑู ุจูุงุฎุฏ ู ู ู ุฌู ูุน ูุงู ุ", "category": "egyptian_arabic", "expected_topics": ["medicine_minimum"]}, | |
| {"question": "ุงุฒุงู ุงุฑูุญ ุงูุฌุงู ุนุฉ ุจุงูู ูุงุตูุงุชุ", "category": "egyptian_arabic", "expected_topics": ["transportation"]} | |
| ] | |
| eval_file = BASE_DIR / "tests" / "evaluation_questions.json" | |
| eval_file.parent.mkdir(parents=True, exist_ok=True) | |
| with open(eval_file, "w", encoding="utf-8") as f: | |
| json.dump(questions, f, ensure_ascii=False, indent=2) | |
| print(f"โ Saved {len(questions)} evaluation questions to {eval_file}.") | |
| return questions | |
| def audit_section_12_retrieval_diagnostics(retriever, questions): | |
| print("\n==================================================") | |
| print("12. RETRIEVAL DIAGNOSTICS LOGGING") | |
| print("==================================================") | |
| router = QueryRouter() | |
| context_bld = ContextBuilder() | |
| diagnostics_log = [] | |
| for item in questions: | |
| q = item["question"] | |
| route = router.classify_query(q) | |
| lang_info = LanguageEngine.detect_language(q) | |
| start_time = time.time() | |
| chunks, diag = retriever.retrieve(q, intent=route["intent"], filter_filename=route["filter_filename"]) | |
| latency_ms = diag["latency_ms"] | |
| context_str = context_bld.build_context(chunks) | |
| log_entry = { | |
| "question": q, | |
| "category": item["category"], | |
| "intent": route["intent"], | |
| "language": lang_info["language"], | |
| "retrieval_latency_ms": latency_ms, | |
| "retrieved_documents": list(set([c.filename for c in chunks])), | |
| "retrieved_sections": list(set([c.section for c in chunks])), | |
| "retrieved_chunk_ids": [c.chunk_id for c in chunks], | |
| "context_size_chars": len(context_str) | |
| } | |
| diagnostics_log.append(log_entry) | |
| log_file = BASE_DIR / "tests" / "diagnostics_output.json" | |
| with open(log_file, "w", encoding="utf-8") as f: | |
| json.dump(diagnostics_log, f, ensure_ascii=False, indent=2) | |
| print(f"โ Diagnostics logged for {len(diagnostics_log)} questions to {log_file}.") | |
| def main(): | |
| docs, filenames = audit_section_2_inventory() | |
| chunks = audit_section_3_chunking(docs) | |
| index = HybridIndex() | |
| index.build_index(chunks, enable_sentence_transformers=False) | |
| retriever = HybridRetriever(index=index) | |
| audit_section_4_multi_fact(retriever) | |
| audit_section_5_developers() | |
| audit_section_6_coordination() | |
| audit_section_7_language() | |
| audit_section_8_cache() | |
| questions = build_section_11_evaluation_questions() | |
| audit_section_12_retrieval_diagnostics(retriever, questions) | |
| if __name__ == "__main__": | |
| main() | |