A reference implementation of the technology stack behind AI-assisted clinical documentation: a multi-agent pipeline that turns a doctor's raw, fragmented FHIR chart into a single, condition-centric pre-fill form — de-identified, evidence-grounded, hallucination-checked, and RBAC-gated, before a clinician ever sees it. Every component runs end to end against a real local Postgres database and either bundled synthetic FHIR data or a live HAPI FHIR Docker server — no mocked responses over fake data.
| Metric | Value |
|---|---|
| Agents | 11 (LangGraph StateGraph, RBAC-gated) |
| Standards | HL7 v2, FHIR R4 |
| Backend | FastAPI, PostgreSQL/SQLAlchemy |
| AI | OpenAI GPT-4o |
| Vector DB | Qdrant (RAG) |
| Tests | 426 passing, offline by default |
| Security | JWT (HS256), AES-256-GCM, sha256 audit hash-chain, RBAC |
| Status | Reference implementation — not yet deployed to a real hospital |
EMR Agentic AI is a reference implementation of the technology stack behind AI-assisted clinical documentation: a multi-agent pipeline that turns a doctor's raw, fragmented FHIR chart into a single, condition-centric pre-fill form — de-identified, evidence-grounded, hallucination-checked, and RBAC-gated, before a clinician ever sees it. It was built to demonstrate the exact skill set a healthcare AI engineering role requires: clinical workflow translation, FHIR/HL7 interoperability, agentic pipeline architecture, and the judgment to build a system that is trustworthy, not just impressive, in a domain with near-zero tolerance for a hallucinated diagnosis or a leaked identifier.
Every component is real and runs end to end against a local Postgres database and either bundled synthetic FHIR data or a live HAPI FHIR Docker server — no mocked responses over fake data.
A doctor loses 2–4 hours a day to manual EMR data entry, and the reasons don't fix themselves independently:
The same data is re-typed across 6+ screens because nothing pre-populates from what the system already knows.
Feeding a raw chart straight into an LLM produces hallucinations; data has to be cleaned and structured first.
An LLM answer is only useful if it's traceable back to a real source in the chart.
Data organized by type (Labs tab, Meds tab) forces the clinician to mentally reassemble the picture; real clinical reasoning is organized by condition.
The hard part is that closing these gaps requires an AI pipeline touching real patient data, in a domain with near-zero tolerance for the two things generative AI is worst at by default: making things up, and seeing information it shouldn't have touched.
An 11-agent LangGraph StateGraph, RBAC-gated so each role only runs the agents its permissions cover:
| Layer | Technology |
|---|---|
| Backend | FastAPI, PostgreSQL, SQLAlchemy |
| Orchestration | LangGraph (StateGraph) |
| AI / Reasoning | OpenAI GPT-4o |
| Retrieval | Qdrant (RAG) |
| Interoperability | HAPI FHIR (R4), hl7apy (HL7 v2) |
| Auth | JWT (python-jose, HS256) |
| Testing | pytest (426 tests) |
The build was sequenced across five phases, each clearing a named architectural gate before the next workstream was allowed to build on it.
A pluggable FHIR client, a Phase-1 stub pipeline, a condition-centric response schema built directly from real nurse-practitioner workflow feedback, and a real React/TypeScript clinician UI wired to the API.
Real 18-identifier stripping and free-text NER redaction, a HIPAA audit log, and stable pseudonymous patient identity via an HMAC blind-index lookup.
Migrated to a LangGraph StateGraph with RBAC-driven routing; added Timeline Chronicler, Qdrant-backed Evidence Retrieval, a Clinical Risk Detector, and a real HAPI FHIR Docker backend.
Added evidence-grounded differential diagnosis and a Verification Agent with a numeric confidence score; swapped hardcoded ICD-10/LOINC dictionaries for a live terminology service — which surfaced a real coding-safety bug (see Challenges below).
Replaced an honor-system role parameter with a verified JWT; built a from-scratch HL7 v2 → FHIR translator; closed with an RBAC allow-list fix, a tamper-evident audit hash-chain, rate limiting, and security headers.
PHI is always stripped before anything reaches the LLM, regardless of caller role. Whether a clinician's own UI may show a patient's real name is a separate, narrowly-scoped RBAC decision — conflating the two is a common healthcare-AI design mistake.
The same patient needed the same pseudonymous ID across visits, without ever storing a queryable plaintext MRN. Solved with an HMAC-SHA256 blind index — an equality lookup and uniqueness constraint without ever decrypting the encrypted MRN column.
A deny-list only rejected one role, silently letting a later-added machine-credential role fall through to the full patient list. Fixed by inverting to an explicit allow-list, with every denial audited — a deny-list fails safe until the next role is added; an allow-list fails safe by construction.
Roughly 30 call sites across the pipeline's audit-writing code were passing the role string ("doctor") as the acting user's ID — the audit trail could show what happened and what role did it, but never who. In a real HIPAA audit trail, that's the difference between "a doctor edited this record" and "Dr. Jones edited this record." Fixed by threading a real user_id, distinct from role, through the entire pipeline.
Rather than asking the Reasoning Agent to "please cite your sources," a separate Verification Agent independently cross-checks every claim against the actual retrieved evidence — deterministically first, falling back to an LLM faithfulness judgment only when the deterministic check can't resolve it. A model grading its own homework is a weaker safety property than a second, narrower system whose only job is checking the first one's work.
ResponseValidationError invisible to the entire test suite.
GET /prefill/{id} degraded correctly to a stub response for a low-access role, but FastAPI's response_model validated independently of what the handler returned and rejected it with a 500 — invisible to every test because they all called the pipeline function directly, bypassing FastAPI's response layer. Caught only by adding real HTTP tests. The lesson: verify through the interface a user actually touches, not just the function underneath it.
After adding a sha256 hash chain to the audit log, the first live run against real Postgres flagged every row as tampered. Root cause: the hash was computed from a timezone-aware timestamp at insert time, but the column itself is timezone-naive — Postgres silently drops the timezone, so re-reading the row produces a different string than what was hashed. Fixed by normalizing the timestamp consistently before hashing, with a regression test pinning the exact case. The lesson: a live pass against a real database is not optional even when unit tests are green.
After swapping a hardcoded ICD-10 dictionary for a live NLM Clinical Tables lookup, live testing surfaced spurious "ICD-10 conflict" flags against already-correctly-coded conditions. The API's raw #1 search hit doesn't reliably rank the generic/canonical code first — the same bug would have silently miscoded any new condition with nothing existing to catch the mismatch against. Fixed with a safety-conservative disambiguation pass that penalizes subtype/etiology qualifiers, and raises an explicit "ambiguous — needs manual review" flag rather than guessing when it still can't settle on one candidate.
Mapped explicitly against HIPAA's technical safeguards:
Just as important, this is stated plainly: TLS termination, KMS/HSM-managed keys, storage-layer encryption at rest, and MFA/SSO are deliberately deferred, built only when a real deployment target exists rather than speculatively. There has been no professional security audit yet.
Regulator-facing audit endpoint with role-based access control and patient-level filtering.
Tamper-evident audit chain verification confirming log integrity across the complete audit trail.
Stated deliberately, because a system that hides its limitations is worth less than one that states them:
Whether you're building an AI-assisted EMR, a clinical workflow automation platform, or a healthcare interoperability solution, we're always interested in discussing secure, agentic AI architectures.