← Back to list

Building Production-Grade AI Systems: Part 1 — Infrastructure at scale

Introduction

Pranav Masariya · 2026-03-04 21:33 · 4 claps · 16.4 min read
#llm #generative-ai-platform #ai-in-healthcare #system-design-concepts #llmops
Open on Medium ↗
Wiki topics: LLM · Large Language Models OPS · LLMOps & Inference AI · AI · General

Building Production-Grade AI Systems: Part 1 — Infrastructure at scale

Photo by Vadim Sherbakov on Unsplash

Photo by Vadim Sherbakov on Unsplash

Introduction

Over the last 12 years, most of my engineering work has been in production environments where reliability, latency, auditability, and operational discipline matter more than demos. AI systems amplify that reality. It is easy to make something look impressive in a prototype. It is much harder to make it dependable under real traffic, against live source systems, with strong security boundaries and clear failure modes.

This article is a technical walkthrough of the architecture patterns I consider necessary for production-grade AI systems, especially in regulated or operationally sensitive environments. Some examples come from healthcare-style data and workflows because they expose the constraints clearly: real-time access requirements, strict compliance expectations, legacy integration, and very little tolerance for errors.

What follows is not a product pitch or a case study for a specific organization. It is a distillation of production engineering lessons: how to think about live data access, parallel processing, translation layers, LLM gateways, observability, and the tradeoffs that appear once a system has to run reliably in the real world.

Core Technical Challenges

Challenge 1: Real-Time Data Access

The Problem

Initial iterations of healthcare AI systems often rely on the EHR’s reporting database — a standardized schema updated nightly through batch processes. While this approach simplifies data access, it creates an unacceptable limitation: clinicians make decisions based on current patient state, not yesterday’s snapshot.

A critically ill patient’s lab values, vital signs, and medication orders can change hourly. A 24-hour data lag makes an AI assistant effectively useless for clinical decision-making.

Evolution of Solutions

Most teams explore three approaches:

[1]. Reporting Database Only (Rejected)

  • ✅ Simple, standardized schema
  • ❌ 24-hour data lag
  • ❌ Unusable for clinical decisions

[2]. Hybrid: Reporting Database + HL7v2 Real-Time Messages (Rejected)

  • ✅ Near real-time updates
  • ❌ Complex data reconciliation
  • ❌ Two different data models to maintain
  • ❌ HL7v2 is a messaging standard, not ideal for querying

[3]. FHIR-Based Architecture (Recommended)

  • ✅ Industry-standard healthcare API
  • ✅ Vendor-neutral
  • ✅ Designed for data exchange
  • ✅ Real-time query capabilities
  • ⚠️ Requires significant engineering to scale

Why FHIR?

Fast Healthcare Interoperability Resources (FHIR) emerged as the optimal solution. FHIR is a modern RESTful API standard designed specifically for healthcare data exchange. It provides:

  • Resource-based model: Patient data is organized into discrete resources (Patient, Observation, MedicationRequest, etc.)
  • Standardized endpoints: Consistent API patterns across vendors
  • Search capabilities: Rich query parameters for filtering and retrieval
  • Real-time access: Query live data directly from source systems

However, implementing FHIR at enterprise scale introduces new challenges:

  • Data completeness: Ensuring all relevant clinical data surfaces through FHIR endpoints
  • Latency requirements: Achieving sub-second queries across distributed systems
  • Reliability: Building fault-tolerant patterns for clinical-grade uptime
  • Complexity: Coordinating data from multiple source systems (labs, pharmacy, radiology, etc.)

Implementation Considerations

Achieving production-grade FHIR performance requires:

  • Close collaboration between platform teams, EHR administrators, and clinical informatics specialists
  • Significant engineering effort to optimize query patterns
  • Investment in caching and parallel processing infrastructure
  • Robust error handling and fallback mechanisms

Key Takeaway for Builders

If you’re building a similar system:

  • Don’t underestimate the complexity of real-time clinical data access
  • Invest in FHIR infrastructure early — it pays dividends
  • Plan for significant engineering effort to achieve production-grade performance
  • Build strong relationships with EHR administrators and clinical informatics teams

Challenge 2: Processing Data at Speed

The Problem

A typical patient’s EHR contains thousands of discrete data points: years of lab results, imaging reports, medication histories, clinical notes, vital signs, and procedures. Processing this volume with acceptable latency while maintaining clinical accuracy requires careful architectural design.

Early prototypes often explore various retrieval-augmented generation (RAG) approaches, but simple vector similarity search proves insufficient for clinical accuracy. Medical queries often require temporal reasoning, understanding clinical significance, and synthesizing information across multiple data domains.

