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
Update README.md
Browse files
README.md
CHANGED
|
@@ -19,303 +19,56 @@ license: apache-2.0
|
|
| 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 |
-
|
| 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.
|
| 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 |
-
|
| 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 |
-
|
| 292 |
|
| 293 |
-
```
|
| 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 |
-
#
|
| 313 |
|
| 314 |
-
|
|
|
|
|
|
|
|
|
|
| 315 |
|
| 316 |
-
|
| 317 |
|
| 318 |
-
|
| 319 |
|
| 320 |
```python
|
| 321 |
MIN_RERANK_SCORE = -8
|
|
@@ -324,873 +77,73 @@ 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.
|
| 331 |
-
3.
|
| 332 |
-
4. At least
|
| 333 |
|
| 334 |
-
Otherwise, the system refuses to
|
| 335 |
|
| 336 |
-
|
| 337 |
|
| 338 |
-
```
|
| 339 |
-
Question:
|
| 340 |
-
Why do I feel short of breath and tired when I climb stairs?
|
| 341 |
|
| 342 |
-
|
| 343 |
-
No sufficiently relevant evidence.
|
| 344 |
|
| 345 |
-
|
| 346 |
-
|
| 347 |
-
|
| 348 |
-
```
|
| 349 |
|
| 350 |
-
|
|
|
|
| 351 |
|
| 352 |
-
|
| 353 |
|
| 354 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 355 |
|
| 356 |
-
|
| 357 |
|
| 358 |
-
|
| 359 |
|
| 360 |
-
|
| 361 |
-
openai/gpt-oss-20b
|
| 362 |
-
```
|
| 363 |
|
| 364 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 365 |
|
| 366 |
-
|
| 367 |
|
| 368 |
-
```
|
| 369 |
-
|
| 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 |
-
|
| 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 |
-
|
| 598 |
-
Category Recall = 0.98
|
| 599 |
-
n = 100
|
| 600 |
-
```
|
| 601 |
|
| 602 |
-
##
|
| 603 |
|
| 604 |
-
|
| 605 |
-
Category Recall = 0.97
|
| 606 |
-
n = 100
|
| 607 |
-
```
|
| 608 |
|
| 609 |
-
|
| 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 |
-
|
|
|
|
|
|
|
|
|
| 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 semantic retrieval (Sentence Transformers + FAISS), medical query expansion, Cross-Encoder reranking, evidence deduplication, confidence gating, and LLM-based answer generation.
|
| 23 |
|
| 24 |
+
> **Important:** This system is for research and educational purposes only. It is not a medical diagnostic system and should not replace professional medical advice.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
|
| 26 |
---
|
| 27 |
|
| 28 |
+
## 1. Pipeline
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
|
| 30 |
```text
|
| 31 |
User Question
|
| 32 |
│
|
| 33 |
▼
|
| 34 |
+
Query Expansion (lay terms → medical terms)
|
| 35 |
│
|
| 36 |
▼
|
| 37 |
Sentence Transformer Embedding
|
| 38 |
│
|
| 39 |
▼
|
| 40 |
+
FAISS Vector Search (Top 20 candidates)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
│
|
| 42 |
▼
|
| 43 |
+
Cross-Encoder Reranking (Top 6 evidence)
|
| 44 |
│
|
| 45 |
▼
|
| 46 |
Near-Duplicate Removal
|
| 47 |
│
|
| 48 |
▼
|
| 49 |
+
Confidence Gate ──reject──▶ "Insufficient evidence"
|
| 50 |
+
│ pass
|
|
|
|
|
|
|
| 51 |
▼
|
| 52 |
LLM Generation
|
| 53 |
│
|
| 54 |
▼
|
| 55 |
+
Evidence-Based Answer (with doc_id citations)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 56 |
```
|
| 57 |
|
| 58 |
+
## 2. Knowledge Base
|
| 59 |
|
| 60 |
+
~16,384 medical Question-Answer records with columns `Question`, `Answer`, `Category`, `doc_id`.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
|
| 62 |
+
## 3. Retrieval
|
| 63 |
|
| 64 |
+
- **Embedding model:** `sentence-transformers/all-MiniLM-L6-v2`, embeddings normalized so FAISS `IndexFlatIP` behaves as cosine similarity.
|
| 65 |
+
- **Query Expansion:** a small lay-term → medical-term dictionary is appended to the query before embedding (e.g. `underactive thyroid → hypothyroidism`, `high blood sugar → hyperglycemia`). The original query is never replaced, only extended.
|
| 66 |
+
- **Reranking:** `cross-encoder/ms-marco-MiniLM-L-6-v2` scores `(query, Answer)` — not `(query, Question)` — since many records share the same question with different answers; this identifies which *evidence* is actually useful.
|
| 67 |
+
- **Evidence deduplication:** near-identical answers are collapsed via `difflib.SequenceMatcher` (threshold 0.92).
|
| 68 |
|
| 69 |
+
## 4. Confidence Gate
|
| 70 |
|
| 71 |
+
Evidence is only sent to the LLM if **all** conditions hold:
|
| 72 |
|
| 73 |
```python
|
| 74 |
MIN_RERANK_SCORE = -8
|
|
|
|
| 77 |
SUPPORT_SCORE = -5.0
|
| 78 |
```
|
| 79 |
|
|
|
|
|
|
|
| 80 |
1. At least one result exists.
|
| 81 |
+
2. Top rerank score ≥ `MIN_RERANK_SCORE`.
|
| 82 |
+
3. Top similarity ≥ `MIN_SIMILARITY_FLOOR`.
|
| 83 |
+
4. At least `MIN_SUPPORT_COUNT` results score ≥ `SUPPORT_SCORE`.
|
| 84 |
|
| 85 |
+
Otherwise, the system explicitly refuses to answer rather than guessing.
|
| 86 |
|
| 87 |
+
## 5. Generation
|
| 88 |
|
| 89 |
+
LLM: Groq-hosted `openai/gpt-oss-20b`, instructed to answer **only** from retrieved evidence, cite sources by `[doc_id]`, flag disagreement between sources, avoid diagnosis/prescriptions, and include a medical disclaimer. API key is read from `GROQ_API_KEY` (never hard-coded).
|
|
|
|
|
|
|
| 90 |
|
| 91 |
+
## 6. Example
|
|
|
|
| 92 |
|
| 93 |
+
**Q:** *What signs might suggest that my thyroid is not producing enough hormones?*
|
| 94 |
+
**Evidence:** 6 thyroid-related records retrieved and reranked.
|
| 95 |
+
**Answer:** Evidence-based summary (fatigue, feeling cold, weight gain, constipation, menstrual changes, hair loss) + disclaimer.
|
|
|
|
| 96 |
|
| 97 |
+
**Out-of-domain example — Q:** *What is the best treatment for a broken leg?*
|
| 98 |
+
Retrieval returned diabetic-foot documents (lexically similar), but the gate/LLM correctly identified them as inappropriate evidence and refused to answer — demonstrating that similarity alone doesn't guarantee relevance.
|
| 99 |
|
| 100 |
+
## 7. Evaluation Summary
|
| 101 |
|
| 102 |
+
| Metric | Result |
|
| 103 |
+
|---|---:|
|
| 104 |
+
| Knowledge Base Size | 16,384 records |
|
| 105 |
+
| Self-Retrieval Top-1 | 100% |
|
| 106 |
+
| Retrieval Recall@1 / @5 / @10 | 69.0% / 89.9% / 95.9% |
|
| 107 |
+
| Retrieval MRR | 1.000 |
|
| 108 |
+
| Retrieval + Reranker Recall@1 / @5 / @10 | 8.3% / 22.2% / 38.8% |
|
| 109 |
+
| Retrieval + Reranker MRR | 0.237 |
|
| 110 |
+
| Category Recall (retrieval / +reranker) | 97% / 98% |
|
| 111 |
+
| Confidence Gate Accuracy | 88% (n=50) |
|
| 112 |
+
| Example End-to-End Latency | 0.99 sec |
|
| 113 |
|
| 114 |
+
**Key finding:** the general-purpose Cross-Encoder scores notably *worse* than plain retrieval on this duplicate-question-based benchmark. This isn't hidden — it indicates the reranker (trained for general passage relevance) doesn't align well with medical relevance judgments, and needs validation against a manually reviewed gold set before being trusted in production. A semi-automatic gold-set workflow (exact duplicates + semantic candidates ≥0.90 similarity, human-reviewed 1/0 labels) is included for this purpose; Category-match is used only as a secondary sanity check, not ground truth.
|
| 115 |
|
| 116 |
+
The confidence-gate threshold was tuned via 5-fold cross-validation rather than a single split, to check stability rather than overfit to one small sample.
|
| 117 |
|
| 118 |
+
## 8. Limitations
|
|
|
|
|
|
|
| 119 |
|
| 120 |
+
- Knowledge base doesn't cover every condition/scenario.
|
| 121 |
+
- Semantic similarity ≠ appropriate evidence (see broken-leg example).
|
| 122 |
+
- Reranker not fine-tuned on this medical domain yet.
|
| 123 |
+
- Manual gold set is still small — evaluation should be treated as preliminary.
|
| 124 |
+
- Not for diagnosis, emergencies, prescriptions, or personalized treatment.
|
| 125 |
|
| 126 |
+
## 9. Installation
|
| 127 |
|
| 128 |
+
```bash
|
| 129 |
+
pip install sentence-transformers faiss-cpu numpy pandas openpyxl groq
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
```
|
| 131 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 132 |
```python
|
| 133 |
import os
|
| 134 |
+
GROQ_API_KEY = os.environ["GROQ_API_KEY"] # never hard-code keys
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 135 |
```
|
| 136 |
|
| 137 |
+
## 10. Future Work
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 138 |
|
| 139 |
+
Complete manual gold-set annotation → re-evaluate reranker against it → consider a medical-domain reranker → expand confidence-gate validation set → add citations/monitoring/auth → deploy behind a REST API → evaluate faithfulness/hallucination separately from retrieval quality.
|
|
|
|
|
|
|
|
|
|
| 140 |
|
| 141 |
+
## 11. Responsible Use
|
| 142 |
|
| 143 |
+
Not a diagnostic tool, clinical decision-support system, prescription system, or emergency service. Consult a qualified healthcare professional for medical decisions.
|
|
|
|
|
|
|
|
|
|
| 144 |
|
| 145 |
+
## References
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 146 |
|
| 147 |
+
- [Sentence Transformers](https://www.sbert.net/)
|
| 148 |
+
- [FAISS](https://github.com/facebookresearch/faiss)
|
| 149 |
+
- [Cross-Encoder](https://www.sbert.net/examples/applications/cross-encoder/README.html)
|