Instructions to use nsr51324/CortexRAG with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- sentence-transformers
How to use nsr51324/CortexRAG with sentence-transformers:
from sentence_transformers import CrossEncoder model = CrossEncoder("nsr51324/CortexRAG") query = "Which planet is known as the Red Planet?" passages = [ "Venus is often called Earth's twin because of its similar size and proximity.", "Mars, known for its reddish appearance, is often referred to as the Red Planet.", "Jupiter, the largest planet in our solar system, has a prominent red spot.", "Saturn, famous for its rings, is sometimes mistaken for the Red Planet." ] scores = model.predict([(query, passage) for passage in passages]) print(scores) - Notebooks
- Google Colab
- Kaggle
Create README.md
Browse files
README.md
ADDED
|
@@ -0,0 +1,1196 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
language:
|
| 3 |
+
- en
|
| 4 |
+
library_name: sentence-transformers
|
| 5 |
+
tags:
|
| 6 |
+
- rag
|
| 7 |
+
- retrieval-augmented-generation
|
| 8 |
+
- medical
|
| 9 |
+
- question-answering
|
| 10 |
+
- semantic-search
|
| 11 |
+
- faiss
|
| 12 |
+
- cross-encoder
|
| 13 |
+
- medical-qa
|
| 14 |
+
pipeline_tag: question-answering
|
| 15 |
+
license: apache-2.0
|
| 16 |
+
---
|
| 17 |
+
|
| 18 |
+
# Medical RAG System
|
| 19 |
+
|
| 20 |
+
A retrieval-augmented generation (RAG) system for answering medical questions using a curated English medical Question-Answer knowledge base.
|
| 21 |
+
|
| 22 |
+
The system combines:
|
| 23 |
+
|
| 24 |
+
- Semantic retrieval using Sentence Transformers
|
| 25 |
+
- FAISS vector search
|
| 26 |
+
- Medical query expansion
|
| 27 |
+
- Cross-Encoder reranking
|
| 28 |
+
- Evidence deduplication
|
| 29 |
+
- Confidence gating
|
| 30 |
+
- LLM-based answer generation
|
| 31 |
+
- Retrieval evaluation
|
| 32 |
+
- Confidence-gate evaluation
|
| 33 |
+
- Latency measurement
|
| 34 |
+
|
| 35 |
+
> **Important:** This system is intended for research and educational purposes. It is not a medical diagnostic system and should not be used as a substitute for professional medical advice.
|
| 36 |
+
|
| 37 |
+
---
|
| 38 |
+
|
| 39 |
+
# 1. Overview
|
| 40 |
+
|
| 41 |
+
This project implements a complete Retrieval-Augmented Generation pipeline for medical Question-Answer data.
|
| 42 |
+
|
| 43 |
+
Instead of asking an LLM to answer directly from its internal knowledge, the system first retrieves relevant medical evidence from a local knowledge base.
|
| 44 |
+
|
| 45 |
+
The pipeline is:
|
| 46 |
+
|
| 47 |
+
```text
|
| 48 |
+
User Question
|
| 49 |
+
│
|
| 50 |
+
▼
|
| 51 |
+
Query Expansion
|
| 52 |
+
│
|
| 53 |
+
▼
|
| 54 |
+
Sentence Transformer Embedding
|
| 55 |
+
│
|
| 56 |
+
▼
|
| 57 |
+
FAISS Vector Search
|
| 58 |
+
│
|
| 59 |
+
▼
|
| 60 |
+
Top-N Candidate Documents
|
| 61 |
+
│
|
| 62 |
+
▼
|
| 63 |
+
Cross-Encoder Reranking
|
| 64 |
+
│
|
| 65 |
+
▼
|
| 66 |
+
Top-K Evidence
|
| 67 |
+
│
|
| 68 |
+
▼
|
| 69 |
+
Near-Duplicate Removal
|
| 70 |
+
│
|
| 71 |
+
▼
|
| 72 |
+
Confidence Gate
|
| 73 |
+
│
|
| 74 |
+
├── Reject → Insufficient Evidence
|
| 75 |
+
│
|
| 76 |
+
▼
|
| 77 |
+
LLM Generation
|
| 78 |
+
│
|
| 79 |
+
▼
|
| 80 |
+
Evidence-Based Answer
|
| 81 |
+
```
|
| 82 |
+
|
| 83 |
+
---
|
| 84 |
+
|
| 85 |
+
# 2. Knowledge Base
|
| 86 |
+
|
| 87 |
+
The knowledge base contains approximately:
|
| 88 |
+
|
| 89 |
+
```text
|
| 90 |
+
16,384 medical Question-Answer records
|
| 91 |
+
```
|
| 92 |
+
|
| 93 |
+
The dataset contains medical questions and corresponding answers, together with categorical information.
|
| 94 |
+
|
| 95 |
+
Main columns used by the system:
|
| 96 |
+
|
| 97 |
+
```text
|
| 98 |
+
Question
|
| 99 |
+
Answer
|
| 100 |
+
Category
|
| 101 |
+
doc_id
|
| 102 |
+
```
|
| 103 |
+
|
| 104 |
+
A unique `doc_id` is assigned to every document:
|
| 105 |
+
|
| 106 |
+
```python
|
| 107 |
+
df = df.reset_index(drop=True)
|
| 108 |
+
df["doc_id"] = df.index
|
| 109 |
+
```
|
| 110 |
+
|
| 111 |
+
The `doc_id` is later used to identify evidence sources.
|
| 112 |
+
|
| 113 |
+
---
|
| 114 |
+
|
| 115 |
+
# 3. Embedding Model
|
| 116 |
+
|
| 117 |
+
The system uses:
|
| 118 |
+
|
| 119 |
+
```text
|
| 120 |
+
sentence-transformers/all-MiniLM-L6-v2
|
| 121 |
+
```
|
| 122 |
+
|
| 123 |
+
The model converts every medical question into a dense vector representation.
|
| 124 |
+
|
| 125 |
+
Embeddings are normalized:
|
| 126 |
+
|
| 127 |
+
```python
|
| 128 |
+
question_embeddings = embedder.encode(
|
| 129 |
+
questions,
|
| 130 |
+
batch_size=64,
|
| 131 |
+
show_progress_bar=True,
|
| 132 |
+
convert_to_numpy=True,
|
| 133 |
+
normalize_embeddings=True,
|
| 134 |
+
)
|
| 135 |
+
```
|
| 136 |
+
|
| 137 |
+
Normalization allows the FAISS Inner Product index to behave as cosine similarity search.
|
| 138 |
+
|
| 139 |
+
The resulting embedding matrix is stored as:
|
| 140 |
+
|
| 141 |
+
```text
|
| 142 |
+
question_embeddings.npy
|
| 143 |
+
```
|
| 144 |
+
|
| 145 |
+
---
|
| 146 |
+
|
| 147 |
+
# 4. FAISS Retrieval
|
| 148 |
+
|
| 149 |
+
FAISS is used for efficient vector similarity search.
|
| 150 |
+
|
| 151 |
+
The index is created using:
|
| 152 |
+
|
| 153 |
+
```python
|
| 154 |
+
faiss.IndexFlatIP(embedding_dim)
|
| 155 |
+
```
|
| 156 |
+
|
| 157 |
+
Because the embeddings are normalized, Inner Product corresponds to cosine similarity.
|
| 158 |
+
|
| 159 |
+
The FAISS index contains:
|
| 160 |
+
|
| 161 |
+
```text
|
| 162 |
+
16,384 vectors
|
| 163 |
+
```
|
| 164 |
+
|
| 165 |
+
The index is saved as:
|
| 166 |
+
|
| 167 |
+
```text
|
| 168 |
+
questions.index
|
| 169 |
+
```
|
| 170 |
+
|
| 171 |
+
Retrieval works by:
|
| 172 |
+
|
| 173 |
+
1. Embedding the user query
|
| 174 |
+
2. Searching the FAISS index
|
| 175 |
+
3. Returning the top-N most similar questions
|
| 176 |
+
|
| 177 |
+
Default retrieval configuration:
|
| 178 |
+
|
| 179 |
+
```python
|
| 180 |
+
RETRIEVE_TOP_N = 20
|
| 181 |
+
```
|
| 182 |
+
|
| 183 |
+
---
|
| 184 |
+
|
| 185 |
+
# 5. Query Expansion
|
| 186 |
+
|
| 187 |
+
The system includes a lightweight medical query expansion layer.
|
| 188 |
+
|
| 189 |
+
This helps bridge the gap between everyday language and medical terminology.
|
| 190 |
+
|
| 191 |
+
Examples:
|
| 192 |
+
|
| 193 |
+
```text
|
| 194 |
+
underactive thyroid
|
| 195 |
+
↓
|
| 196 |
+
hypothyroidism
|
| 197 |
+
|
| 198 |
+
overactive thyroid
|
| 199 |
+
↓
|
| 200 |
+
hyperthyroidism
|
| 201 |
+
|
| 202 |
+
high blood sugar
|
| 203 |
+
↓
|
| 204 |
+
hyperglycemia
|
| 205 |
+
|
| 206 |
+
low blood sugar
|
| 207 |
+
↓
|
| 208 |
+
hypoglycemia
|
| 209 |
+
|
| 210 |
+
sugar disease
|
| 211 |
+
↓
|
| 212 |
+
diabetes
|
| 213 |
+
```
|
| 214 |
+
|
| 215 |
+
For example:
|
| 216 |
+
|
| 217 |
+
```text
|
| 218 |
+
I suffer from an underactive thyroid gland
|
| 219 |
+
```
|
| 220 |
+
|
| 221 |
+
becomes:
|
| 222 |
+
|
| 223 |
+
```text
|
| 224 |
+
I suffer from an underactive thyroid gland
|
| 225 |
+
(hypothyroidism)
|
| 226 |
+
```
|
| 227 |
+
|
| 228 |
+
The original query is not replaced.
|
| 229 |
+
|
| 230 |
+
The medical term is simply appended before generating the embedding.
|
| 231 |
+
|
| 232 |
+
---
|
| 233 |
+
|
| 234 |
+
# 6. Cross-Encoder Reranking
|
| 235 |
+
|
| 236 |
+
After FAISS retrieves the initial candidates, a Cross-Encoder is used to rerank them.
|
| 237 |
+
|
| 238 |
+
Model:
|
| 239 |
+
|
| 240 |
+
```text
|
| 241 |
+
cross-encoder/ms-marco-MiniLM-L-6-v2
|
| 242 |
+
```
|
| 243 |
+
|
| 244 |
+
The system retrieves:
|
| 245 |
+
|
| 246 |
+
```text
|
| 247 |
+
Top 20
|
| 248 |
+
```
|
| 249 |
+
|
| 250 |
+
candidates and reranks them to keep:
|
| 251 |
+
|
| 252 |
+
```text
|
| 253 |
+
Top 6
|
| 254 |
+
```
|
| 255 |
+
|
| 256 |
+
Configuration:
|
| 257 |
+
|
| 258 |
+
```python
|
| 259 |
+
RETRIEVE_TOP_N = 20
|
| 260 |
+
RERANK_TOP_K = 6
|
| 261 |
+
```
|
| 262 |
+
|
| 263 |
+
The reranker scores:
|
| 264 |
+
|
| 265 |
+
```text
|
| 266 |
+
(query, Answer)
|
| 267 |
+
```
|
| 268 |
+
|
| 269 |
+
rather than:
|
| 270 |
+
|
| 271 |
+
```text
|
| 272 |
+
(query, Question)
|
| 273 |
+
```
|
| 274 |
+
|
| 275 |
+
This is important because the dataset contains multiple records with the same Question but different medical answers.
|
| 276 |
+
|
| 277 |
+
Reranking the Answer therefore helps determine which evidence is actually useful for answering the user's question.
|
| 278 |
+
|
| 279 |
+
---
|
| 280 |
+
|
| 281 |
+
# 7. Evidence Deduplication
|
| 282 |
+
|
| 283 |
+
Medical datasets can contain multiple answers that are almost identical.
|
| 284 |
+
|
| 285 |
+
The system removes near-duplicate answers using:
|
| 286 |
+
|
| 287 |
+
```python
|
| 288 |
+
difflib.SequenceMatcher
|
| 289 |
+
```
|
| 290 |
+
|
| 291 |
+
with:
|
| 292 |
+
|
| 293 |
+
```python
|
| 294 |
+
threshold = 0.92
|
| 295 |
+
```
|
| 296 |
+
|
| 297 |
+
This prevents the LLM from receiving multiple copies of essentially the same evidence.
|
| 298 |
+
|
| 299 |
+
Each evidence item contains:
|
| 300 |
+
|
| 301 |
+
```text
|
| 302 |
+
doc_id
|
| 303 |
+
question
|
| 304 |
+
answer
|
| 305 |
+
category
|
| 306 |
+
similarity
|
| 307 |
+
rerank_score
|
| 308 |
+
```
|
| 309 |
+
|
| 310 |
+
---
|
| 311 |
+
|
| 312 |
+
# 8. Confidence Gate
|
| 313 |
+
|
| 314 |
+
The system does not automatically answer every question.
|
| 315 |
+
|
| 316 |
+
Before sending evidence to the LLM, a confidence gate checks whether the retrieved evidence is strong enough.
|
| 317 |
+
|
| 318 |
+
Current configuration:
|
| 319 |
+
|
| 320 |
+
```python
|
| 321 |
+
MIN_RERANK_SCORE = -8
|
| 322 |
+
MIN_SIMILARITY_FLOOR = 0.40
|
| 323 |
+
MIN_SUPPORT_COUNT = 2
|
| 324 |
+
SUPPORT_SCORE = -5.0
|
| 325 |
+
```
|
| 326 |
+
|
| 327 |
+
The system accepts the evidence only if:
|
| 328 |
+
|
| 329 |
+
1. At least one result exists.
|
| 330 |
+
2. The highest rerank score is above the minimum threshold.
|
| 331 |
+
3. The similarity score is above the minimum similarity floor.
|
| 332 |
+
4. At least two retrieved documents provide sufficient support.
|
| 333 |
+
|
| 334 |
+
Otherwise, the system refuses to generate an evidence-based answer.
|
| 335 |
+
|
| 336 |
+
Example:
|
| 337 |
+
|
| 338 |
+
```text
|
| 339 |
+
Question:
|
| 340 |
+
Why do I feel short of breath and tired when I climb stairs?
|
| 341 |
+
|
| 342 |
+
Result:
|
| 343 |
+
No sufficiently relevant evidence.
|
| 344 |
+
|
| 345 |
+
System:
|
| 346 |
+
I couldn't find sufficiently relevant medical evidence
|
| 347 |
+
in the knowledge base to answer this question.
|
| 348 |
+
```
|
| 349 |
+
|
| 350 |
+
This behavior is important for reducing unsupported answers.
|
| 351 |
+
|
| 352 |
+
---
|
| 353 |
+
|
| 354 |
+
# 9. LLM Generation
|
| 355 |
+
|
| 356 |
+
The generation stage uses a Groq-hosted LLM.
|
| 357 |
+
|
| 358 |
+
Current model used during development:
|
| 359 |
+
|
| 360 |
+
```text
|
| 361 |
+
openai/gpt-oss-20b
|
| 362 |
+
```
|
| 363 |
+
|
| 364 |
+
The LLM is explicitly instructed to operate in RAG mode.
|
| 365 |
+
|
| 366 |
+
Main generation rules:
|
| 367 |
+
|
| 368 |
+
```text
|
| 369 |
+
1. Use only retrieved evidence.
|
| 370 |
+
2. Do not add outside medical knowledge.
|
| 371 |
+
3. Do not guess.
|
| 372 |
+
4. Cite evidence using [doc_id].
|
| 373 |
+
5. Mention when evidence is insufficient.
|
| 374 |
+
6. Do not diagnose users.
|
| 375 |
+
7. Do not prescribe personalized treatment or dosages.
|
| 376 |
+
8. Mention disagreements between sources.
|
| 377 |
+
9. Keep answers concise and organized.
|
| 378 |
+
10. Include a medical-information disclaimer.
|
| 379 |
+
```
|
| 380 |
+
|
| 381 |
+
The API key is NOT stored in this repository.
|
| 382 |
+
|
| 383 |
+
It should be provided through an environment variable:
|
| 384 |
+
|
| 385 |
+
```python
|
| 386 |
+
import os
|
| 387 |
+
|
| 388 |
+
GROQ_API_KEY = os.environ["GROQ_API_KEY"]
|
| 389 |
+
```
|
| 390 |
+
|
| 391 |
+
---
|
| 392 |
+
|
| 393 |
+
# 10. Example
|
| 394 |
+
|
| 395 |
+
## Input
|
| 396 |
+
|
| 397 |
+
```text
|
| 398 |
+
What signs might suggest that my thyroid is not producing enough hormones?
|
| 399 |
+
```
|
| 400 |
+
|
| 401 |
+
## Retrieved Evidence
|
| 402 |
+
|
| 403 |
+
```text
|
| 404 |
+
[15967] Thyroid symptoms
|
| 405 |
+
[13504] Thyroid symptoms
|
| 406 |
+
[10676] Thyroid symptoms
|
| 407 |
+
[13989] What are the symptoms of thyroid disease?
|
| 408 |
+
[13559] Symptoms that indicate a disorder in the thyroid gland
|
| 409 |
+
[12173] How do I know that I have a thyroid problem?
|
| 410 |
+
```
|
| 411 |
+
|
| 412 |
+
## Generated Answer
|
| 413 |
+
|
| 414 |
+
The system generated an evidence-based response describing symptoms associated with low thyroid hormone production, including:
|
| 415 |
+
|
| 416 |
+
- Fatigue
|
| 417 |
+
- Feeling cold
|
| 418 |
+
- Weight gain
|
| 419 |
+
- Constipation
|
| 420 |
+
- Menstrual changes
|
| 421 |
+
- Hair loss
|
| 422 |
+
|
| 423 |
+
The final response also included the required medical disclaimer.
|
| 424 |
+
|
| 425 |
+
---
|
| 426 |
+
|
| 427 |
+
# 11. Example of Out-of-Domain Query
|
| 428 |
+
|
| 429 |
+
## Input
|
| 430 |
+
|
| 431 |
+
```text
|
| 432 |
+
What is the best treatment for a broken leg?
|
| 433 |
+
```
|
| 434 |
+
|
| 435 |
+
The retriever returned some medically related documents, mostly concerning diabetic foot injuries.
|
| 436 |
+
|
| 437 |
+
However, those documents did not contain appropriate evidence about treating fractures.
|
| 438 |
+
|
| 439 |
+
The final system response therefore stated that the available evidence did not contain information about treating a broken leg.
|
| 440 |
+
|
| 441 |
+
This demonstrates an important property of the system:
|
| 442 |
+
|
| 443 |
+
> Retrieval similarity alone does not guarantee that the retrieved evidence is appropriate for the question.
|
| 444 |
+
|
| 445 |
+
---
|
| 446 |
+
|
| 447 |
+
# 12. Evaluation
|
| 448 |
+
|
| 449 |
+
The system was evaluated using multiple complementary evaluation methods.
|
| 450 |
+
|
| 451 |
+
These include:
|
| 452 |
+
|
| 453 |
+
- Self-retrieval accuracy
|
| 454 |
+
- Recall@K
|
| 455 |
+
- Precision@K
|
| 456 |
+
- F1@K
|
| 457 |
+
- MRR
|
| 458 |
+
- NDCG@K
|
| 459 |
+
- Category sanity check
|
| 460 |
+
- Confidence-gate accuracy
|
| 461 |
+
- Cross-validation threshold tuning
|
| 462 |
+
- Latency evaluation
|
| 463 |
+
|
| 464 |
+
---
|
| 465 |
+
|
| 466 |
+
# 13. Self-Retrieval Evaluation
|
| 467 |
+
|
| 468 |
+
A sample of 50 questions was tested.
|
| 469 |
+
|
| 470 |
+
Result:
|
| 471 |
+
|
| 472 |
+
```text
|
| 473 |
+
Self-retrieval Top-1 Accuracy:
|
| 474 |
+
50 / 50
|
| 475 |
+
|
| 476 |
+
= 100%
|
| 477 |
+
```
|
| 478 |
+
|
| 479 |
+
However, this metric should NOT be interpreted as proof that the system has 100% real-world retrieval accuracy.
|
| 480 |
+
|
| 481 |
+
It mainly verifies that the embedding/index pipeline can retrieve the original record when the exact same question is used.
|
| 482 |
+
|
| 483 |
+
---
|
| 484 |
+
|
| 485 |
+
# 14. Retrieval Evaluation
|
| 486 |
+
|
| 487 |
+
The retrieval evaluation used questions that have duplicate Question entries in the dataset.
|
| 488 |
+
|
| 489 |
+
This provides multiple possible relevant answers for the same question.
|
| 490 |
+
|
| 491 |
+
```text
|
| 492 |
+
Questions with duplicate entries usable for evaluation:
|
| 493 |
+
67
|
| 494 |
+
```
|
| 495 |
+
|
| 496 |
+
## Retrieval Only
|
| 497 |
+
|
| 498 |
+
```text
|
| 499 |
+
Recall@1 = 0.690
|
| 500 |
+
Precision@1 = 1.000
|
| 501 |
+
F1@1 = 0.745
|
| 502 |
+
|
| 503 |
+
Recall@3 = 0.851
|
| 504 |
+
Precision@3 = 0.560
|
| 505 |
+
F1@3 = 0.563
|
| 506 |
+
|
| 507 |
+
Recall@5 = 0.899
|
| 508 |
+
Precision@5 = 0.423
|
| 509 |
+
F1@5 = 0.462
|
| 510 |
+
|
| 511 |
+
Recall@10 = 0.959
|
| 512 |
+
Precision@10 = 0.286
|
| 513 |
+
F1@10 = 0.349
|
| 514 |
+
|
| 515 |
+
MRR = 1.000
|
| 516 |
+
```
|
| 517 |
+
|
| 518 |
+
Number of evaluated queries:
|
| 519 |
+
|
| 520 |
+
```text
|
| 521 |
+
150
|
| 522 |
+
```
|
| 523 |
+
|
| 524 |
+
---
|
| 525 |
+
|
| 526 |
+
# 15. Retrieval + Cross-Encoder Evaluation
|
| 527 |
+
|
| 528 |
+
The same evaluation was also performed after Cross-Encoder reranking.
|
| 529 |
+
|
| 530 |
+
Results:
|
| 531 |
+
|
| 532 |
+
```text
|
| 533 |
+
Recall@1 = 0.083
|
| 534 |
+
Precision@1 = 0.113
|
| 535 |
+
F1@1 = 0.093
|
| 536 |
+
|
| 537 |
+
Recall@3 = 0.157
|
| 538 |
+
Precision@3 = 0.087
|
| 539 |
+
F1@3 = 0.100
|
| 540 |
+
|
| 541 |
+
Recall@5 = 0.222
|
| 542 |
+
Precision@5 = 0.121
|
| 543 |
+
F1@5 = 0.120
|
| 544 |
+
|
| 545 |
+
Recall@10 = 0.388
|
| 546 |
+
Precision@10 = 0.135
|
| 547 |
+
F1@10 = 0.153
|
| 548 |
+
|
| 549 |
+
MRR = 0.237
|
| 550 |
+
```
|
| 551 |
+
|
| 552 |
+
Number of evaluated queries:
|
| 553 |
+
|
| 554 |
+
```text
|
| 555 |
+
150
|
| 556 |
+
```
|
| 557 |
+
|
| 558 |
+
---
|
| 559 |
+
|
| 560 |
+
# 16. Important Evaluation Observation
|
| 561 |
+
|
| 562 |
+
The evaluation shows that the Cross-Encoder performed significantly worse under this particular retrieval benchmark.
|
| 563 |
+
|
| 564 |
+
This is an important finding rather than something to hide.
|
| 565 |
+
|
| 566 |
+
The current reranker was trained for general passage relevance using:
|
| 567 |
+
|
| 568 |
+
```text
|
| 569 |
+
cross-encoder/ms-marco-MiniLM-L-6-v2
|
| 570 |
+
```
|
| 571 |
+
|
| 572 |
+
while this evaluation is based on medical Question-Answer records.
|
| 573 |
+
|
| 574 |
+
Therefore, the Cross-Encoder may not rank the medical answers in the same way as the manually defined relevance criteria.
|
| 575 |
+
|
| 576 |
+
This suggests that the reranking component requires further investigation before being considered production-ready.
|
| 577 |
+
|
| 578 |
+
Possible future improvements include:
|
| 579 |
+
|
| 580 |
+
- Medical-domain reranker
|
| 581 |
+
- Fine-tuning a Cross-Encoder on medical relevance pairs
|
| 582 |
+
- Using a manually reviewed gold set
|
| 583 |
+
- Better relevance labeling
|
| 584 |
+
- Evaluating reranking separately from retrieval
|
| 585 |
+
- Testing alternative reranking models
|
| 586 |
+
|
| 587 |
+
---
|
| 588 |
+
|
| 589 |
+
# 17. Category Sanity Check
|
| 590 |
+
|
| 591 |
+
A category-based sanity check was also performed.
|
| 592 |
+
|
| 593 |
+
The goal is to determine whether retrieved documents contain at least one document belonging to the same category as the query's source document.
|
| 594 |
+
|
| 595 |
+
## Retrieval + Reranker
|
| 596 |
+
|
| 597 |
+
```text
|
| 598 |
+
Category Recall = 0.98
|
| 599 |
+
n = 100
|
| 600 |
+
```
|
| 601 |
+
|
| 602 |
+
## Retrieval Only
|
| 603 |
+
|
| 604 |
+
```text
|
| 605 |
+
Category Recall = 0.97
|
| 606 |
+
n = 100
|
| 607 |
+
```
|
| 608 |
+
|
| 609 |
+
This indicates that the retrieval system generally retrieves documents from the expected medical category.
|
| 610 |
+
|
| 611 |
+
However:
|
| 612 |
+
|
| 613 |
+
> Category matching is only a supporting diagnostic metric and is NOT treated as the final ground truth for relevance.
|
| 614 |
+
|
| 615 |
+
---
|
| 616 |
+
|
| 617 |
+
# 18. Confidence Gate Evaluation
|
| 618 |
+
|
| 619 |
+
A manually constructed evaluation set contained:
|
| 620 |
+
|
| 621 |
+
```text
|
| 622 |
+
25 in-domain questions
|
| 623 |
+
25 out-of-domain questions
|
| 624 |
+
```
|
| 625 |
+
|
| 626 |
+
Total:
|
| 627 |
+
|
| 628 |
+
```text
|
| 629 |
+
50 questions
|
| 630 |
+
```
|
| 631 |
+
|
| 632 |
+
The system achieved:
|
| 633 |
+
|
| 634 |
+
```text
|
| 635 |
+
Gate Decision Accuracy = 88%
|
| 636 |
+
```
|
| 637 |
+
|
| 638 |
+
The system correctly accepted most in-domain questions and rejected most out-of-domain questions.
|
| 639 |
+
|
| 640 |
+
However, several false positives were observed.
|
| 641 |
+
|
| 642 |
+
Examples included questions about:
|
| 643 |
+
|
| 644 |
+
```text
|
| 645 |
+
broken legs
|
| 646 |
+
kidney stones
|
| 647 |
+
sprained ankles
|
| 648 |
+
broken arms
|
| 649 |
+
food poisoning
|
| 650 |
+
```
|
| 651 |
+
|
| 652 |
+
These questions sometimes retrieved medically related evidence even though the knowledge base did not contain appropriate evidence.
|
| 653 |
+
|
| 654 |
+
This demonstrates why confidence gating and stronger relevance evaluation are necessary.
|
| 655 |
+
|
| 656 |
+
---
|
| 657 |
+
|
| 658 |
+
# 19. Confidence Score Distribution
|
| 659 |
+
|
| 660 |
+
For the confidence-gate evaluation:
|
| 661 |
+
|
| 662 |
+
### Expected answer = True
|
| 663 |
+
|
| 664 |
+
```text
|
| 665 |
+
Mean score = 5.539
|
| 666 |
+
Minimum = -8.938
|
| 667 |
+
Maximum = 9.321
|
| 668 |
+
```
|
| 669 |
+
|
| 670 |
+
### Expected answer = False
|
| 671 |
+
|
| 672 |
+
```text
|
| 673 |
+
Mean score = -6.167
|
| 674 |
+
Minimum = -11.062
|
| 675 |
+
Maximum = 1.532
|
| 676 |
+
```
|
| 677 |
+
|
| 678 |
+
There is overlap between the two distributions.
|
| 679 |
+
|
| 680 |
+
Therefore, a single rerank-score threshold cannot perfectly separate valid and invalid questions.
|
| 681 |
+
|
| 682 |
+
---
|
| 683 |
+
|
| 684 |
+
# 20. Threshold Tuning
|
| 685 |
+
|
| 686 |
+
The confidence threshold was evaluated using 5-fold cross-validation.
|
| 687 |
+
|
| 688 |
+
Instead of selecting a threshold from one train/test split, the data is divided into five different folds.
|
| 689 |
+
|
| 690 |
+
For every fold:
|
| 691 |
+
|
| 692 |
+
1. A threshold is optimized on the training portion.
|
| 693 |
+
2. The threshold is evaluated on the held-out fold.
|
| 694 |
+
3. The threshold and test accuracy are recorded.
|
| 695 |
+
4. Mean and standard deviation are calculated.
|
| 696 |
+
|
| 697 |
+
This helps determine whether the selected threshold is stable or highly dependent on a small evaluation sample.
|
| 698 |
+
|
| 699 |
+
A large threshold standard deviation indicates that the evaluation set is too small or unstable and should be expanded before selecting a production threshold.
|
| 700 |
+
|
| 701 |
+
---
|
| 702 |
+
|
| 703 |
+
# 21. Gold Set
|
| 704 |
+
|
| 705 |
+
A semi-automatic gold-set construction process was implemented.
|
| 706 |
+
|
| 707 |
+
The process:
|
| 708 |
+
|
| 709 |
+
```text
|
| 710 |
+
100 sampled questions
|
| 711 |
+
│
|
| 712 |
+
▼
|
| 713 |
+
Exact duplicate questions
|
| 714 |
+
│
|
| 715 |
+
▼
|
| 716 |
+
Semantic candidates with similarity >= 0.90
|
| 717 |
+
│
|
| 718 |
+
▼
|
| 719 |
+
Manual review
|
| 720 |
+
│
|
| 721 |
+
▼
|
| 722 |
+
Relevant = 1
|
| 723 |
+
Not relevant = 0
|
| 724 |
+
│
|
| 725 |
+
▼
|
| 726 |
+
Final Gold Set
|
| 727 |
+
```
|
| 728 |
+
|
| 729 |
+
The initial run generated:
|
| 730 |
+
|
| 731 |
+
```text
|
| 732 |
+
100 questions
|
| 733 |
+
16 candidate pairs
|
| 734 |
+
3 questions with semantic candidates
|
| 735 |
+
```
|
| 736 |
+
|
| 737 |
+
The generated file:
|
| 738 |
+
|
| 739 |
+
```text
|
| 740 |
+
gold_set_for_manual_review.xlsx
|
| 741 |
+
```
|
| 742 |
+
|
| 743 |
+
contains candidate pairs that should be manually reviewed.
|
| 744 |
+
|
| 745 |
+
For each candidate, the reviewer should enter:
|
| 746 |
+
|
| 747 |
+
```text
|
| 748 |
+
1 = relevant
|
| 749 |
+
0 = not relevant
|
| 750 |
+
```
|
| 751 |
+
|
| 752 |
+
Only manually approved candidates should be added to the final relevant evidence set.
|
| 753 |
+
|
| 754 |
+
---
|
| 755 |
+
|
| 756 |
+
# 22. Gold Set Evaluation Metrics
|
| 757 |
+
|
| 758 |
+
Once the manually reviewed gold set is completed, the system evaluates:
|
| 759 |
+
|
| 760 |
+
```text
|
| 761 |
+
Recall@5
|
| 762 |
+
MRR
|
| 763 |
+
NDCG@5
|
| 764 |
+
```
|
| 765 |
+
|
| 766 |
+
The evaluation compares:
|
| 767 |
+
|
| 768 |
+
```text
|
| 769 |
+
Retriever only
|
| 770 |
+
```
|
| 771 |
+
|
| 772 |
+
against:
|
| 773 |
+
|
| 774 |
+
```text
|
| 775 |
+
Retriever + Cross-Encoder Reranker
|
| 776 |
+
```
|
| 777 |
+
|
| 778 |
+
The manually reviewed gold set is preferred over category-based evaluation because it provides explicit relevance judgments.
|
| 779 |
+
|
| 780 |
+
---
|
| 781 |
+
|
| 782 |
+
# 23. Latency Evaluation
|
| 783 |
+
|
| 784 |
+
The complete RAG pipeline was also measured for response latency.
|
| 785 |
+
|
| 786 |
+
Example result:
|
| 787 |
+
|
| 788 |
+
```text
|
| 789 |
+
Retrieval = 0.016 sec
|
| 790 |
+
Reranking = 0.094 sec
|
| 791 |
+
Evidence build = 0.025 sec
|
| 792 |
+
LLM generation = 0.858 sec
|
| 793 |
+
|
| 794 |
+
Total = 0.993 sec
|
| 795 |
+
```
|
| 796 |
+
|
| 797 |
+
Approximate breakdown:
|
| 798 |
+
|
| 799 |
+
```text
|
| 800 |
+
Retrieval █
|
| 801 |
+
Reranking █████
|
| 802 |
+
Evidence ██
|
| 803 |
+
LLM █████████████████████████████████████
|
| 804 |
+
```
|
| 805 |
+
|
| 806 |
+
The LLM generation stage is the largest contributor to total latency.
|
| 807 |
+
|
| 808 |
+
The measured end-to-end latency in this test was approximately:
|
| 809 |
+
|
| 810 |
+
```text
|
| 811 |
+
0.99 seconds
|
| 812 |
+
```
|
| 813 |
+
|
| 814 |
+
This should not be interpreted as a guaranteed production latency because API/network conditions, hardware, load, and model availability can change.
|
| 815 |
+
|
| 816 |
+
---
|
| 817 |
+
|
| 818 |
+
# 24. Saved Artifacts
|
| 819 |
+
|
| 820 |
+
The system uses the following main artifacts:
|
| 821 |
+
|
| 822 |
+
```text
|
| 823 |
+
questions.index
|
| 824 |
+
question_embeddings.npy
|
| 825 |
+
README.md
|
| 826 |
+
gold_set_for_manual_review.xlsx
|
| 827 |
+
```
|
| 828 |
+
|
| 829 |
+
## questions.index
|
| 830 |
+
|
| 831 |
+
FAISS vector index containing the question embeddings.
|
| 832 |
+
|
| 833 |
+
## question_embeddings.npy
|
| 834 |
+
|
| 835 |
+
NumPy array containing the normalized question embeddings.
|
| 836 |
+
|
| 837 |
+
## gold_set_for_manual_review.xlsx
|
| 838 |
+
|
| 839 |
+
Human-review file used to create the manually verified relevance set.
|
| 840 |
+
|
| 841 |
+
---
|
| 842 |
+
|
| 843 |
+
# 25. Local Deployment
|
| 844 |
+
|
| 845 |
+
The RAG system can be deployed locally as a Python service.
|
| 846 |
+
|
| 847 |
+
Recommended architecture:
|
| 848 |
+
|
| 849 |
+
```text
|
| 850 |
+
Frontend
|
| 851 |
+
│
|
| 852 |
+
▼
|
| 853 |
+
Backend API
|
| 854 |
+
│
|
| 855 |
+
▼
|
| 856 |
+
Medical RAG Pipeline
|
| 857 |
+
│
|
| 858 |
+
├── FAISS
|
| 859 |
+
├── Sentence Transformer
|
| 860 |
+
├── Cross Encoder
|
| 861 |
+
└── LLM API
|
| 862 |
+
```
|
| 863 |
+
|
| 864 |
+
The application should load the embedding model and FAISS index once when the service starts.
|
| 865 |
+
|
| 866 |
+
They should NOT be reloaded for every user request.
|
| 867 |
+
|
| 868 |
+
Example:
|
| 869 |
+
|
| 870 |
+
```python
|
| 871 |
+
from sentence_transformers import SentenceTransformer, CrossEncoder
|
| 872 |
+
import faiss
|
| 873 |
+
import numpy as np
|
| 874 |
+
|
| 875 |
+
embedder = SentenceTransformer(
|
| 876 |
+
"sentence-transformers/all-MiniLM-L6-v2"
|
| 877 |
+
)
|
| 878 |
+
|
| 879 |
+
reranker = CrossEncoder(
|
| 880 |
+
"cross-encoder/ms-marco-MiniLM-L-6-v2"
|
| 881 |
+
)
|
| 882 |
+
|
| 883 |
+
faiss_index = faiss.read_index(
|
| 884 |
+
"questions.index"
|
| 885 |
+
)
|
| 886 |
+
|
| 887 |
+
question_embeddings = np.load(
|
| 888 |
+
"question_embeddings.npy"
|
| 889 |
+
)
|
| 890 |
+
```
|
| 891 |
+
|
| 892 |
+
---
|
| 893 |
+
|
| 894 |
+
# 26. Example API Architecture
|
| 895 |
+
|
| 896 |
+
A backend can expose an endpoint such as:
|
| 897 |
+
|
| 898 |
+
```http
|
| 899 |
+
POST /api/rag/query
|
| 900 |
+
```
|
| 901 |
+
|
| 902 |
+
Request:
|
| 903 |
+
|
| 904 |
+
```json
|
| 905 |
+
{
|
| 906 |
+
"question": "What are the symptoms of diabetes?"
|
| 907 |
+
}
|
| 908 |
+
```
|
| 909 |
+
|
| 910 |
+
Response:
|
| 911 |
+
|
| 912 |
+
```json
|
| 913 |
+
{
|
| 914 |
+
"answer": "...",
|
| 915 |
+
"evidence": [
|
| 916 |
+
{
|
| 917 |
+
"doc_id": 4793,
|
| 918 |
+
"similarity": 0.91,
|
| 919 |
+
"rerank_score": 8.21
|
| 920 |
+
}
|
| 921 |
+
]
|
| 922 |
+
}
|
| 923 |
+
```
|
| 924 |
+
|
| 925 |
+
This makes the RAG pipeline independent from the frontend.
|
| 926 |
+
|
| 927 |
+
---
|
| 928 |
+
|
| 929 |
+
# 27. Recommended Project Structure
|
| 930 |
+
|
| 931 |
+
```text
|
| 932 |
+
medical-rag/
|
| 933 |
+
│
|
| 934 |
+
├── README.md
|
| 935 |
+
│
|
| 936 |
+
├── artifacts/
|
| 937 |
+
│ ├── questions.index
|
| 938 |
+
│ └── question_embeddings.npy
|
| 939 |
+
│
|
| 940 |
+
├── src/
|
| 941 |
+
│ ├── retrieval.py
|
| 942 |
+
│ ├── reranker.py
|
| 943 |
+
│ ├── query_expansion.py
|
| 944 |
+
│ ├── evidence.py
|
| 945 |
+
│ ├── confidence_gate.py
|
| 946 |
+
│ ├── generation.py
|
| 947 |
+
│ └── rag_pipeline.py
|
| 948 |
+
│
|
| 949 |
+
├── api/
|
| 950 |
+
│ └── app.py
|
| 951 |
+
│
|
| 952 |
+
├── evaluation/
|
| 953 |
+
│ ├── retrieval_evaluation.py
|
| 954 |
+
│ ├── gate_evaluation.py
|
| 955 |
+
│ └── gold_set_evaluation.py
|
| 956 |
+
│
|
| 957 |
+
├── data/
|
| 958 |
+
│ └── gold_set_for_manual_review.xlsx
|
| 959 |
+
│
|
| 960 |
+
└── requirements.txt
|
| 961 |
+
```
|
| 962 |
+
|
| 963 |
+
---
|
| 964 |
+
|
| 965 |
+
# 28. Installation
|
| 966 |
+
|
| 967 |
+
Install the main dependencies:
|
| 968 |
+
|
| 969 |
+
```bash
|
| 970 |
+
pip install sentence-transformers faiss-cpu numpy pandas openpyxl
|
| 971 |
+
```
|
| 972 |
+
|
| 973 |
+
For Groq-based generation:
|
| 974 |
+
|
| 975 |
+
```bash
|
| 976 |
+
pip install groq
|
| 977 |
+
```
|
| 978 |
+
|
| 979 |
+
---
|
| 980 |
+
|
| 981 |
+
# 29. Environment Variables
|
| 982 |
+
|
| 983 |
+
Never hard-code API keys inside source code.
|
| 984 |
+
|
| 985 |
+
Set:
|
| 986 |
+
|
| 987 |
+
```bash
|
| 988 |
+
GROQ_API_KEY=your_api_key_here
|
| 989 |
+
```
|
| 990 |
+
|
| 991 |
+
Python:
|
| 992 |
+
|
| 993 |
+
```python
|
| 994 |
+
import os
|
| 995 |
+
|
| 996 |
+
api_key = os.environ["GROQ_API_KEY"]
|
| 997 |
+
```
|
| 998 |
+
|
| 999 |
+
---
|
| 1000 |
+
|
| 1001 |
+
# 30. Security
|
| 1002 |
+
|
| 1003 |
+
The API key used during development must not be committed to GitHub or Hugging Face.
|
| 1004 |
+
|
| 1005 |
+
Recommended practice:
|
| 1006 |
+
|
| 1007 |
+
```text
|
| 1008 |
+
.env
|
| 1009 |
+
```
|
| 1010 |
+
|
| 1011 |
+
and:
|
| 1012 |
+
|
| 1013 |
+
```text
|
| 1014 |
+
.gitignore
|
| 1015 |
+
```
|
| 1016 |
+
|
| 1017 |
+
Example `.gitignore`:
|
| 1018 |
+
|
| 1019 |
+
```text
|
| 1020 |
+
.env
|
| 1021 |
+
__pycache__/
|
| 1022 |
+
*.pyc
|
| 1023 |
+
.ipynb_checkpoints/
|
| 1024 |
+
```
|
| 1025 |
+
|
| 1026 |
+
If an API key has already been exposed publicly, revoke it and generate a new one.
|
| 1027 |
+
|
| 1028 |
+
---
|
| 1029 |
+
|
| 1030 |
+
# 31. Limitations
|
| 1031 |
+
|
| 1032 |
+
This system has several important limitations.
|
| 1033 |
+
|
| 1034 |
+
### 1. Medical domain limitations
|
| 1035 |
+
|
| 1036 |
+
The knowledge base does not necessarily cover every medical condition or clinical scenario.
|
| 1037 |
+
|
| 1038 |
+
### 2. Retrieval limitations
|
| 1039 |
+
|
| 1040 |
+
A semantically similar document is not necessarily an appropriate answer.
|
| 1041 |
+
|
| 1042 |
+
### 3. Reranker limitations
|
| 1043 |
+
|
| 1044 |
+
The current Cross-Encoder was not specifically trained on this medical dataset.
|
| 1045 |
+
|
| 1046 |
+
### 4. Gold-set limitations
|
| 1047 |
+
|
| 1048 |
+
The manually reviewed gold set is still small.
|
| 1049 |
+
|
| 1050 |
+
### 5. Evaluation limitations
|
| 1051 |
+
|
| 1052 |
+
Some evaluation metrics rely on duplicate Question entries rather than a fully manually annotated relevance dataset.
|
| 1053 |
+
|
| 1054 |
+
### 6. LLM limitations
|
| 1055 |
+
|
| 1056 |
+
The generated answer depends on the quality of the retrieved evidence.
|
| 1057 |
+
|
| 1058 |
+
### 7. Clinical safety
|
| 1059 |
+
|
| 1060 |
+
The system should not be used for diagnosis, emergency decisions, prescribing medication, or personalized treatment.
|
| 1061 |
+
|
| 1062 |
+
---
|
| 1063 |
+
|
| 1064 |
+
# 32. Future Improvements
|
| 1065 |
+
|
| 1066 |
+
Recommended next steps:
|
| 1067 |
+
|
| 1068 |
+
```text
|
| 1069 |
+
1. Complete the manual gold-set annotation.
|
| 1070 |
+
|
| 1071 |
+
2. Increase the number of manually reviewed questions.
|
| 1072 |
+
|
| 1073 |
+
3. Train or evaluate a medical-domain reranker.
|
| 1074 |
+
|
| 1075 |
+
4. Re-evaluate the Cross-Encoder using the manual gold set.
|
| 1076 |
+
|
| 1077 |
+
5. Tune the confidence gate using a larger validation set.
|
| 1078 |
+
|
| 1079 |
+
6. Add citation/source metadata.
|
| 1080 |
+
|
| 1081 |
+
7. Add automated regression tests.
|
| 1082 |
+
|
| 1083 |
+
8. Add monitoring for retrieval failures.
|
| 1084 |
+
|
| 1085 |
+
9. Add API-level authentication and rate limiting.
|
| 1086 |
+
|
| 1087 |
+
10. Deploy the RAG pipeline behind a REST API.
|
| 1088 |
+
|
| 1089 |
+
11. Add a frontend chat interface.
|
| 1090 |
+
|
| 1091 |
+
12. Evaluate hallucination/faithfulness separately from retrieval quality.
|
| 1092 |
+
```
|
| 1093 |
+
|
| 1094 |
+
---
|
| 1095 |
+
|
| 1096 |
+
# 33. Evaluation Summary
|
| 1097 |
+
|
| 1098 |
+
| Metric | Result |
|
| 1099 |
+
|---|---:|
|
| 1100 |
+
| Knowledge Base Size | 16,384 records |
|
| 1101 |
+
| Self-Retrieval Top-1 | 100% |
|
| 1102 |
+
| Retrieval Recall@1 | 69.0% |
|
| 1103 |
+
| Retrieval Recall@3 | 85.1% |
|
| 1104 |
+
| Retrieval Recall@5 | 89.9% |
|
| 1105 |
+
| Retrieval Recall@10 | 95.9% |
|
| 1106 |
+
| Retrieval Precision@1 | 100% |
|
| 1107 |
+
| Retrieval F1@1 | 74.5% |
|
| 1108 |
+
| Retrieval MRR | 1.000 |
|
| 1109 |
+
| Category Recall + Reranker | 98% |
|
| 1110 |
+
| Category Recall Retrieval Only | 97% |
|
| 1111 |
+
| Confidence Gate Accuracy | 88% |
|
| 1112 |
+
| Example End-to-End Latency | 0.993 sec |
|
| 1113 |
+
|
| 1114 |
+
> The evaluation numbers above are specific to the current experimental setup and should not be interpreted as general medical accuracy.
|
| 1115 |
+
|
| 1116 |
+
---
|
| 1117 |
+
|
| 1118 |
+
# 34. Responsible Use
|
| 1119 |
+
|
| 1120 |
+
This project provides general medical information retrieval and generation.
|
| 1121 |
+
|
| 1122 |
+
It is not:
|
| 1123 |
+
|
| 1124 |
+
- A diagnostic tool
|
| 1125 |
+
- A clinical decision-support system
|
| 1126 |
+
- A replacement for a physician
|
| 1127 |
+
- A prescription system
|
| 1128 |
+
- An emergency medical service
|
| 1129 |
+
|
| 1130 |
+
Users should consult qualified healthcare professionals for medical decisions.
|
| 1131 |
+
|
| 1132 |
+
---
|
| 1133 |
+
|
| 1134 |
+
# 35. Citation and References
|
| 1135 |
+
|
| 1136 |
+
### Sentence Transformers
|
| 1137 |
+
|
| 1138 |
+
https://www.sbert.net/
|
| 1139 |
+
|
| 1140 |
+
### FAISS
|
| 1141 |
+
|
| 1142 |
+
https://github.com/facebookresearch/faiss
|
| 1143 |
+
|
| 1144 |
+
### Cross-Encoder
|
| 1145 |
+
|
| 1146 |
+
https://www.sbert.net/examples/applications/cross-encoder/README.html
|
| 1147 |
+
|
| 1148 |
+
### Hugging Face Model Cards
|
| 1149 |
+
|
| 1150 |
+
https://huggingface.co/docs/hub/en/model-cards
|
| 1151 |
+
|
| 1152 |
+
### Hugging Face Model Release Checklist
|
| 1153 |
+
|
| 1154 |
+
https://huggingface.co/docs/hub/en/model-release-checklist
|
| 1155 |
+
|
| 1156 |
+
---
|
| 1157 |
+
|
| 1158 |
+
# 36. Project Status
|
| 1159 |
+
|
| 1160 |
+
```text
|
| 1161 |
+
Status: Research / Prototype
|
| 1162 |
+
|
| 1163 |
+
Retrieval: Implemented
|
| 1164 |
+
FAISS: Implemented
|
| 1165 |
+
Query Expansion: Implemented
|
| 1166 |
+
Cross-Encoder Reranking: Implemented
|
| 1167 |
+
Evidence Deduplication: Implemented
|
| 1168 |
+
Confidence Gate: Implemented
|
| 1169 |
+
LLM Generation: Implemented
|
| 1170 |
+
Evaluation: Implemented
|
| 1171 |
+
Latency Measurement: Implemented
|
| 1172 |
+
Manual Gold Set: In Progress
|
| 1173 |
+
Production Deployment: Future Work
|
| 1174 |
+
```
|
| 1175 |
+
|
| 1176 |
+
---
|
| 1177 |
+
|
| 1178 |
+
# 37. Final Note
|
| 1179 |
+
|
| 1180 |
+
The main objective of this project is not simply to generate medical answers.
|
| 1181 |
+
|
| 1182 |
+
The goal is to build a RAG system that:
|
| 1183 |
+
|
| 1184 |
+
```text
|
| 1185 |
+
Retrieves evidence
|
| 1186 |
+
↓
|
| 1187 |
+
Ranks evidence
|
| 1188 |
+
↓
|
| 1189 |
+
Checks confidence
|
| 1190 |
+
↓
|
| 1191 |
+
Generates from evidence
|
| 1192 |
+
↓
|
| 1193 |
+
Provides traceable document IDs
|
| 1194 |
+
```
|
| 1195 |
+
|
| 1196 |
+
This design allows the system to be evaluated as a retrieval pipeline independently from the LLM generation layer.
|