The Solution: Parallel Processing Architecture

The optimal design combines two critical innovations:

1. Optimized Data Transformation

Raw FHIR resources must be transformed into LLM-optimized formats while preserving clinical integrity:

FHIR Resource → Clinical Context Preservation → LLM-Optimized Format

This transformation layer:

  • Preserves medical terminology and units
  • Maintains temporal relationships
  • Structures data for efficient token usage
  • Retains provenance for auditability

2. Distributed Concurrent Processing

Rather than processing a patient’s entire chart sequentially, the system should employ domain-specific parallelization:

User Query
    ↓
Query Analysis & Decomposition
    ↓
┌─────────────┬──────────────┬──────────────┬─────────────┐
│ Medications │ Lab Results  │  Procedures  │   Notes     │
│  LLM Call   │  LLM Call    │  LLM Call    │  LLM Call   │
└─────────────┴──────────────┴──────────────┴─────────────┘
    ↓
Response Synthesis

Each LLM call processes a specific clinical domain concurrently. This architecture:

  • Maintains sub-second response times even with extensive patient histories
  • Allows domain-specific prompt optimization
  • Enables granular error handling per domain
  • Scales horizontally with patient data volume

Performance Characteristics

  • Baseline: Single-threaded processing of comprehensive chart: 10–15 seconds
  • Optimized: Parallel domain processing: <1 second for most queries
  • Scalability: Response time remains consistent across patient history length

Key Takeaway for Builders

  • Don’t rely solely on vector similarity search for clinical data
  • Design for parallelization from day one
  • Invest in data transformation layers that preserve clinical semantics
  • Profile your bottlenecks — latency matters in clinical workflows

Challenge 3: Bridging Technical and Clinical Data Models

The Problem

Healthcare operates in two parallel universes:

Technical World:

  • FHIR resources (Observation, MedicationRequest, DiagnosticReport)
  • HL7v2 message segments (OBX, RXE, ORC)
  • Database schemas (normalized, foreign keys, junction tables)
  • System identifiers and reference IDs

Clinical World:

  • Episodes of care
  • Treatment plans
  • Medication regimens
  • Clinical narratives
  • Patient stories

A query like “What antibiotics was this patient on during their last admission?” requires translating between these worldviews. The system must understand clinical concepts while operating on technical resources.

The Solution: Dual-Layer Data Model

Effective medical AI systems implement a translation layer that maintains both perspectives:

Display Layer (Clinical):

"Patient admitted 2024-09-15 with pneumonia
 Started on Ceftriaxone 1g IV Q24H
 Cultures grew Streptococcus pneumoniae
 Switched to Amoxicillin 500mg PO TID on 2024-09-18
 Discharged 2024-09-20"

Metadata Layer (Technical):

{
  "encounter_id": "enc_12345",
  "medication_orders": [
    {
      "order_id": "rx_67890",
      "fhir_resource": "MedicationRequest/67890",
      "source_system": "Epic Pharmacy",
      "timestamp": "2024-09-15T14:23:00Z"
    }
  ],
  "lab_results": [
    {
      "observation_id": "obs_11111",
      "fhir_resource": "Observation/11111",
      "source_system": "Epic Labs",
      "timestamp": "2024-09-16T08:15:00Z"
    }
  ]
}

Why Both Layers Matter

  • Clinical layer: Enables LLM understanding and natural language generation
  • Metadata layer: Provides auditability, traceability, and data lineage
  • Regulatory compliance: Every displayed fact can be traced to source system
  • Quality assurance: Enables validation of LLM-generated summaries against source data

Implementation Pattern

class ClinicalDataTranslator:
    def transform_fhir_to_clinical(self, fhir_bundle):
        """
        Transform FHIR resources into clinician-friendly narratives
        while preserving technical metadata for auditability.
        """
        clinical_narrative = self._generate_narrative(fhir_bundle)
        technical_metadata = self._extract_provenance(fhir_bundle)

        return {
            'display': clinical_narrative,  # What clinicians see
            'metadata': technical_metadata,  # What auditors need
            'fhir_references': self._extract_references(fhir_bundle)
        }

Key Takeaway for Builders

  • Never lose the technical provenance of clinical data
  • Design your data model to support both human understanding and machine traceability
  • Plan for audits from day one — you’ll need detailed data lineage
  • The translation layer is not optional; it’s foundational

Challenge 4: Secure LLM Integration

The Problem

