Download rag/pipeline.py from PranilB/Question_Answering_Classification: direct link, hf CLI and curl.
- Browser
- Download file 9.14 kB
-
https://huggingface.co/spaces/PranilB/Question_Answering_Classification/resolve/main/rag/pipeline.py
- Command line
-
hf download hf://spaces/PranilB/Question_Answering_Classification/rag/pipeline.py
-
curl -L -o pipeline.py https://huggingface.co/spaces/PranilB/Question_Answering_Classification/resolve/main/rag/pipeline.py
9.14 kB
| """ | |
| pipeline.py β Main RAG orchestrator | |
| Ties together: | |
| 1. Retriever β get top-k NCERT chunks from Supabase | |
| 2. PromptBuilder β construct grounded LLM prompts | |
| 3. Generator β call Groq to answer / justify / verify | |
| 4. Confidence β produce a single confidence score | |
| Usage: | |
| from rag.pipeline import run_pipeline | |
| result = run_pipeline("Which organelle is the powerhouse of the cell?") | |
| print(result) | |
| """ | |
| from rag.retriever import retrieve_chunks | |
| from rag.prompt_builder import ( | |
| build_answer_prompt, | |
| build_justification_prompt, | |
| build_verifier_prompt, | |
| ) | |
| from rag.generator import generate_answer, generate_justification, verify_answer_llm | |
| from rag.confidence import compute_confidence | |
| from Classification.classify_and_extract import classify_question | |
| def run_pipeline( | |
| question: str, | |
| chapter_filter: str = None, | |
| top_k: int = 5, | |
| use_classifier: bool = False, | |
| ) -> dict: | |
| """ | |
| End-to-end RAG pipeline for a NEET Biology question. | |
| Args: | |
| question : The student's question string. | |
| chapter_filter : Optional chapter name to restrict retrieval scope. | |
| Auto-populated by BERT classifier if use_classifier=True. | |
| top_k : Number of NCERT chunks to retrieve. | |
| use_classifier : If True, auto-classify question to get chapter + topics. | |
| Returns a structured dict: | |
| { | |
| "question" : str, | |
| "classification" : {chapter, chapter_id, chapter_confidence, topics}, | |
| "answer" : str, | |
| "justification" : list[str], | |
| "sources" : list[{chapter, section, section_title, text_content, similarity}], | |
| "confidence" : float, | |
| "verified" : bool, | |
| "verifier_note" : str, | |
| } | |
| """ | |
| # ββ Step 0: BERT Classification + Topic Extraction ββββββββββββββββββββββ | |
| classification = None | |
| if use_classifier: | |
| print("[Pipeline] Step 0: BERT classification + topic extraction...") | |
| classification = classify_question(question) | |
| predicted_chapter = classification["chapter"] | |
| print(f"[Pipeline] Predicted chapter: {predicted_chapter} (Not used as filter)") | |
| # ββ Confidence gate: reject if BERT is too uncertain or OOD ββ | |
| if classification.get("rejected") or classification["chapter_confidence"] < 0.45: | |
| reject_reason = classification.get("rejection_reason") or f"BERT confidence ({classification['chapter_confidence']:.2%}) is below the 45% threshold." | |
| print(f"[Pipeline] β οΈ Classifier Rejected β {reject_reason}") | |
| return { | |
| "question" : question, | |
| "classification" : classification, | |
| "answer" : "This question does not appear to be related to NEET Biology.", | |
| "justification" : [], | |
| "sources" : [], | |
| "confidence" : 0.0, | |
| "verified" : False, | |
| "verifier_note" : reject_reason, | |
| "rejected" : True, | |
| } | |
| # ββ Step 1: Retrieve relevant NCERT chunks ββββββββββββββββββββββββββββββ | |
| print("[Pipeline] Step 1: Retrieving NCERT chunks (Global DB Search)...") | |
| chunks = retrieve_chunks(question, chapter_filter=None, top_k=top_k) | |
| if not chunks: | |
| return { | |
| "question" : question, | |
| "classification" : classification, | |
| "answer" : "Insufficient context: no relevant NCERT passages found.", | |
| "justification" : [], | |
| "sources" : [], | |
| "confidence" : 0.0, | |
| "verified" : False, | |
| "verifier_note" : "No chunks retrieved β cannot verify.", | |
| } | |
| # Extract top chapter/section for prompt context | |
| top_chunk = chunks[0] | |
| top_chapter = top_chunk.get("chapter", "Unknown Chapter") | |
| top_section = top_chunk.get("section_title", "Unknown Topic") | |
| # ββ Step 3: Generate answer + justification in a single Groq call ββββββ | |
| print("[Pipeline] Step 3: Generating answer + justification via Groq...") | |
| answer_msgs = build_answer_prompt(question, top_chapter, top_section, chunks) | |
| # Append instruction to also include justification in the same response | |
| answer_msgs[-1]["content"] += ( | |
| "\n\nAfter the answer, on a new line write '---JUSTIFICATION---' " | |
| "and then provide a numbered step-by-step biological justification " | |
| "for why the answer is correct, citing only the NCERT context above." | |
| ) | |
| raw_response = generate_answer(answer_msgs) | |
| # Parse answer and justification apart | |
| if "---JUSTIFICATION---" in raw_response: | |
| parts = raw_response.split("---JUSTIFICATION---", 1) | |
| answer = parts[0].strip() | |
| just_raw = parts[1].strip() | |
| else: | |
| answer = raw_response.strip() | |
| just_raw = "" | |
| # Parse justification into numbered steps | |
| if just_raw: | |
| lines = just_raw.strip().split("\n") | |
| justification = [l.strip() for l in lines if l.strip() and ( | |
| l.strip()[0].isdigit() or l.strip().lower().startswith("step") | |
| )] | |
| if not justification: | |
| justification = [l.strip() for l in lines if l.strip()] | |
| else: | |
| justification = [] | |
| # ββ Step 5: Verify answer (LLM Judge) βββββββββββββββββββββββββββββββββββ | |
| print("[Pipeline] Step 5: Verifying answer via LLM Judge...") | |
| verif_msgs = build_verifier_prompt(question, answer, chunks) | |
| verif_result = verify_answer_llm(verif_msgs) | |
| verified = verif_result["verified"] | |
| verifier_note = verif_result["explanation"] | |
| # ββ Step 6: Compute confidence ββββββββββββββββββββββββββββββββββββββββββ | |
| retrieval_scores = [c.get("similarity", 0.0) for c in chunks] | |
| confidence = compute_confidence(retrieval_scores, verified) | |
| # ββ Step 7: Build sources list (no raw embeddings) βββββββββββββββββββββ | |
| sources = [ | |
| { | |
| "chapter" : c.get("chapter"), | |
| "section" : c.get("section"), | |
| "section_title": c.get("section_title"), | |
| "text_content" : c.get("text_content"), | |
| "similarity" : round(c.get("similarity", 0.0), 4), | |
| } | |
| for c in chunks | |
| ] | |
| return { | |
| "question" : question, | |
| "classification" : classification, | |
| "answer" : answer, | |
| "justification" : justification, | |
| "sources" : sources, | |
| "confidence" : confidence, | |
| "verified" : verified, | |
| "verifier_note" : verifier_note, | |
| } | |
| if __name__ == "__main__": | |
| import json, sys | |
| # ββ Chapters currently in your Supabase DB ββββββββββββββββββββββββββββββ | |
| # Kebo101 β The Living World | |
| # Kebo109 β Biomolecules | |
| TEST_QUESTIONS = [ | |
| "What is the role of cofactors in enzyme activity?", # Biomolecules | |
| "What are primary and secondary metabolites in living organisms?", # Biomolecules | |
| "How does carbonic anhydrase speed up chemical reactions?", # Biomolecules | |
| "What is the difference between metabolism, anabolism and catabolism?", # Biomolecules | |
| "What is binomial nomenclature and who proposed it?", # The Living World | |
| ] | |
| question = TEST_QUESTIONS[0] # β change index (0-4) to test different questions | |
| debug = "--debug" in sys.argv # run with: python -m rag.pipeline --debug | |
| print(f"\n{'='*60}") | |
| print(f"QUESTION: {question}") | |
| print(f"{'='*60}\n") | |
| result = run_pipeline(question) | |
| if debug: | |
| # Show full retrieved context so you can verify what the LLM saw | |
| print("\nββ RETRIEVED CONTEXT ββββββββββββββββββββββββββββββββββββββ") | |
| for i, src in enumerate(result["sources"], 1): | |
| print(f"\n[{i}] ({src['similarity']:.3f}) {src['chapter']} > {src['section_title']}") | |
| print(f" {src['text_content'][:300]}...") | |
| print() | |
| print("ββ ANSWER βββββββββββββββββββββββββββββββββββββββββββββββββ") | |
| print(result["answer"]) | |
| print("\nββ JUSTIFICATION ββββββββββββββββββββββββββββββββββββββββββ") | |
| for step in result["justification"]: | |
| print(f" {step}") | |
| print(f"\nββ CONFIDENCE: {result['confidence']} | VERIFIED: {result['verified']} ββ") | |
| if result.get("verifier_note"): | |
| print(f" Verifier note: {result['verifier_note']}") | |