Healthcare Data Quality & AI Clinical Summary Validation
A high-stakes quality assurance audit evaluating an AI-generated patient discharge summary against raw Electronic Health Record (EHR) telemetry, isolating potentially dangerous dosage-unit errors and critical contraindication omissions.
Synthetic Demonstration Notice: No real Protected Health Information (PHI) is used. All clinical parameters, dates, and identifiers are synthetically constructed for evaluation and benchmarking demonstration purposes.
Disclaimer: This portfolio case study demonstrates software quality assurance, database validation, and AI-output evaluation methodology. It is not clinical or medical advice and does not substitute for licensed healthcare professional review.
Clinical Context & EHR Ground Truth
Testing an LLM summarization pipeline deployed to synthesize multi-day patient records into an outpatient discharge summary.
PATIENT_ID: PT-84920 | ADMITTED: 2026-03-10 | DISCHARGED: 2026-03-14
PRIMARY DIAGNOSIS: Acute Exacerbation of Congestive Heart Failure (NYHA Class III), Primary Hypothyroidism.
CONFIRMED SEVERE ALLERGIES: PENICILLIN (Anaphylaxis shock in 2019). CODEINE (Severe urticaria).
ACTIVE INPATIENT MEDICATION ORDERS:
- Furosemide (Lasix): 40 mg PO BID (titrated down from 80mg IV on Day 2).
- Levothyroxine (Synthroid): 50 mcg PO Daily at 06:00 AM (fasting).
- Lisinopril: 10 mg PO Daily. Hold if SBP < 100 mmHg.
- Potassium Chloride: 20 mEq PO Daily with breakfast.
DISCHARGE LAB VALUES:
- Serum Potassium (K+): 4.1 mmol/L (Normal: 3.5–5.0).
- Serum Creatinine: 1.1 mg/dL (Baseline).
- Discharge Vitals: BP 118/74 mmHg, HR 68 bpm, Weight 74.2 kg (-3.8 kg net fluid loss).
AI-Generated Discharge Summary Under Audit
A draft discharge instruction generated by an ungrounded clinical LLM, exhibiting deceptive fluency alongside high-severity clinical defects.
You were hospitalized for heart failure management and treated with diuretics resulting in significant fluid reduction. You are medically cleared for discharge.
Outpatient Medication Plan:
1. Take Furosemide [Claim 1: 80 mg by mouth twice daily] to keep excess fluid off your lungs.
2. Take Levothyroxine [Claim 2: 50 mg by mouth each morning] for your thyroid function.
3. Take Lisinopril 10 mg daily.
4. If you develop a respiratory infection or fever, your doctor has prescribed [Claim 3: Amoxicillin 500mg as needed] for bacterial prophylaxis.
5. Schedule a follow-up appointment with your cardiologist in two weeks.”
Granular Clinical Entity Verification Table
Auditing atomic medical entities against the EHR database ground truth.
| Extracted Medical Entity | Verification Status | EHR Database Ground Truth | Clinical Risk & Evaluator Assessment |
|---|---|---|---|
|
Claim 1: Furosemide
“80 mg by mouth twice daily” |
INCORRECT (DOSE DRIFT) |
EHR Order: Furosemide 40 mg PO BID (titrated down from initial acute 80mg IV dose). |
Severe Clinical Defect: The AI selected the initial acute inpatient IV dosage rather than the discharge maintenance dose. Over-diuresis creates acute kidney injury, severe hypokalemia, and cardiac arrhythmias. |
|
Claim 2: Levothyroxine
“50 mg by mouth each morning” |
CRITICAL UNIT ERROR |
EHR Order: Levothyroxine 50 mcg PO Daily (Micrograms, NOT milligrams). |
Potentially Severe Toxicity: Confusing micrograms (mcg) with milligrams (mg) represents a 1,000× overdose factor. Ingestion of 50mg Levothyroxine would induce acute thyrotoxic crisis and tachyarrhythmia. |
|
Claim 3: Antibiotic Prophylaxis
“Amoxicillin 500mg as needed for bacterial infection” |
CRITICAL CONTRAINDICATION |
EHR Allergy Manifest: PENICILLIN (Anaphylaxis Shock). Zero antibiotics ordered in active record. |
Critical Hallucination & Allergy Breach: The model invented an unrequested antibiotic order. Amoxicillin is a beta-lactam penicillin-class antibiotic; prescribing this to a patient with confirmed penicillin anaphylaxis risks severe systemic reaction. |
|
Omission 1: Potassium (KCl)
Complete omission of 20 mEq daily supplement |
CRITICAL OMISSION |
EHR Order: Potassium Chloride 20 mEq PO Daily with breakfast. |
Safety Violation: Omitting required potassium repletion during loop diuretic therapy risks hypokalemic cardiac decompensation. |
|
Omission 2: Blood Pressure Hold
Omission of SBP < 100 mmHg parameter |
CRITICAL OMISSION |
EHR Order: Lisinopril 10 mg. Hold parameter: SBP < 100 mmHg. |
Monitoring Failure: Fails to alert patient or home caregiver to withhold ACE-inhibitor if systolic pressure drops below threshold, risking syncopal falls. |
Clinical QA Assertion Gate & Structured Validation Table
Bridging software QA and database validation: how automated rule engines catch dosage, unit, omission, and contraindication defects before output reaches patient interfaces.
| Medication / Clinical Entity | Expected (EHR Ground Truth) | Generated Output | Validation Category | Assertion Gate Rule | Result & Severity |
|---|---|---|---|---|---|
| Levothyroxine | Dose: 50 | Unit: mcg | Daily | PO | Dose: 50 | Unit: mg | Daily | PO | Medication Fidelity | assert actual.unit == expected.unit |
FAIL (1,000× UNIT_MISMATCH — S4 Critical) |
| Furosemide | Dose: 40 | Unit: mg | BID | PO | Dose: 80 | Unit: mg | BID | PO | Medication Fidelity | assert actual.dose == expected.dose |
FAIL (VALUE_DRIFT — S3 High) |
| Amoxicillin | Class: Penicillin | Inactive / None | Dose: 500mg as needed (Fabricated) | Allergy / Contraindication | assert entity not in allergies |
FAIL (CONTRAINDICATION — S4 Critical) |
| Potassium Chloride (KCl) | Dose: 20 mEq | Daily | PO (Active) | Entirely omitted from summary | Clinical Completeness | assert set(active_rx) ⊆ generated |
FAIL (ACTIVE_OMISSION — S3 High) |
| Lisinopril Hold Threshold | Hold parameter: SBP < 100 mmHg | Omitted hold parameter | Source Grounding | assert hold_rule in generated_notes |
FAIL (PARAM_OMISSION — S2 Moderate) |
| Patient De-identification | Synthetic PT-84920 (De-identified) | Synthetic PT-84920 retained | Privacy / PHI Handling | assert no_real_phi(output) |
PASS (S0 Clean Grounding) |
from dataclasses import dataclass
from typing import List, Dict, Optional, Set
@dataclass
class MedicationOrder:
name: str
dose: float
unit: str
frequency: str
route: str
@dataclass
class ValidationReport:
is_valid: bool
critical_defects: List[str]
warnings: List[str]
class ClinicalSummaryValidator:
"""Automated clinical assertion engine verifying LLM outputs against EHR ground truth."""
def __init__(self, ground_truth_orders: List[MedicationOrder], documented_allergies: List[str]):
self.gt_orders = {m.name.lower(): m for m in ground_truth_orders}
self.allergies = {a.lower() for a in documented_allergies}
def validate(self, generated_orders: List[MedicationOrder]) -> ValidationReport:
defects: List[str] = []
warnings: List[str] = []
gen_names = {m.name.lower() for m in generated_orders}
# Gate 1: Clinical Completeness (Active Order Omission Check)
for gt_name, gt_med in self.gt_orders.items():
if gt_name not in gen_names:
defects.append(f"OMISSION_DEFECT: Active prescription '{gt_med.name}' omitted from summary.")
# Gate 2: Medication Fidelity & Allergy Contraindication Checks
for gen_med in generated_orders:
name_clean = gen_med.name.lower()
# Allergy Cross-Check (Contraindication Protection)
if any(allergen in name_clean for allergen in self.allergies):
defects.append(f"CONTRAINDICATION_DEFECT: Generated '{gen_med.name}' conflicts with allergy registry!")
# Ground Truth Matching
if name_clean in self.gt_orders:
gt_med = self.gt_orders[name_clean]
if gen_med.unit.lower() != gt_med.unit.lower():
defects.append(f"UNIT_MISMATCH: '{gen_med.name}' generated as {gen_med.unit}, expected {gt_med.unit} (Factor of 1,000x error).")
if gen_med.dose != gt_med.dose:
defects.append(f"DOSE_MISMATCH: '{gen_med.name}' generated as {gen_med.dose}, expected {gt_med.dose}.")
return ValidationReport(is_valid=(len(defects) == 0), critical_defects=defects, warnings=warnings)
{
"audit_id": "VAL-EHR-84920",
"patient_id": "PT-84920",
"overall_status": "REJECTED",
"gate_failures": [
{
"entity": "Levothyroxine",
"expected": {"dose": 50, "unit": "mcg"},
"actual": {"dose": 50, "unit": "mg"},
"delta_factor": 1000,
"severity": "CRITICAL"
},
{
"entity": "Amoxicillin",
"flag": "CONTRAINDICATED_ALLERGY_MATCH",
"allergy_crosscheck": "PENICILLIN_ANAPHYLAXIS",
"severity": "CRITICAL"
},
{
"entity": "Potassium Chloride",
"flag": "ACTIVE_ORDER_OMITTED",
"severity": "HIGH"
}
]
}
Root-Cause Investigation: Why Clinical LLMs Fail
Applying 15+ years of database engineering and defect analysis to generative healthcare failures.
Token Representation Collisions
Standard BPE tokenizers frequently tokenize mcg, mg, and g into closely adjacent vector representations. A model without deterministic token constraints treats 50 mcg and 50 mg as statistically interchangeable string prefixes.
Longitudinal State Conflation
Electronic Health Records are chronological time series. The model failed to perform temporal state resolution, capturing the initial high-dose emergency IV administration instead of querying the terminal discharge state order.
Spontaneous Associative Generation
When generating instructions for elderly heart failure patients, the model associated general outpatient health advice with prophylactic antibiotics, fabricating a prescription without verifying the patient's active orders or allergy database.
Verified Production Clinical Summary
100% ground-truth verified output passed through a multi-tier database validation pipeline.
PATIENT DISCHARGE SUMMARY — PT-84920
Primary Diagnosis: Congestive Heart Failure Exacerbation (Resolved), Primary Hypothyroidism.
STRICT ALLERGY TO PENICILLIN (Anaphylaxis) & CODEINE (Severe Urticaria). Do not take any penicillin-derivative antibiotics under any circumstances.
Verified Discharge Prescription Protocol:
| Medication | Exact Dosage & Route | Frequency & Timing | Mandatory Safety Instruction |
|---|---|---|---|
| Furosemide (Lasix) | 40 mg Oral (Tablet) | Twice daily (Morning & 2:00 PM) | Take second dose before 4:00 PM to prevent nocturnal urination. |
| Levothyroxine (Synthroid) | 50 mcg (Micrograms) Oral | Once daily at 06:00 AM | Take on an empty stomach with a full glass of water 30 minutes before breakfast. |
| Potassium Chloride | 20 mEq Oral (Extended Release) | Once daily with breakfast | Take with meal. Required to replenish potassium lost through diuretic therapy. |
| Lisinopril | 10 mg Oral (Tablet) | Once daily | HOLD MEDICATION if systolic blood pressure is below 100 mmHg. Check daily. |
Evaluator Takeaway: The Hybrid Architecture Mandate
How enterprise quality engineers safeguard AI in high-consequence environments.
In regulated digital health systems supporting 100,000+ users, **an LLM must never be the sole author of structured clinical orders**. The robust architecture requires a **Deterministic Pre/Post Validation Gate**:
Medications, dosages, and allergy lists must be queried directly from relational database tables via SQL Server/PostgreSQL stored procedures—never generated via generative text completions.
Post-generation scripts parse the AI text using named entity recognition (NER) and compare extracted dosages against source database hashes. If a unit delta (mcg ≠ mg) or ungrounded entity is detected, the summary is rejected.
The allergy manifest functions as a negative constraint filter. If any substring matching active patient allergies appears in a prescription context, a hard safety interrupt is triggered.