Modern AI applications benefit from multiple model providers, each with different strengths:

  • OpenAI GPT-4 for general reasoning
  • Anthropic Claude for long-context understanding
  • Domain-specific fine-tuned models for clinical tasks
  • Open-source models for cost-sensitive operations

However, healthcare AI requires:

  • Security: All PHI must remain within enterprise boundaries
  • Auditability: Every LLM interaction must be logged
  • Reliability: Failover capabilities across providers
  • Governance: Centralized control over model access

Direct integration with each provider creates a security and operational nightmare.

The Solution: Self-Hosted LLM Gateway

Production AI systems should implement a centralized gateway that serves as the single point of integration for all LLM interactions:

┌─────────────────────────────────────────────┐
│         Medical AI Applications             │
└──────────────────┬──────────────────────────┘
                   ↓
┌─────────────────────────────────────────────┐
│          LLM Gateway (Self-Hosted)          │
│  ┌─────────────────────────────────────┐   │
│  │  Request Logging & Monitoring        │   │
│  │  Authentication & Authorization      │   │
│  │  Model Selection & Routing           │   │
│  │  Response Standardization            │   │
│  │  Rate Limiting & Quota Management    │   │
│  └─────────────────────────────────────┘   │
└──────────────────┬──────────────────────────┘
                   ↓
        ┌──────────┴──────────┐
        ↓                     ↓
  ┌──────────┐          ┌──────────┐
  │ Provider │          │ Provider │
  │    A     │          │    B     │
  └──────────┘          └──────────┘

Gateway Capabilities

[1]. Unified API Interface

# Applications call a single standardized endpoint
response = llm_gateway.complete(
    messages=[{"role": "user", "content": query}],
    model_preference="gpt-4",  # Or "claude-3", "local-model"
    context="clinical_summary",
    temperature=0.0
)

[2]. Automatic Model Selection The gateway routes requests based on:

  • Query type and complexity
  • Token length requirements
  • Cost constraints
  • Availability and failover

[3]. Comprehensive Logging Every interaction is logged with:

  • Request timestamp and user ID
  • Input tokens and output tokens
  • Model used and response time
  • Success/failure status
  • PHI-redacted summaries for audit

[4]. Security Controls

  • API keys never exposed to applications
  • All traffic encrypted in transit
  • Enterprise firewall protection
  • No PHI leaves the gateway

Benefits of This Architecture

  • Provider agnostic: Swap models without changing application code
  • Cost optimization: Route queries to cost-effective models
  • Observability: Centralized monitoring and alerting
  • Security: Single point for security controls
  • Compliance: Simplified audit trails

Key Takeaway for Builders

  • Never allow applications to call LLM APIs directly
  • Build or adopt a gateway pattern early
  • Logging is not optional — you’ll need it for debugging and compliance
  • Design for multi-provider from the start; vendor lock-in is risky in fast-moving AI landscape

The Four-Pillar Architecture

With the core challenges addressed, the production systems I trust usually crystallize into four foundational pillars:

Pillar 1: LLM Router

Purpose: Centralized access point for all AI model interactions

Components:

┌─────────────────────────────────────────┐
│           LLM Router                    │
│  ┌───────────────────────────────────┐ │
│  │  Model Selection Logic            │ │
│  │  - Query type analysis            │ │
│  │  - Token count estimation         │ │
│  │  - Cost-performance tradeoff      │ │
│  └───────────────────────────────────┘ │
│  ┌───────────────────────────────────┐ │
│  │  Standardization Layer            │ │
│  │  - Normalize provider responses   │ │
│  │  - Unified error handling         │ │
│  │  - Consistent token counting      │ │
│  └───────────────────────────────────┘ │
│  ┌───────────────────────────────────┐ │
│  │  Observability                    │ │
│  │  - Request/response logging       │ │
│  │  - Performance metrics            │ │
│  │  - Cost tracking                  │ │
│  └───────────────────────────────────┘ │
└─────────────────────────────────────────┘

Design Pattern:

class LLMRouter:
    def __init__(self):
        self.providers = {
            'openai': OpenAIProvider(),
            'anthropic': AnthropicProvider(),
            'local': LocalModelProvider()
        }
        self.logger = AuditLogger()

    async def complete(self, request):
        # 1. Select optimal model
        model = self._select_model(request)

        # 2. Route to provider
        provider = self.providers[model.provider]

        # 3. Execute with monitoring
        start_time = time.time()
        try:
            response = await provider.complete(
                model=model.name,
                **request
            )

            # 4. Log and standardize
            self.logger.log_request(
                request=request,
                response=response,
                duration=time.time() - start_time,
                model=model.name
            )

            return self._standardize_response(response)

        except Exception as e:
            self.logger.log_error(request, e)
            return self._handle_fallback(request, model)

