Building autonomous clinical workflows requires moving beyond basic conversational interfaces to direct system integration. When deploying LLM-driven agents into clinical environments, the goal is automating structured data entry into Electronic Health Record (EHR) systems via HL7 FHIR (Fast Healthcare Interoperability Resources) APIs.
However, connecting probabilistic models directly to production EHR write endpoints introduces severe architectural risks.
Consider an autonomous intake agent listening to patient check-in dialogue. The patient states: βI take 50 micrograms of levothyroxine every morning on an empty stomach.β
The modelβs function-calling pipeline parses the utterance, constructs a MedicationRequest JSON payload, and posts it to the EHR API. The HTTP gateway responds with 200 OK.
Yet, when the attending physician reviews the chart, the medication is recorded as 50 mgβa thousand-fold overdose.
The root cause was not an overt hallucination. It was a silent schema coercion bug during model output serialization where metric prefixes were dropped and accepted by a permissive ingestion gateway.
+--------------------------------------------------------------------------------------------------+| THE SILENT EHR DOSAGE WRITE FAILURE IN NAIVE AGENT PIPELINES |+--------------------------------------------------------------------------------------------------+[Patient Dialogue] βββΊ "50 micrograms of Levothyroxine daily" β βΌ [LLM Extraction] βββΊ Probabilistic JSON Generation β βΌ [Malformed Payload]βββΊ {"medication": "Levothyroxine", "value": 50, "unit": "mg"} <-- [CORRUPTION] β βΌ [Permissive Gateway]βββΊ Accepts Generic String / Incomplete Schema Validation β βΌ [Production EHR] βββΊ Committed directly to Active Patient Chart (HTTP 200 OK) β βΌ [Clinical Impact] βββΊ 1,000x Overdose Risk Generated Silently
Integrating LLMs with FHIR resources (such as MedicationRequest, Observation, and Condition) exposes three major architectural vulnerabilities:
LLMs generate text probabilistically based on token prediction rather than deterministic type checking. When mapping conversational clinical text into a FHIR Dosage structure, models frequently compress nested objects.
A complete FHIR doseAndRate object requires explicit distinction between value, unit, system, and code (e.g., using Unified Code for Units of Measure / UCUM):
"doseQuantity": { "value": 50, "unit": "ug", "system": "http://unitsofmeasure.org", "code": "ug"}
Under token pressure or context fragmentation, the extraction model often simplifies this to:
"doseQuantity": { "value": 50, "unit": "mg"}
FHIR servers are designed to handle heterogeneous data from legacy hospital systems. Many endpoints accept raw text strings within the dosageInstruction.text field to maintain backward compatibility.
If an autonomous agent submits a structurally incomplete or unit-corrupted payload, the API gateway often coerces the type or falls back to text parsing rather than throwing an HTTP 400 Bad Request. The malformed record commits silently.
Naive agent architectures grant autonomous agents direct write access to live database tables. When an error occurs, there is no intermediate staging layer or human-in-the-loop verification gate to intercept the corrupted state before it impacts active patient care.
To eliminate dosage corruption and unauthorized clinical mutations, write-backs must be governed at the infrastructure layer through Gateway-Enforced Pydantic Schema Contracts and Draft-State Database Isolation.
STATEFUL CLINICAL WRITE-BACK CONTROL TOWER [Unstructured Clinical Dialogue / Note] β βΌββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ LLM EXTRACTION ENGINE ββ Extracts Raw Clinical Entities & Proposes FHIR Payload ββββββββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββ β βΌββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ GATEWAY PYDANTIC VALIDATION PROXY ββ β’ Enforce Strict Type Safety & Forbid Unregistered Extra Keys ββ β’ Validate Units Against UCUM Ontologies (Reject Ambiguous Units) ββ β’ Resolve Medication Strings Against Standardized RxNorm Concepts ββββββββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββ β [Validation Successful?] / \ YES/ \NO βΌ βΌββββββββββββββββββββββββββββββββββββββββββ ββββββββββββββββββββββββββββββββββββ ISOLATED DRAFT-STATE REPOSITORY β β EXECUTION CIRCUIT BREAKER ββ β’ Commit to Staging Database Only β β β’ Halt Execution Immediately ββ β’ Attach Audio Timestamp Coordinate β β β’ Log Schema Violation Event ββ β’ Generate Clinician Review Diff β β β’ Route Payload to Triage Desk βββββββββββββββββββββ¬ββββββββββββββββββββββ βββββββββββββββββββββββββββββββββββ β βΌββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ MANDATORY CLINICIAN ATTESTATION ββ Physician Reviews Diff & Digitally Signs Staged Write ββββββββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββ β βΌββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ PRODUCTION EHR HL7 FHIR API ββ Record Committed to Live Patient Chart ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Below is a production-grade Python implementation of an identity-bound clinical write-back gateway using Pydantic v2.
The gateway enforces strict unit validation, verifies RxNorm coding, and prevents direct writes to production by routing payloads to an isolated draft state:
from pydantic import BaseModel, Field, field_validator, ConfigDictfrom typing import Optional, Listfrom enum import Enumimport reimport logginglogging.basicConfig(level=logging.INFO)logger = logging.getLogger("ClinicalGateway")class UCUMUnit(str, Enum): MICROGRAM = "ug" MILLIGRAM = "mg" GRAM = "g" MILLILITER = "mL" INTERNATIONAL_UNIT = "IU"class StrictDoseQuantity(BaseModel): model_config = ConfigDict(extra="forbid", frozen=True) value: float = Field(..., gt=0, description="Dose value must be strictly positive") unit: UCUMUnit = Field(..., description="Must conform to strict UCUM standard") system: str = Field(default="http://unitsofmeasure.org")class StrictMedicationRequest(BaseModel): model_config = ConfigDict(extra="forbid", frozen=True) patient_id: str = Field(..., min_length=4) medication_name: str = Field(..., min_length=2) rxnorm_code: str = Field(..., pattern=r"^\d{4,8}$", description="Valid RxNorm CUI format") dose: StrictDoseQuantity frequency_per_day: int = Field(..., ge=1, le=12) timing_instructions: str = Field(..., min_length=3) is_draft: bool = Field(default=True, description="Enforces draft isolation") @field_validator("medication_name") @classmethod def validate_medication_format(cls, v: str) -> str: if re.search(r"(\d+(\.\d+)?)\s*(mg|mcg|ug|g)", v, re.IGNORECASE): raise ValueError( "Dosage strength must not be embedded in medication_name string; " "use the explicit dose object." ) return v.strip().title()class ClinicalIngestionGateway: def __init__(self, ehr_client: object, staging_store: object): self.ehr_client = ehr_client self.staging_store = staging_store def process_agent_write_back(self, raw_model_output: dict) -> dict: """ Intercepts LLM extraction output, applies strict schema validation, and enforces draft-state isolation. """ try: # Step 1: Deterministic Schema Validation validated_payload = StrictMedicationRequest(**raw_model_output) # Step 2: Ensure Direct Production Commits are Blocked if not validated_payload.is_draft: raise PermissionError("Autonomous writes directly to production are prohibited.") # Step 3: Route to Isolated Staging Database for Clinician Review staged_record_id = self._commit_to_staging(validated_payload) logger.info( f"SUCCESS: Staged MedicationRequest {staged_record_id} for Patient {validated_payload.patient_id}." ) return { "status": "STAGED_FOR_REVIEW", "staging_id": staged_record_id, "dosage_verified": f"{validated_payload.dose.value} {validated_payload.dose.unit.value}" } except Exception as e: # Step 4: Circuit Breaker Execution self._trip_circuit_breaker(raw_model_output, str(e)) return { "status": "VALIDATION_FAILED", "error": str(e) } def _commit_to_staging(self, payload: StrictMedicationRequest) -> str: # Commit to isolated draft store with immutable audit coordinates return "staged_draft_984723" def _trip_circuit_breaker(self, raw_payload: dict, error_msg: str) -> None: logger.error("CRITICAL: Clinical Write-Back Validation Failed.") logger.error(f"Violation: {error_msg}") logger.error(f"Raw Input Payload: {raw_payload}") # In production: Fire alert to Clinical Ops Desk and halt dependent agent tools
Large language models provide powerful capabilities for clinical entity extraction, but they cannot serve as their own safety controllers. Allowing probabilistic systems to commit data directly to production health records creates unacceptable clinical and regulatory liabilities.
Governing high-stakes healthcare AI requires deterministic infrastructure: enforcing strict gateway contracts, validating units against clinical standards, and isolating all agent operations within draft states until authorized clinicians attest to the record.
On the team at Claire By The Algorithm
Explore stateful digital labor at letsaskclaire.com.
Why Autonomous Agents Fail on EHR Write-Backs: Architecting Gateway Validation for FHIR APIs was originally published in Towards AI on Medium, where people are continuing the conversation by highlighting and responding to this story.