Inspiration

TB still kills more than a million people a year, and the clinics carrying that burden are usually the ones with no reliable internet. Every "AI-powered" screening tool we looked at assumed a cloud connection that simply is not there when you need it. So we set ourselves one constraint and refused to break it: a real clinical assistant (one that reads chest X-rays, explains itself, and speaks the patient's language) had to run entirely on a basic 8 GB laptop with the Wi-Fi off. TBScreen is our proof that it can.

What it does

A health worker opens TBScreen, uploads a chest X-ray, and optionally adds what they already know about the patient: age, how long the cough has lasted, symptoms, HIV status, TB contact at home. In under half a minute, all offline, they get:

  • a TB risk score from an ONNX vision model
  • a deterministic WHO-aligned triage decision: refer, retest, follow up, or monitor
  • a plain-language explanation of what the score means for this patient, with citations to WHO guidance
  • a Clinical Q&A chat: ask follow-ups ("can you explain the results better to me?") and get answers grounded in the WHO knowledge base, personalized to the screening when one is on screen
  • everything available in English, Yorùbá, Hausa, or Igbo

Every response carries a "screening, not diagnosis" disclaimer, and a negative screen never clears a symptomatic patient. That rule is in code, not in the model's judgment.

How we built it

The decision that shaped the whole architecture: the LLM makes no safety decisions and never sees pixels. A 4.4 MB MobileNetV3 model (ONNX Runtime, CPU) produces the TB probability $p_{\mathrm{TB}}$, and the screen flips to positive at $p_{\mathrm{TB}} \geq 0.65$. Deterministic Python applies the WHO triage rules (including the ones models get wrong, like refusing to clear a symptomatic patient on a negative X-ray). A hand-rolled TF-IDF retriever pulls from 98 WHO guideline passages across the four languages (the tokenizer preserves Yorùbá and Igbo diacritics, which most tokenizers quietly destroy). In concrete terms, a term is weighted with smoothed TF-IDF and a query ranks against passages by cosine; because every vector is L2-normalized, the cosine is just a dot product:

$$\mathrm{tfidf}(t,d)=\frac{c(t,d)}{|d|}\left(\log\frac{1+N}{1+\mathrm{df}(t)}+1\right),\qquad \mathrm{sim}(q,d)=\hat v_q\cdot\hat v_d$$

Only then does a quantized Gemma 4 E2B (GGUF Q4_K_M via llama.cpp) do the one thing it's actually good at: turning all of that into structured, readable language the health worker chose.

We ran a genuine bake-off before committing: Gemma against Qwen, Llama 3.2, and Cohere's Aya, scored on YAML parse rate, triage correctness, citation honesty, and peak memory. Gemma won on merit, but two rivals lost for instructive reasons: Qwen's newest architecture wouldn't even load in llama.cpp, and Aya's research-only license blocked any real-world deployment.

Because an LLM in a clinical loop will sometimes invent things, we treat its output as untrusted. Every citation is checked against the passages actually retrieved (invented ones are stripped), and if the model forgets the disclaimer, the code injects it before anyone sees the answer.

Challenges we ran into

Cross-lingual retrieval without an embedding server. We solved it with a diacritic-aware TF-IDF index plus a cross-lingual topic boost: score the English passages, then lift same-topic passages in the asked-for language ($+0.3$ on the cosine score).

Fitting a clinical stack under 7 GB of RAM. Q4_K_M quantization, lazy model loading, and CPU-only inference keep the whole app near 3.1 GB.

Trusting an LLM clinically. We stopped trying to make the model safer and made it smaller in the pipeline instead: deterministic code owns every decision, retrieval owns every fact, and the model owns only the words.

Localizing WHO guidance. Our Yorùbá, Hausa, and Igbo passages are careful paraphrases, and we say so: they're flagged for native clinician review rather than passed off as verified translations.

Accomplishments that we're proud of

  • 100% offline end to end. No cloud calls, no CDN fonts, no telemetry. Unplug the network and everything still works.
  • The full stack peaks around 3.1 GB, comfortably inside the 8 GB target with room to spare. On the official profiler run, the efficiency score works out to $\mathrm{Seff} = \frac{7 - 2.135}{7} \times 100 \approx 69.5$.
  • An automated harness that checks YAML structure, triage correctness, citation honesty, and disclaimer presence across all four languages on every change.
  • We report the ugly numbers too: the vision model's 60% specificity on our smoke set, and the unvalidated status of our localized passages. A screening tool that hides its weaknesses isn't decision support, it's marketing.

What we learned

Small quantized models are far more capable than we expected once you constrain their job. The biggest reliability win wasn't prompt engineering. It was architecture: decisions in code, facts in retrieval, and the LLM purely as the language layer. We also learned that model selection is a portability problem as much as a quality problem; a smarter model that won't load in the judge's runtime scores exactly zero.

What's next for TBScreen

  • Native clinician review of the Yorùbá, Hausa, and Igbo passages
  • Validating the vision model on a larger, properly labelled dataset

Built With

Share this project:

Updates

Submission history