Key Features:

  • Vendor-agnostic interface
  • Automatic failover across providers
  • Cost and performance optimization
  • Built-in observability

Pillar 2: Real-Time Data Access

Purpose: Serverless FHIR-based data retrieval with intelligent caching and parallel processing

Architecture:

User Query → Data Orchestrator
                    ↓
          ┌─────────┴─────────┐
          ↓                   ↓
    Query Analysis      Cache Check
          ↓                   ↓
    FHIR Resource      [Cache Hit]
    Determination            ↓
          ↓              Return Data
    Parallel Fetch
          ↓
   ┌──────┴──────┐
   ↓             ↓
Patient      Observations
   ↓             ↓
Meds       Procedures
   ↓             ↓
   └──────┬──────┘
          ↓
   Transform & Cache
          ↓
    Return Data

Implementation Pattern:

class FHIRMesh:
    def __init__(self):
        self.fhir_client = FHIRClient()
        self.cache = RedisCache()
        self.transformer = ClinicalDataTranslator()

    async def fetch_patient_data(self, patient_id, data_types):
        """
        Fetch and transform FHIR data with intelligent caching
        and parallel processing.
        """
        # Check cache first
        cache_key = f"{patient_id}:{':'.join(data_types)}"
        cached = await self.cache.get(cache_key)
        if cached:
            return cached

        # Parallel fetch across resource types
        tasks = []
        for data_type in data_types:
            tasks.append(
                self._fetch_fhir_resource(patient_id, data_type)
            )

        fhir_resources = await asyncio.gather(*tasks)

        # Transform to clinical format
        clinical_data = self.transformer.transform_fhir_to_clinical(
            fhir_resources
        )

        # Cache with appropriate TTL
        await self.cache.set(
            cache_key, 
            clinical_data, 
            ttl=300  # 5 minutes
        )

        return clinical_data

    async def _fetch_fhir_resource(self, patient_id, resource_type):
        """Fetch a specific FHIR resource type for a patient."""
        return await self.fhir_client.search(
            resource_type=resource_type,
            search_params={
                'patient': patient_id,
                '_sort': '-date',
                '_count': 100
            }
        )

Caching Strategy:

Different data types have different volatility:

  • Demographic data: Cache for 24 hours (changes rarely)
  • Active medications: Cache for 5 minutes (changes during care)
  • Vital signs: Cache for 1 minute (real-time in ICU)
  • Historical data: Cache for 1 hour (immutable)

Key Features:

  • Sub-second query times through caching
  • Parallel resource fetching
  • Automatic cache invalidation
  • Graceful degradation on FHIR server errors

Pillar 3: Function Server

Purpose: Task-specific endpoints that combine LLM capabilities with clinical data access

Architecture:

┌─────────────────────────────────────────┐
│         Function Server                 │
│  ┌───────────────────────────────────┐ │
│  │  Chat Completion Endpoints        │ │
│  │  - Clinical Q&A                   │ │
│  │  - Chart summarization            │ │
│  │  - Medication reconciliation      │ │
│  └───────────────────────────────────┘ │
│  ┌───────────────────────────────────┐ │
│  │  Workflow Automation Endpoints    │ │
│  │  - Discharge summary generation   │ │
│  │  - Prior authorization prep       │ │
│  │  - Referral letter drafting       │ │
│  └───────────────────────────────────┘ │
│  ┌───────────────────────────────────┐ │
│  │  Data Analysis Endpoints          │ │
│  │  - Clinical trend analysis        │ │
│  │  - Risk stratification            │ │
│  │  - Protocol compliance checking   │ │
│  └───────────────────────────────────┘ │
└─────────────────────────────────────────┘

Example Endpoint: Clinical Q&A:

class ChatCompletionEndpoint:
    def __init__(self, llm_router, data_orchestrator):
        self.llm = llm_router
        self.data = data_orchestrator

    async def handle_clinical_query(self, request):
        """
        Process a clinical query by fetching relevant data
        and generating an LLM-powered response.
        """
        patient_id = request.patient_id
        query = request.query

        # 1. Analyze query to determine required data
        required_data = self._analyze_query_requirements(query)

        # 2. Fetch relevant clinical data
        clinical_context = await self.data.fetch_patient_data(
            patient_id=patient_id,
            data_types=required_data
        )

        # 3. Construct LLM prompt with clinical context
        prompt = self._build_clinical_prompt(
            query=query,
            context=clinical_context
        )

        # 4. Get LLM response
        response = await self.llm.complete(
            messages=[
                {
                    "role": "system",
                    "content": CLINICAL_ASSISTANT_SYSTEM_PROMPT
                },
                {
                    "role": "user",
                    "content": prompt
                }
            ],
            temperature=0.0,  # Deterministic for clinical use
            max_tokens=1000
        )

        # 5. Post-process and validate
        return self._validate_clinical_response(
            response=response,
            source_data=clinical_context
        )

