Back to projects
Case Study · Side Project · Health Tech

Health Analyzer

An offline AI health tracker. Upload a lab report, an OCR pipeline pulls every marker out of the PDF, and a locally-running Llama 3.1 explains — in plain language — what sits outside the reference range. Nothing leaves the laptop.

Problem

Twice a year I get a stack of lab results — twelve PDFs, fifty markers, three different reference-range conventions. The interesting story is rarely a single bad value; it's the trend across two years. But I'm not about to paste my full blood panel into ChatGPT, and the SaaS trackers that promise this either charge €15/month or quietly forward your data to a third-party API.

Why local Ollama

Health data is the textbook case for staying on-device. Hormone panels, lipid trends, anything liver-related — once it leaves your machine, you don't get it back. Running Llama 3.1 8B through Ollama means the model loads in 4 GB of RAM, runs at ~30 tokens/s on an M2 Air, and the only network traffic the app makes is checking for Ollama on localhost:11434. No accounts, no API keys, no cloud bill that scales with how curious I am that week.

How it works — OCR + bulk import

Drop a folder of PDFs onto the import zone. Tesseract OCR scans each page, a small parser detects the marker name + value + unit + reference range (Czech and German labs use different layouts, so there's a per-lab template registry). Markers get normalised, deduplicated against history, and stored in Postgres. Once a year's worth is in, you click "Explain" — the app builds a structured markdown summary (current value vs. range vs. 12-month trend) and ships that, not the raw PDFs, to the local LLM. The model returns three sections: what to watch, what to ignore, what to ask the GP.

Tech Stack

Next.js 15
Ollama
Llama 3.1
Tesseract OCR
PostgreSQL
Tailwind

Key Features

100% local inference

Llama 3.1 8B via Ollama on localhost. No API keys, no rate limits, no subscription. Works on a plane.

Bulk PDF import + OCR

Drag a folder of lab PDFs onto the import zone. Tesseract pulls text, a per-lab parser extracts marker name + value + unit + range.

Trend analysis across years

Each marker is normalised and stored over time, so you see whether ferritin is genuinely drifting or just noisy week-to-week.

Plain-language explanations

The LLM gets a structured summary, not the raw PDFs. It returns three sections: what to watch, what to ignore, what to ask the GP.

Timeline — how it grew

  1. Evening 1

    Painful spike: paste one lab PDF into Claude and ask for analysis. The output was great. The €0.40 per request was not the issue — sending the panel to a third party was.

  2. Evening 2

    Installed Ollama, pulled llama3.1:8b, wired up a tiny Next.js page that POSTs to localhost:11434. First local response in 6 seconds.

  3. Weekend 1

    OCR pipeline with Tesseract. First three Czech lab templates parsed cleanly; the German one needed a per-lab regex.

  4. Weekend 2

    Postgres schema for markers + history. Bulk import of 18 months of PDFs. First trend view.

  5. Evening (later)

    Prompt engineering: structured markdown summary, few-shot examples for tone, three-section output. Quality jumped past the original cloud version.

  6. Ongoing

    Adding lab templates as I encounter new layouts. Next: an Ollama-based natural-language query layer ("show me ferritin since 2023").

Numbers

100%
Local inference
€0
Monthly API bill
4 GB
RAM for the model
~30
Tokens / second on M2
50+
Markers tracked
0
Bytes leaving the laptop

Screenshots

Bulk import — drop a folder of PDFs

Screenshots redacted — real lab data inside

Marker trend view, 12 months

Screenshots redacted — real lab data inside

LLM explanation panel — three sections

Screenshots redacted — real lab data inside

Per-lab template editor

Screenshots redacted — real lab data inside

What I learned

Local-first changes what you let yourself store

When inference costs €0 and data never leaves the laptop, you stop self-censoring what you import. The dataset got richer because the privacy cost dropped to zero.

Prompt > parameter count, again

Llama 3.1 8B with a structured summary beats GPT-4 with raw PDFs. The win was in the input shape, not the model size — same lesson I keep relearning.

OCR is the unsexy 80%

I budgeted a weekend for the LLM and an evening for OCR. It was the other way around. Lab PDFs have wildly inconsistent layouts; a per-lab template registry was unavoidable.

Health data deserves a higher bar than 'we encrypt in transit'

Most SaaS trackers will happily process your blood panel through a third-party LLM. I'm not willing to assume that ends well. Local-first wasn't a feature — it was the whole reason to build it.

Want the build journal?

Read the technical write-up