Deterministic Validation in Clinical AI: Why LLMs Need Biological Plausibility Gates & LOINC Standardization
Why relying solely on LLM JSON output for health records invites catastrophic silent errors, and how we engineered a deterministic clinical validator using physiological plausibility limits, canonical LOINC mapping, and PostgreSQL security-invoker views.
The Fallacy of Pure LLM Extraction
When ingesting laboratory reports—whether multi-page CBC panels, metabolic profiles, or lipid panels—modern multimodal LLMs excel at optical character parsing and associating unstructured key-value pairs. However, deploying an LLM directly to persist patient observations introduces critical failure modes:
1. **Hallucinated Clinical Codes**: Models frequently hallucinate plausible-looking 5-digit LOINC codes (e.g., assigning `1558-6` for fasting glucose when the report specified random plasma glucose, or generating nonexistent syntax). 2. **Unit Incongruity**: Reporting a blood glucose value of `105` without capturing whether the laboratory evaluated `mg/dL` or `mmol/L` turns an ordinary reading into either normal fasting blood sugar or severe, life-threatening hyperosmolar coma. 3. **Silent Omissions & Boundary Violations**: LLMs will happily parse a typing artifact such as `Hemoglobin: 140 g/dL` instead of `14.0 g/dL`, generating physiologically impossible observations that corrupt longitudinal analytics.
In **VitalTrace**, we adopted a non-negotiable architectural rule:
**The LLM is an untrusted extractor; deterministic code is the authoritative judge.**
[ Raw Report PDF ] ──► [ Gemini 1.5 Edge Func ] ──► [ Strict Zod Parser ]
│
┌──────────────────────────────────────────────────┘
▼
[ Deterministic Clinical Validator ]
├── 1. Biological Plausibility Limits Check (e.g., 0.5 < Hb < 25.0 g/dL)
├── 2. Canonical LOINC & Synonym Normalization (aliasDictionary.ts)
├── 3. Duplicate Specimen Collision Detection
└── 4. Unit Harmonization & Missing Unit Audit
│
▼
[ PostgreSQL: pending Review State ] ──► [ Clinician / Patient Workbench ]Step 1: Strict Runtime Typing with Zod
The edge function output must first satisfy a strict Zod schema before entering the validation engine. If the LLM generates extra fields or unexpected data types, the run halts immediately rather than poisoning downstream stores:
export const ExtractedObservationSchema = z.object({
raw_test_name: z.string().min(1),
value_numeric: z.number().nullable(),
value_text: z.string().nullable(),
unit: z.string().nullable(),
reference_range_low: z.number().nullable(),
reference_range_high: z.number().nullable(),
flag: z.enum(["normal", "low", "high", "critical_low", "critical_high", "abnormal"]).nullable(),
loinc_code: z.string().nullable(),
});Step 2: Biological Plausibility Bounds
Even if a lab report OCR reads `Potassium: 42.0 mmol/L` due to a smudge on the printed paper, human serum potassium cannot exceed approximately `10.0 mmol/L` in a living patient. We defined strict physiological bounds (`PLAUSIBILITY_BOUNDS`):
export const PLAUSIBILITY_BOUNDS: Record<string, { min: number; max: number; expectedUnit: string }> = {
// Potassium: 1.0 to 10.0 mmol/L
"2823-3": { min: 1.0, max: 10.0, expectedUnit: "mmol/L" },
// Hemoglobin: 2.0 to 26.0 g/dL
"718-7": { min: 2.0, max: 26.0, expectedUnit: "g/dL" },
// Fasting Blood Glucose: 20 to 1200 mg/dL
"1558-6": { min: 20.0, max: 1200.0, expectedUnit: "mg/dL" },
// Platelets: 5 to 2000 x10^3/uL
"777-3": { min: 5.0, max: 2000.0, expectedUnit: "x10^3/uL" },
};If an observation breaches these bounds, it is automatically flagged with severity `CRITICAL_PLAUSIBILITY_BREACH` and requires explicit human verification in the review workbench.
Step 3: Zero-Tolerance LOINC Mapping
Standardizing synonyms (e.g., `"FBS"`, `"Fasting Blood Sugar"`, `"Glucose, Fasting Plasma"`) to canonical LOINC code `1558-6` is vital for longitudinal charting. However, when an unfamiliar or bespoke proprietary test is encountered, our dictionary mandates **null fallback**:
export function resolveLoincCode(rawName: string): { code: string | null; canonicalName: string } {
const normalized = normalizeAliasKey(rawName);
const match = CLINICAL_ALIAS_DICTIONARY[normalized];
if (match) {
return { code: match.loinc, canonicalName: match.canonicalName };
}
// Zero-hallucination guarantee: Preserve raw name, never guess a code
return { code: null, canonicalName: rawName.trim() };
}Step 4: Multi-Tenant PostgreSQL Security Invoker Views
Analytical queries calculating patient trajectories must never leak between accounts. In PostgreSQL 15+, analytical views can specify `WITH (security_invoker = true)`. This ensures that views execute under the privileges of the querying user rather than the view creator:
CREATE OR REPLACE VIEW view_patient_biomarker_trajectories
WITH (security_invoker = true) AS
SELECT
obs.profile_id,
obs.loinc_code,
obs.canonical_name,
obs.observation_date,
obs.value_numeric,
obs.unit
FROM observations obs
WHERE obs.status = 'confirmed';Because `security_invoker` is active, PostgreSQL enforces the base table's Row-Level Security policy (`auth.uid() = profile_id`), guaranteeing zero cross-tenant data leakage.
Key Takeaway
In production engineering, especially across health and data infrastructure, machine learning models should serve as intelligent transducers, never as autonomous final arbiters. Deterministic layers provide the defensible audit trail required for institutional trust.