Endpoint Categories:

[1]. Interactive Endpoints (Real-time chat)

  • Real-time clinical Q&A
  • Chart exploration
  • Differential diagnosis support

[2]. Automation Endpoints (Background processes)

  • Scheduled report generation
  • Batch data analysis
  • Proactive clinical alerts

[3]. Integration Endpoints (Third-party systems)

  • Standardized API for vendor tools
  • Webhook-based event handling
  • Batch processing interfaces

Key Features:

  • Domain-specific prompt engineering
  • Clinical validation layers
  • Consistent error handling
  • Usage analytics per endpoint

Pillar 4: System Integration

Purpose: Secure, reliable connection layer between the AI platform and the source-of-truth system

Components:

┌─────────────────────────────────────────┐
│    Source System Integration Service    │
│  ┌───────────────────────────────────┐ │
│  │  Authentication & Authorization   │ │
│  │  - SSO integration                │ │
│  │  - Role-based access control      │ │
│  │  - Session management             │ │
│  └───────────────────────────────────┘ │
│  ┌───────────────────────────────────┐ │
│  │  Context Management               │ │
│  │  - Patient context propagation    │ │
│  │  - Encounter awareness            │ │
│  │  - Department/location context    │ │
│  └───────────────────────────────────┘ │
│  ┌───────────────────────────────────┐ │
│  │  Rate Limiting & Quotas           │ │
│  │  - Per-user request limits        │ │
│  │  - System-wide throttling         │ │
│  │  - Priority queuing               │ │
│  └───────────────────────────────────┘ │
│  ┌───────────────────────────────────┐ │
│  │  Audit Logging                    │ │
│  │  - Every access logged            │ │
│  │  - HIPAA-compliant audit trails   │ │
│  │  - PHI access tracking            │ │
│  └───────────────────────────────────┘ │
└─────────────────────────────────────────┘

Security Implementation:

class SourceSystemIntegrationService:
    def __init__(self):
        self.auth_provider = SourceSystemAuthProvider()
        self.access_logger = HIPAACompliantLogger()
        self.rate_limiter = RateLimiter()

    async def validate_request(self, request):
        """
        Validate and enrich incoming requests with
        source-system context and access controls.
        """
        # 1. Authenticate user
        user = await self.auth_provider.validate_token(
            request.auth_token
        )

        # 2. Check authorization
        if not self._has_patient_access(user, request.patient_id):
            self.access_logger.log_unauthorized_access(
                user=user,
                patient=request.patient_id
            )
            raise UnauthorizedError("No access to patient")

        # 3. Rate limiting
        if not await self.rate_limiter.check_limit(user.id):
            raise RateLimitError("Rate limit exceeded")

        # 4. Log access
        await self.access_logger.log_phi_access(
            user=user,
            patient=request.patient_id,
            action=request.action,
            timestamp=datetime.now()
        )

        # 5. Enrich with context
        return {
            **request,
            'user_context': {
                'user_id': user.id,
                'role': user.role,
                'department': user.department
            },
            'patient_context': await self._get_patient_context(
                request.patient_id
            )
        }

Key Features:

  • Seamless identity integration
  • Automatic record context propagation
  • Comprehensive audit logging
  • Rate limiting and abuse prevention

Putting It All Together: Request Flow

Understanding how these four pillars work together is crucial. Here’s a detailed walkthrough of a typical interaction:

Scenario: A user asks a record-specific question that requires live retrieval and synthesis

Step-by-Step Flow:

1. USER INPUT
   ↓
   User types query into embedded interface
2. SYSTEM INTEGRATION (Pillar 4)
   ↓
   - Request includes inherited auth token from the source system
   - Service validates user identity
   - Extracts patient context (patient_id, encounter_id)
   - Checks user authorization for this patient
   - Logs PHI access in audit trail
   - Passes validated request to Function Server
3. FUNCTION SERVER (Pillar 3)
   ↓
   - Receives query: "What was this patient's peak troponin 
     during their last admission?"
   - Analyzes query requirements:
     * Need: Troponin lab results
     * Need: Most recent admission dates
   - Routes to chat_completion endpoint
