nsr51324 commited on
Commit
9cc7df5
·
verified ·
1 Parent(s): 76b2481

Update README.md

Browse files
Files changed (1) hide show
  1. README.md +62 -1109
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
- - 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
@@ -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. 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.
 
 
 
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)