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

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:
- Security at Every Layer: User validated before data access, PHI never leaves enterprise, every action logged
- Performance: Parallel processing + caching = sub-second responses despite complex data retrieval
- Accuracy: LLM response validated against source data, full traceability maintained
- 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