4. DATA ORCHESTRATOR (Pillar 2)
   ↓
   - Parallel fetch initiated:
     * Task 1: Get recent encounters (find "last admission")
     * Task 2: Get Observation resources (troponin tests)
   - FHIR queries execute concurrently:

   GET /Encounter?patient={id}&class=inpatient&_sort=-date&_count=1
   GET /Observation?patient={id}&code=troponin&_sort=-date&_count=50

   - Cache check: Miss (first query for this patient today)
   - Results returned in ~200ms
   - Transform FHIR to clinical format
5. LLM ROUTER (Pillar 1)
   ↓
   - Function Server requests LLM completion
   - Router analyzes request:
     * Query type: Factual data extraction
     * Token count: ~500 input tokens
     * Required: High accuracy, low latency
   - Selects appropriate model
   - Response received in ~800ms
6. FUNCTION SERVER (Pillar 3) - Validation
   ↓
   - Receives LLM response
   - Validates against source data
   - Attaches metadata for auditability
7. SYSTEM INTEGRATION (Pillar 4) - Logging
   ↓
   - Logs complete interaction
   - Returns response to user
Total Time: ~1.2 seconds

Key Observations:

  1. Security at Every Layer: User validated before data access, PHI never leaves enterprise, every action logged
  2. Performance: Parallel processing + caching = sub-second responses despite complex data retrieval
  3. Accuracy: LLM response validated against source data, full traceability maintained
  4. Clinical Integration: Seamless experience within existing workflow, no context switching

Advanced Patterns and Best Practices

Pattern 1: Intelligent Caching Strategy

Not all clinical data changes at the same rate. Implement tiered caching:

class TieredCacheStrategy:
    CACHE_POLICIES = {
        'Patient': {'ttl': 86400, 'priority': 'high'},      # Demographics change rarely
        'Observation': {'ttl': 300, 'priority': 'medium'},  # Labs updated frequently
        'MedicationRequest': {'ttl': 300, 'priority': 'high'}, # Active meds critical
        'Encounter': {'ttl': 3600, 'priority': 'medium'},   # Encounters semi-stable
        'Procedure': {'ttl': 3600, 'priority': 'low'},      # Historical data stable
        'DiagnosticReport': {'ttl': 1800, 'priority': 'medium'}
    }

    async def get_with_policy(self, resource_type, patient_id):
        policy = self.CACHE_POLICIES[resource_type]
        cache_key = f"{patient_id}:{resource_type}"

        # Check cache
        cached = await self.cache.get(cache_key)
        if cached and not self._is_stale(cached, policy['ttl']):
            return cached

        # Fetch fresh data
        fresh_data = await self.fhir_client.fetch(resource_type, patient_id)

        # Cache with appropriate TTL
        await self.cache.set(
            cache_key, 
            fresh_data, 
            ttl=policy['ttl'],
            priority=policy['priority']
        )

        return fresh_data

Pattern 2: Graceful Degradation

Clinical systems must never fail completely. Implement fallback strategies:

class ResilientDataFetcher:
    async def fetch_with_fallback(self, patient_id, resource_type):
        try:
            # Try real-time FHIR fetch
            return await self.fhir_client.fetch(resource_type, patient_id)
        except FHIRServerTimeout:
            # Fallback to cached data, even if stale
            cached = await self.cache.get(f"{patient_id}:{resource_type}")
            if cached:
                logger.warning(f"Using stale cache for {resource_type}")
                return cached.mark_as_stale()
            raise
        except FHIRServerError:
            # Fallback to reporting database for historical data
            if resource_type in ['Observation', 'Procedure']:
                logger.warning(f"Falling back to reporting DB for {resource_type}")
                return await self.reporting_db.fetch(resource_type, patient_id)
            raise

Pattern 3: Response Validation

Never trust LLM output in clinical settings:

class ClinicalResponseValidator:
    def validate_response(self, llm_response, source_data):
        """
        Validate LLM response against source data to prevent
        hallucinations in clinical context.
        """
        validations = [
            self._check_factual_accuracy(llm_response, source_data),
            self._check_temporal_consistency(llm_response, source_data),
            self._check_unit_consistency(llm_response, source_data),
            self._check_reference_validity(llm_response, source_data)
        ]

        if not all(validations):
            # Flag for human review
            self.alert_service.notify_quality_team(
                response=llm_response,
                source=source_data,
                failed_validations=validations
            )

            # Return conservative response
            return self._generate_conservative_fallback(source_data)

        return llm_response

Pattern 4: Prompt Engineering for Clinical Accuracy

