00 Narrative summary — what this demonstrates
This project shows a working, end-to-end path from a clinician's plain-language question to a coded, auditable cohort answer — and it runs live in the browser against real FHIR data, with no backend server of its own. In one sentence: type a clinical phrase, get back a real patient cohort matched by SNOMED CT code, pre-computed cohort analytics, an interactive trajectory graph, and two data-driven report templates — with every step traceable back to a code, a query, or a record, never to a generated guess.
The system deliberately keeps generative AI out of anything that touches clinical facts. The one place language modeling is allowed to help is turning a free-text phrase into a SNOMED CT concept — and even that step is implemented today as a deterministic lookup table, not a model call, so the same phrase always resolves to the same code. Everything downstream — cohort counts, hub nodes, timelines, brief and report content — is a direct read from computed data. That discipline is what makes the output auditable: two people running the same query get the same answer, and every number on screen can be traced back to a FHIR resource or a SNOMED code.
How to build this, phase by phase
- Phase 1 — FHIR connectivity. Build one shared client with a base URL and standard headers, add retry-with-backoff for rate limits and transient errors, and write thin typed search wrappers for
Condition,Encounter,Procedure,MedicationRequest,Observation, andPatient. Prove that SNOMED-coded token search works, including comma-joined OR queries. Do this first — nothing else works without a reliable data pipe. - Phase 2 — SNOMED CT layer. Curate a local concept dictionary (50–100 concepts is enough to start) covering your target clinical domains, and support ECL-style descendant (
<<) expansion against it. Build the phrase-to-concept extractor as an explicit lookup table, not a model — this is what keeps the whole system auditable. - Phase 3 — Query translation. Turn selected concept IDs into
http://snomed.info/sct|<id>tokens, join them for OR semantics, and assemble a real FHIR search URL with pagination support. Confirm the full round trip: phrase → concept → optional ECL expansion → token param → live query → rendered patient results. - Phase 4 — Cohort analytics layer. Define a schema for presentation clusters, decision/hub nodes, intervention timelines, and tracking correlations, then batch-compute and cache these keyed by concept ID so they aren't recomputed per query. This is the layer that turns a flat list of matched patients into something clinically meaningful.
- Phase 5 — Output templates. Build the Clinical Intelligence Brief and Insight Report as fixed templates whose fields are direct lookups into Phase 4's analytics — never generated prose describing clinical facts. Wire in any ready-made query findings so exploratory analysis and formal output stay connected.
- Phase 6 — Visualization. Build a single-patient trajectory view (comorbidity → symptom → diagnosis → intervention → escalation → outcome, timed by day-offset) and a cohort-level radial graph with a trend-over-time toggle, so a clinician can move fluidly between the population view and any one patient's story.
- Phase 7 — Integration and polish. Collapse the pipeline behind one "Run query" action, add an explicit "unresolved concept" fallback instead of ever guessing silently, and preload a couple of ready-run example queries so a first-time user can see the whole thing work before typing anything.
Clinical analytics this pipeline enables
- Cohort discovery by code, not keyword — a plain-language question resolves to SNOMED CT concepts (with optional descendant expansion) and returns the real patients who match, sourced live from a FHIR server.
- Presentation clustering — comorbidities, symptoms, and findings that co-occur with the seed diagnosis across the matched cohort.
- Decision / hub-node identification — the clinical junctions (interventions, escalations) that a meaningful share of the cohort passes through, highlighted in the trajectory graph.
- Intervention timelines — what treatments and procedures happen, in what order, and how long after diagnosis, per patient and aggregated across the cohort.
- Readymade cohort queries — top comorbidities, most-tracked symptoms, escalation rate, average interventions per patient, and top intervention types, computed directly from the matched set.
- Advanced cohort analytics — comorbidity co-occurrence pairs, time-from-diagnosis-to-escalation, polypharmacy burden, symptom-to-diagnosis proximity, and composite risk stratification scoring.
- Backward (case-level and cohort-level) analysis — tracing outcomes back through the intervention and symptom history that preceded them, for a single patient or the whole cohort.
- Trend-over-time view — monthly onset and escalation counts per hub node, so a cluster's behavior can be read as a trend rather than a single snapshot.
- Template-generated clinical outputs — a Clinical Intelligence Brief (match %, active hub node, presentation metrics, intervention, predicted outcome) and an Insight Report (Presentation / Intervention / Tracking & Correlation), both filled from computed data rather than generated text.
- Offline fallback dataset — a bundled Pneumonia cohort so every analytic above can be explored end to end even if the live sandbox is slow or unreachable.
Condition.code into the FHIR search itself
(compound/negated phrases are handled by aggregating after the fetch, not in the query), and the
readymade query templates currently only run against the bundled offline Pneumonia dataset rather
than against any concept fetched live. See the risks section below for the full list.
01 The idea
Build a real, working system that takes a clinician's plain-language bedside question and turns it into a coded, auditable cohort query — not a free-text guess.
- Extract clinical concepts from the question and map them to canonical SNOMED CT codes.
- Compose those concepts into a formal ECL (Expression Constraint Language) cohort query.
- Run that query as a live lookup against real patient resources on a FHIR server.
- Return a matched cohort with pre-computed structure — presentation clusters, hub/decision nodes, intervention timelines.
- Render a standardized Clinical Intelligence Brief and Insight Report — template-filled from data, never generated prose for clinical facts.
- Let the clinician explore an interactive cohort trajectory graph.
This is a real-time, auditable front door onto existing evidence — not a replacement for RCTs or systematic reviews. Concepts must be coded, not just written, so "hub node" and "cohort match" mean the same thing on every run.
02 Data source: SMART Health IT public sandbox
Decision locked in — no server setup required to start.
| Property | Value |
|---|---|
| Endpoint | https://r4.smarthealthit.org |
| FHIR version | R4 |
| Auth | None needed for open reads — standard REST search |
| Data | Pre-loaded synthetic (Synthea-style) patients maintained by SMART Health IT |
| Search pattern | Condition?code=http://snomed.info/sct|<conceptId> |
03 System architecture
Design discipline carried over from the reference demo: LLMs are permitted only in the constrained NL→concept extraction step — never to invent cohort stats, hub nodes, or outcomes.
04 Step-wise execution plan
FHIR connectivity — implemented in the live demo below
- One shared client (
fhirFetch) instead of each call site building its ownfetch()— centralizes the base URL (https://r4.smarthealthit.org) and theAccept: application/fhir+jsonheader. - Retry with exponential backoff (2 retries, 400ms/800ms) on rate-limit (429) and transient server errors (5xx) — the shared sandbox does both occasionally. 404/400s fail fast, no pointless retry.
- Thin search wrappers for all five resource types the pipeline touches:
Condition,Encounter,Procedure,MedicationRequest,Observation, plusPatient—searchCondition/searchEncounter/etc., all built onfhirSearchUrl(resourceType, params)so query-param assembly isn't duplicated per call site. - Confirmed code-based filtering works:
code=http://snomed.info/sct|<id>, including comma-joined multi-token OR queries for ECL expansion. - Known gap: no request de-duplication — if two UI actions trigger the same patient's data at once, both hit the network. Fine at demo scale (session cache in Phase 4 covers the common repeat-query case), but a real deployment would want an in-flight request cache too.
SNOMED CT layer
- Start with a curated local concept dictionary (~50–100 concepts) covering the demo's clinical domains.
- Validate ECL-style expansion (
<<descendant-or-self) against the curated set before reaching for a full terminology server. - Build the deterministic NL→concept extractor as a phrase → concept-ID lookup table — auditable, not generative.
Query translation — implemented in the live demo below
- Concept(s) selected in Phase 2 → one or more
http://snomed.info/sct|<id>tokens, comma-joined into a single OR'd FHIR token param — this is what "ECL<<descendant-or-self" resolves to once translated (seeuseEcl/tokenParamin the demo'srunBtnhandler). - Tokens assembled into a real search URL:
GET /Condition?code=<tokens>&_count=10&_include=Condition:patient— the exact request shown in the "Live · runs in your browser" badge above and echoed instatuswhile it runs. - Round trip confirmed end to end: phrase → concept → (optional) ECL expansion → token param → live sandbox query → matched
Patientresources rendered as results, with pagination via FHIR'snextlink (fetchPage/"Load next 10"). - Known gap: only
Condition.codeis translated today. Query translation for compound/negated phrases (e.g. "pneumonia without diabetes", symptom + intervention combined in one filter) isn't built — cohort analytics currently gets that combination by aggregating after the fetch, not by encoding it into the FHIR search itself. Extending translation toObservation/Procedure/MedicationRequestsearch params directly (rather than always fetching all four per patient in Phase 4) is the next real step here.
Cohort analytics layer
- Define the schema: presentation clusters, decision/hub nodes, intervention timelines, tracking correlations.
- Batch-compute these offline and cache them, keyed by concept ID — not recomputed per query.
Output templates — implemented in the live demo below
- Clinical Intelligence Brief: match %, active hub node, presentation metrics, intervention, predicted outcome — all direct lookups from cohort analytics (Phase 4).
- Insight Report: Presentation / Intervention / Tracking & Correlation — data reads, not generated prose.
- Now wired to the readymade queries: every readymade query result (top comorbidities, symptom panel, escalation rate, avg interventions, top intervention types) is logged in-session (
pnaResultsLog), de-duped by title, and pulled into a "Readymade query findings" section inside both the Brief and Insight Report whenever the seed concept is Pneumonia — so running a readymade query and then generating a report actually carries that finding into the output, instead of the two surfaces staying disconnected. - Known gap: the readymade queries themselves only run against the bundled offline Pneumonia dataset — the live sandbox path (any other concept, or Pneumonia fetched live rather than from the bundle) doesn't get this section. Generalizing the five query patterns to run against
lastCohortMetafor any concept, not just the hardcoded offline set, is the natural next step.
Visualization
- Single-patient case view: comorbidity → symptom → diagnosis → intervention → escalation → outcome, timed by day-offset.
- Cohort trajectory graph: nodes sized by patient count, colored by outcome rate, hub node highlighted, clickable for per-node detail; toggle to a trend-over-time view plotting monthly onset/escalation counts per node.
Integration & polish
- Wire the pipeline behind a single "Run query" action.
- Add an "unresolved concept → say so" fallback instead of a silent guess.
- Preload the two reference example queries as ready-run demos.
05 Live demo
This queries the real SMART Health IT sandbox from your browser, right now — Phases 1 through 7 of the plan above, working end to end: FHIR connectivity, a curated SNOMED CT dictionary with ECL-style descendant expansion across all ten concepts, query translation, cached cohort analytics with per-node escalation/intervention rollups, a cohort trajectory graph, Clinical Intelligence Brief / Insight Report output templates, single-patient trajectory visualization, and preloaded example queries — plus a bundled offline Pneumonia dataset below so the demo still works if the shared sandbox is slow, empty, or unreachable.
The public sandbox above is a shared, rate-limited pool — cohort size and uptime aren't guaranteed. This bundled Pneumonia cohort was captured from that same pipeline (each file is exactly what the "Download patient data" button below produces) and works entirely offline, from data embedded in this page. Click a patient for their whole-case trajectory, or run cohort analytics across all six.
Readymade template queries — each is a fixed lookup over the bundled 6-patient Pneumonia cohort (no free text, nothing generated):
Deeper cross-cuts — these join across lanes/dates instead of counting a single lane:
Data → query, not query → data
Every button above runs in the normal direction: a clinician (or Anthropic, when we wrote the template) decides the question first, then the query filters the data down to an answer. This step runs backward — it scans the raw timeline data first, finds what's actually there (dominant patterns, coverage gaps, what preceded an escalation, which factors correlate with worse outcomes), and only then synthesizes the query statement that would reproduce that finding. The query is the output of looking at the data, not the input to it — flagged with the ⟲ badge everywhere it shows up so it's never confused with an analyst-authored lookup.
1. Type a clinical phrase (deterministic phrase → concept lookup — auditable, not generative), or pick a concept below:
Pick a concept above, then run the query to see real matching patients from the sandbox.
3. Click any patient above to see their whole-case trajectory: comorbidity → symptom → diagnosis → intervention → escalation → outcome.
06 Open risks / things to watch
- Shared sandbox data: the SMART Health IT pool isn't ours — cohort sizes for any given concept may be small or absent, and the dataset can change over time.
- SNOMED CT licensing: a full terminology + ECL server typically needs a UMLS Metathesaurus license (free for US-affiliated use, registration required).
- Deterministic NER accuracy: dictionary-based extraction is auditable but brittle on real phrasing — plan for an explicit "unresolved" path.
- Cohort analytics validity: "hub nodes" and correlations are only as good as cohort size and data quality — label outputs as illustrative until validated at scale.
- Clinical safety: this is a decision-support/aggregation tool, not a diagnostic system — any real deployment needs clear scope boundaries and clinical governance review.