Inspiration
Someone I know had a knee replacement and spent the first week terrified. Not of the surgery — of the silence afterwards. You leave hospital with a folder of paperwork, the next appointment is a fortnight away, and then at 11pm something in your leg doesn't feel right.
At that moment you have three options and all three fail you. Search hands you the worst case first — two scrolls into any post-surgical symptom and you're reading about clots and sepsis, more frightened and no better informed. A general assistant won't commit — it's fluent, calm, and ends exactly where you began: this could be normal, but it could also be serious, please consult your doctor. The emergency room will answer, and charges you a night, a bill and your dignity to do it; nobody wants to be the person who came in over a bruise.
So most people take the fourth option nobody lists: wait, and don't sleep.
The question at 11pm isn't what is this symptom. It's how worried should I be, right now, given everything about me — and answering that needs your record, which none of those tools has.
What it does
Meantime is a precision alert system for the hours you're on your own. It has
already read the patient's discharge summary. It commits to one of five urgency
levels — EMERGENCY, URGENT, CONTACT_TEAM, MONITOR, UNCERTAIN — tells
them what to do, and sends the escalation to a named human. When nothing matches,
it says so and gives them the nurse line. It never guesses to make someone feel
better.
It covers three phases of the same patient's care: before any treatment, waiting for surgery (where a sore throat can mean the operation is postponed), and after discharge. The same sentence gets a different answer in each.
How I built it
The order mattered more than anything else: the rule engine first, with tests, before a single line of LLM code.
engine.py imports nothing but re. It takes a structured symptom report, a
patient context and 44 curated rules, and returns a level. No network, no model,
same answer every time. Each rule cites MedlinePlus (NIH/NLM), the NHS, AAOS
OrthoInfo, the patient's own discharge summary, or the 988 Lifeline.
Only then did Claude Sonnet 5 come in, on the Messages API with a hand-written tool loop and eight tools. The model's entire job is understanding language — converting what a frightened person typed into canonical symptom names. It is not permitted to set the urgency level at all.
The narrowing is set arithmetic, which is exactly why it's auditable:
$$\text{level} = \max_{\text{rank}}\ {\, r \in R \;:\; \text{phase}(r) \wedge \text{procedure}(r) \wedge (T_r \cap S \neq \emptyset) \wedge (X_r \cap S = \emptyset) \,}$$
where $S$ is what the patient said, $T_r$ a rule's triggers and $X_r$ its exclusions. For "my knee is swollen and my calf hurts" that runs 44 → 31 → 2 → 1, and the dashboard shows it happening.
Five public APIs supply the evidence live — MedlinePlus Connect keyed on the patient's real ICD-10 codes, the MedlinePlus web service, RxNav/RxNorm, openFDA and ClinicalTrials.gov. No keys, no registration.
Challenges I ran into
A prompt rule is not an enforcement mechanism. I wrote "you must always call
assess_urgency" into the system prompt. It failed three separate times in live
testing — once on a hot, swollen knee with a fever, where the model asked a
sensible clarifying question and left the patient with no verdict at all. I
moved both safety invariants out of prose and into the loop: a turn that ends
without running the rules gets sent back, and so does one that ends UNCERTAIN
without looking anything up.
The hard case is reassurance, not alarm. Anyone can build something that shouts 911. Telling a frightened man on a blood thinner that his blackened leg is expected — and being right — is the difficult behaviour. "Swelling is normal after knee surgery" is perfectly true, and the wrong answer the moment the swelling is in the calf. That single exclusion is the difference between reassurance and a same-day clot check.
Deciding what not to make live. I wanted everything pulled from APIs. But no public endpoint publishes machine-readable post-operative red-flag thresholds, and putting a network call in the safety path would make "is this an emergency" depend on someone else's uptime. So: live content, local decision. A fetched page can never move the level, in either direction — which I proved by asking about nightly leg cramps, watching it fetch "these are common and harmless", and watching it hold the helpline anyway.
Dynamic meant two different things. The filtering was real from early on and completely invisible, so "the rules are dynamic" was a claim rather than something you could watch. The rule library itself is static and hand-reviewed — and that's deliberate. A system that drafts its own triage thresholds from patient language drifts, and it drifts toward reassurance, because nobody complains about being wrongly told they were fine.
What I learned
Where you put the model is the whole architecture. Language models are extraordinary at understanding a worried person and terrible as something you can audit — so give them the half they're good at and a boundary they cannot cross. Every urgency decision here traces to a numbered line in a JSON file with a citation a surgeon could read.
I also learned that "I don't know" has to be a first-class answer. Most symptom checkers have no way to say it, so they guess fluently. Here, nothing matching is never "you're fine" — it's a phone number.
What's next
Candidate rules: when live lookups keep firing for something uncovered, draft
a rule from the fetched source, mark it UNREVIEWED, and never let it fire until
a clinician approves it. The library grows from real patient language; the
approval gate is the point.
Then a second procedure — the phase machinery is already procedure-agnostic — and voice input, because typing at 11pm in pain is a real barrier for exactly the person this is for.
The patient, clinicians and all 555-prefixed numbers are synthetic. The rules are adapted from public patient-education material, not a licensed triage protocol, and would need clinical sign-off before real use.
Built With
- ai
- clinical-decision-support
- clinicaltrials.gov
- healthcare
- icd-10
- medlineplus
- medlineplus-connect
- models
- nih
- nlm
- openfda
- patient-safety
- pytest
- python
- python-dotenv
- rules-engine
- rxnav
- rxnorm
- sqlite
- streamlit
- tool-use
- triage
Log in or sign up for Devpost to join the conversation.