Clinical prompts require special care:

CLINICAL_SYSTEM_PROMPT = """You are a clinical AI assistant integrated into an electronic health record system. Your role is to help clinicians quickly find and understand information in patient charts.CRITICAL RULES:
1. Base ALL responses solely on the provided patient data
2. If data is insufficient to answer, explicitly state what's missing
3. Never speculate or make assumptions about clinical information
4. Cite specific data points (e.g., "BP was 145/92 on 2024-09-15")
5. Use appropriate medical terminology and units
6. Acknowledge uncertainty clearly
7. Flag concerning findings that require clinical judgment

RESPONSE FORMAT:
- Direct answer first
- Supporting data second
- Relevant caveats or limitations third

Remember: Clinicians are making real decisions based on your responses. Accuracy is paramount."""

Pattern 5: Audit Trail Architecture

HIPAA compliance requires comprehensive logging:

class HIPAACompliantAuditLogger:
    async def log_phi_access(self, event):
        """
        Log PHI access with all required HIPAA audit fields.
        """
        audit_entry = {
            # Who
            'user_id': event.user_id,
            'user_role': event.user_role,
            'user_department': event.department,

            # What
            'patient_id': self._hash_patient_id(event.patient_id),
            'data_accessed': event.data_types,
            'action': event.action,

            # When
            'timestamp': datetime.utcnow().isoformat(),
            'session_id': event.session_id,

            # Where
            'ip_address': event.ip_address,
            'location': event.physical_location,
            'system': 'MedicalAI',

            # Why
            'clinical_context': event.encounter_id,

            # How
            'access_method': 'AI_Query',
            'query': self._sanitize_query(event.query),
            'response_summary': self._sanitize_response(event.response)
        }

        # Write to immutable audit log
        await self.audit_store.write(audit_entry)

        # Also send to SIEM for real-time monitoring
        await self.siem.send_event(audit_entry)

Security Considerations

Defense in Depth

Implement multiple security layers:

[1]. Network Layer

  • All traffic TLS 1.3+
  • mTLS between internal services
  • Network segmentation (DMZ, application, data tiers)

[2]. Application Layer

  • Input validation and sanitization
  • Rate limiting per user/endpoint
  • CSRF protection
  • XSS prevention

[3]. Data Layer

  • Encryption at rest (AES-256)
  • Field-level encryption for PHI
  • Database access controls
  • Query parameterization

[4]. Identity Layer

  • Multi-factor authentication
  • Role-based access control (RBAC)
  • Principle of least privilege
  • Just-in-time access for emergencies

Performance Optimization Techniques

1. Request Coalescing

Combine multiple concurrent requests for the same data:

class RequestCoalescer:
    def __init__(self):
        self.pending = {}

    async def fetch(self, patient_id, resource_type):
        """
        Coalesce concurrent requests for same resource.
        """
        key = f"{patient_id}:{resource_type}"

        # If request already in flight, wait for it
        if key in self.pending:
            return await self.pending[key]

        # Start new request
        future = asyncio.Future()
        self.pending[key] = future

        try:
            data = await self._fetch_data(patient_id, resource_type)
            future.set_result(data)
            return data
        except Exception as e:
            future.set_exception(e)
            raise
        finally:
            del self.pending[key]

2. Predictive Prefetching

Anticipate clinician needs:

class PredictivePrefetcher:
    async def prefetch_likely_queries(self, patient_id, current_context):
        """
        Based on current context, prefetch likely next queries.
        """
        if current_context.location == 'ICU':
            # ICU clinicians likely to ask about vitals, labs
            asyncio.create_task(
                self.data.fetch_patient_data(
                    patient_id, 
                    ['Observation', 'MedicationRequest']
                )
            )
        elif current_context.encounter_type == 'pre-op':
            # Pre-op clinicians likely to ask about allergies, meds
            asyncio.create_task(
                self.data.fetch_patient_data(
                    patient_id,
                    ['AllergyIntolerance', 'MedicationStatement']
                )
            )

Evaluation and Quality Assurance

Continuous Monitoring

Production monitoring should track:

class ProductionMonitor:
    def __init__(self):
        self.metrics = MetricsCollector()
        self.alerting = AlertingService()

    async def monitor_request(self, request, response, metadata):
        """
        Track key metrics for every request.
        """
        # Performance metrics
        self.metrics.record({
            'endpoint': metadata.endpoint,
            'latency_ms': metadata.duration_ms,
            'tokens_input': metadata.tokens_in,
            'tokens_output': metadata.tokens_out,
            'model_used': metadata.model
        })

        # Quality metrics
        if response.validation_score < 0.8:
            self.metrics.increment('low_quality_responses')

        # Cost tracking
        cost = self._calculate_cost(
            metadata.model,
            metadata.tokens_in,
            metadata.tokens_out
        )
        self.metrics.record_cost(cost)

        # Alert on anomalies
        if metadata.duration_ms > 5000:
            await self.alerting.send_alert(
                'High Latency Detected',
                metadata
            )

