| import numpy as np |
| import faiss, torch, gradio as gr |
| from sentence_transformers import SentenceTransformer |
| from transformers import AutoTokenizer, AutoModelForCausalLM |
|
|
| DOCS = [ |
| "The Generative AI Summer Bootcamp at Najran University runs for three weeks.", |
| "Week 1 covers the foundations of generative AI and prompt engineering.", |
| "Week 2 focuses on large language models, embeddings, and retrieval-augmented generation.", |
| "Week 3 covers multimodal AI: vision, audio, and image generation.", |
| "The bootcamp prepares students for the NVIDIA NCA-GENL and NCA-GENM associate exams.", |
| "The NCA-GENL exam has 50 to 60 questions and a time limit of one hour.", |
| "Students need a free Google Colab account and a free Hugging Face account.", |
| "The refund policy: tuition is fully refundable up to seven days before the start date.", |
| "All lab notebooks run on a free Colab T4 GPU with about 15 GB of memory.", |
| "Certificates are issued to students who complete every hands-on lab.", |
| ] |
|
|
| embedder = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2") |
| emb = np.asarray(embedder.encode(DOCS, normalize_embeddings=True), dtype="float32") |
| index = faiss.IndexFlatIP(emb.shape[1]); index.add(emb) |
|
|
| GEN_ID = "Qwen/Qwen2.5-1.5B-Instruct" |
| tokenizer = AutoTokenizer.from_pretrained(GEN_ID) |
| model = AutoModelForCausalLM.from_pretrained(GEN_ID, torch_dtype=torch.float32) |
|
|
| def retrieve(query, k=3): |
| q = np.asarray(embedder.encode([query], normalize_embeddings=True), dtype="float32") |
| scores, idxs = index.search(q, k) |
| return [(DOCS[i], float(s)) for i, s in zip(idxs[0], scores[0])] |
|
|
| def generate(prompt, max_new_tokens=256): |
| msgs = [{"role": "user", "content": prompt}] |
| inp = tokenizer.apply_chat_template(msgs, add_generation_prompt=True, return_tensors="pt") |
| out = model.generate(inp, max_new_tokens=max_new_tokens, do_sample=False, |
| pad_token_id=tokenizer.eos_token_id) |
| return tokenizer.decode(out[0][inp.shape[1]:], skip_special_tokens=True).strip() |
|
|
| def rag_answer(question, k=3, min_score=0.15): |
| chunks = retrieve(question, k=k) |
| if not chunks or chunks[0][1] < min_score: |
| return "I dont know. (No relevant context was found.)", [] |
| ctx = "\n".join(f"[{i+1}] {t}" for i, (t, _) in enumerate(chunks)) |
| prompt = ("Answer using ONLY the context below. If the answer is not in the context, " |
| "say: I dont know. Cite sources like [1], [2].\n\n" |
| f"Context:\n{ctx}\n\nQuestion: {question}\n\nAnswer:") |
| return generate(prompt), [t for t, _ in chunks] |
|
|
| def chat_fn(question): |
| answer, sources = rag_answer(question) |
| srcs = "\n".join(f"{i}. {s}" for i, s in enumerate(sources, 1)) or "(none)" |
| return answer, srcs |
|
|
| demo = gr.Interface( |
| fn=chat_fn, |
| inputs=gr.Textbox(label="Ask about the bootcamp"), |
| outputs=[gr.Textbox(label="Answer"), gr.Textbox(label="Sources")], |
| title="Najran Bootcamp RAG Chatbot", |
| ) |
|
|
| if __name__ == "__main__": |
| demo.launch() |
|
|