Key Lessons for Healthcare AI Builders

Based on production deployment experience, here are essential lessons:

1. Start with Infrastructure

Don’t begin with the flashy chat interface. Build solid foundations first:

  • ✅ Real-time data access (Pillar 2)
  • ✅ LLM gateway (Pillar 1)
  • ✅ EHR integration (Pillar 4)
  • ✅ Then build applications (Pillar 3)

2. FHIR is Worth the Investment

Despite the complexity, FHIR provides:

  • Vendor neutrality
  • Industry standardization
  • Future-proofing
  • Real-time capabilities

Invest in FHIR expertise early.

3. Security Cannot Be Bolted On

Security must be designed into the architecture:

  • Assume breach at every layer
  • Log everything (within reason)
  • Validate all inputs and outputs
  • Never trust, always verify

4. Performance Matters in Clinical Settings

Clinicians won’t use a slow tool:

  • Target <2 second response times
  • Implement aggressive caching
  • Use parallel processing
  • Monitor performance continuously

5. Accuracy Over Cleverness

In healthcare, a conservative correct answer beats a creative wrong one:

  • Validate LLM outputs against source data
  • Flag uncertainty explicitly
  • Provide source citations
  • Implement conservative fallbacks

6. Design for Auditability

Every action must be traceable:

  • Who accessed what data?
  • When and why?
  • What was returned?
  • How was it used?

This isn’t optional — it’s regulatory and medico-legal necessity.

7. Think Beyond the Demo

Production healthcare AI requires:

  • 99.9%+ uptime
  • Graceful degradation
  • Disaster recovery
  • Incident response plans
  • Vendor failover strategies

8. Collaborate with Clinicians

Technology alone doesn’t improve healthcare:

  • Involve clinicians from day one
  • Observe actual workflows
  • Test with real users continuously
  • Iterate based on clinical feedback

9. Plan for Evaluation from the Start

Build evaluation into your architecture:

  • Log all interactions
  • Track quality metrics
  • Enable A/B testing
  • Support continuous improvement

10. Embrace Iteration

No one gets healthcare AI right on the first try:

  • Launch with limited scope
  • Gather real-world feedback
  • Iterate rapidly
  • Expand gradually

Conclusion

Building a production-grade AI system requires solving challenges that extend far beyond LLM integration. Real-time data access, domain accuracy, regulatory constraints, and seamless workflow integration all demand careful architectural design.

The four-pillar architecture described here demonstrates that these challenges are solvable through:

  • Layered architecture: Separation of concerns across pillars
  • Real-time source integration: Fresh data from systems of record
  • Secure LLM gateway: Centralized, auditable AI integration
  • Response validation: Never trust LLM output without verification
  • Production-grade engineering: Performance, reliability, scalability

For teams building similar systems, the technical patterns outlined here provide a foundation. The bigger lesson from production engineering is that architecture has to respect the operating environment. A system that performs well in a notebook or demo can still fail in production if it cannot handle live data, degraded dependencies, strict audit requirements, and sustained operational pressure.

My bias after 12 years working on production infrastructure is simple: treat AI like critical software, not experimental glue code. Build for observability, predictable failure modes, strong interfaces, and operational discipline from the start. That is what turns an interesting model integration into a system people can actually rely on.

Additional Resources

Standards & Specifications:

Implementation Frameworks:

  • SMART on FHIR
  • CDS Hooks
  • FHIR Bulk Data Access

This work represents independent professional experience and is not affiliated with any prior employer or specific institutional project.

This guide is based on lessons learned from 12 years of building and operating production systems. The patterns and principles here reflect practical implementation tradeoffs that show up once AI moves beyond prototypes and into real operational environments.


메타데이터
post_id
a422c0eb0b77
slug
building-production-grade-ai-systems-part-1-a422c0eb0b77
url
https://medium.com/@iammasariya/building-production-grade-ai-systems-part-1-a422c0eb0b77
canonical_url
https://medium.com/@iammasariya/building-production-grade-ai-systems-part-1-a422c0eb0b77
author_url
https://medium.com/@iammasariya
status
ok
fetched_at
2026-06-26 03:39:16