Spaces:
Sleeping
MediShield AI Document Classifier β Architecture & Design
Complete technical architecture documentation for the insurance document classification system.
π System Architecture Overview
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Browser / Client β
β Drag & Drop UI Β· frontend/index.html β
ββββββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββ
β POST /classify (multipart/form-data)
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β FastAPI Server Β· src/api.py Β· Port 8000 β
β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β asyncio.gather β Concurrent Processing β β
β β β β
β β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β β β Orchestrator: src/classifier.py β β β
β β β β β β
β β β ββββββββββββββββ bill_* ββββββββββββββββββββββββ β β β
β β β β Stage 1 ββββββββββββΆβ doc_type = "bill" β β β β
β β β β Rules Engine β β method = "rules" β β β β
β β β β β ββββββββββββββββββββββββ β β β
β β β ββββββββ¬ββββββββ β β β
β β β β others β β β
β β β βΌ β β β
β β β ββββββββββββββββ KYC kw ββββββββββββββββββββββββ β β β
β β β β Stage 2 ββββββββββββΆβ doc_type = "kyc" β β β β
β β β β KYC OCR β β method = "ocr" β β β β
β β β β (easyocr) β ββββββββββββββββββββββββ β β β
β β β ββββββββ¬ββββββββ β β β
β β β β no KYC match β β β
β β β βΌ β β β
β β β ββββββββββββββββ ββββββββββββββββββββββββ β β β
β β β β Stage 3 ββββββββββββΆβ doc_type = "image" β β β β
β β β β Gemini LLM β β sub_type = category β β β β
β β β β β β method = "llm" β β β β
β β β ββββββββββββββββ ββββββββββββββββββββββββ β β β
β β β β β β
β β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β β β β
β β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β β β Monitoring: src/monitoring.py β β β
β β β @traceable spans Β· token counts Β· latency metrics β β β
β β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β β
β β β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Response JSON: { β β
β β filename, doc_type, sub_type, method, β β
β β latency_ms, tokens_used, confidence_score β β
β β } β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ¬βββ β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββΌβββββββ
β
βΌ
ββββββββββββββββββββββββ
β Frontend UI β
β Results table with β
β color-coded badges β
ββββββββββββββββββββββββ
π Three-Stage Classification Pipeline
Stage 1: Rules Engine (src/rules_engine.py)
Trigger: Filename analysis
Method: Regex pattern matching
Cost: $0
Speed: <1ms
Success Rate: ~70% of documents
def rules_engine(filename: str, image_data: bytes) -> Optional[Dict]:
"""
Match documents by filename patterns.
Fast, zero-cost, high-precision.
"""
# Example patterns
if filename.startswith("bill_"):
return {
"doc_type": "bill",
"method": "rules",
"confidence": 1.0,
"latency_ms": 0.5
}
return None
Patterns matched:
bill_*β doc_type = "bill"invoice_*β doc_type = "invoice"receipt_*β doc_type = "receipt"
When to use: Fast, immediate classification when metadata is reliable.
Stage 2: KYC OCR Detection (src/kyc_detector.py)
Trigger: Documents unmatched at Stage 1
Method: easyOCR text extraction + keyword matching
Cost: $0.001 per document6% of total)
Speed: 1-2 seconds
Success Rate: ~20% of remaining documents (
def kyc_detector(image_data: bytes) -> Optional[Dict]:
"""
Detect KYC documents (Aadhaar, PAN, Passport) via OCR.
Medium speed, cheap, reliable for document types.
"""
# Use easyOCR to extract text
ocr_text = easyocr.recognize(image_data)
# Match against KYC keywords
kyc_keywords = ["aadhaar", "pan", "passport", "voter id", "driving license"]
if any(keyword in ocr_text.lower() for keyword in kyc_keywords):
return {
"doc_type": "kyc",
"sub_type": detect_kyc_subtype(ocr_text),
"method": "ocr",
"confidence": 0.95,
"latency_ms": 1500
}
return None
Document types detected:
- Aadhaar β sub_type = "aadhaar"
- PAN β sub_type = "pan"
- Passport β sub_type = "passport"
- Voter ID β sub_type = "voter_id"
- Driving License β sub_type = "driving_license"
When to use: When OCR is fast enough and keyword matching is reliable.
Stage 3: Gemini LLM Classification (src/llm_classifier.py)
Trigger: All documents unmatched at Stages 1 & 2
Method: Gemini API (gemma-4-31b-it)
Cost: ~$0.01 per document
Speed: 2-4 seconds
Success Rate: ~10% of documents (only complex cases)
def llm_classifier(image_data: bytes, ocr_text: str) -> Dict:
"""
Full AI classification for complex/ambiguous documents.
Slow, expensive, but handles edge cases.
"""
prompt = f"""
Classify this insurance document.
OCR extracted text (may be partial/noisy):
{ocr_text}
Image provided for visual analysis.
Respond with JSON:
{{
"doc_type": "image|letter|form|other",
"sub_type": "prescription|lab_report|claim_form|...",
"confidence": 0.0-1.0,
"reasoning": "brief explanation"
}}
"""
response = gemini_api.generate(
image=image_data,
text=prompt
)
return {
"doc_type": response.doc_type,
"sub_type": response.sub_type,
"method": "llm",
"confidence": response.confidence,
"latency_ms": elapsed_time,
"tokens_used": response.usage.total_tokens
}
Document types classified:
- Prescriptions
- Lab reports
- Claim forms
- Medical letters
- X-ray reports
- Test certificates
- Insurance documents (various)
When to use: Complex, ambiguous cases where Rules + OCR aren't sufficient.
β‘ Async Concurrency Architecture
Request Flow
FastAPI Handler (src/api.py)
β
ββ Read multipart form data
ββ Extract file list: [file1.pdf, file2.pdf, ...]
β
βΌ
asyncio.gather([
executor.submit(classify_document, file1),
executor.submit(classify_document, file2),
...
])
β
ββ Runs all files in PARALLEL thread pool
Each file: Stage1 β Stage2 β Stage3 (cascading, any can short-circuit)
β
βΌ
Collect results, emit LangSmith traces
β
βΌ
Return JSON response
Performance Characteristics
Scenario A: All Stage 1 matches (bills with bill_ prefix)
- Input: 10 bills
- Processing: 10 Γ <1ms = ~10ms total
- Response time: ~100ms (overhead)
Scenario B: Mixed (70% rules, 20% OCR, 10% LLM)
- Input: 100 documents
- Stage 1: 70 docs Γ <1ms = ~0.07s
- Stage 2: 20 docs Γ 1.5s = ~30s parallel (1 thread per doc)
- Stage 3: 10 docs Γ 3s = ~30s parallel (1 thread per doc)
- Total: ~30s (OCR & LLM run in parallel, limited by slowest)
Optimization: With N worker threads and M documents:
- If M β€ N: perfect parallelism, time β max(latencies)
- If M > N: queued, time β sum(latencies) / N
π Monitoring & Observability
LangSmith Tracing (@traceable decorator)
Each stage emits structured spans to LangSmith:
from langsmith import traceable
@traceable(name="rules_engine")
def rules_engine(filename: str):
# Automatic tracing:
# - Execution time
# - Input/output
# - Errors
pass
@traceable(name="kyc_ocr")
def kyc_detector(image_data: bytes):
# Token usage tracked automatically
# Latency metrics collected
pass
@traceable(name="gemini_llm")
def llm_classifier(image_data: bytes):
# Detailed LLM call tracing
# Token usage: input + output
# Model name, parameters, latency
pass
Metrics Collected
Per-request metrics:
- Request ID
- Number of documents
- Document types (histogram)
- Total latency (ms)
- Breakdown by stage
- Token usage (for LLM stage)
- Classification confidence scores
Real-time dashboards:
- Azure Monitor: logs, alerts, performance
- LangSmith: traces, token usage, latency percentiles
π Deployment Architecture
Azure Container Apps Stack
βββββββββββββββββββββββββββββββββββββββββββ
β Azure Container Apps (ACA) β
β - 0.5 vCPU, 2GB RAM per replica β
β - Min 1 replica, Max 3 (auto-scale) β
β - HTTPS endpoint β
β - Auto-scaling on CPU/memory β
ββββββββββββββββββ¬βββββββββββββββββββββββββββ
β
ββββββββββββββΌβββββββββββββ
β β β
βΌ βΌ βΌ
ββββββββββ ββββββββββ ββββββββββ
βReplica1β βReplica2β βReplica3β
β(running) β(standby) β(standby)
ββββββββββ ββββββββββ ββββββββββ
β
βββββββββββββββββββ¬ββββββββββββββββββ
β β
βββββββββββββββββββΌβββββββ ββββββββββΌβββββββ
β Azure Monitor β β LangSmith β
β - Logs β β - Traces β
β - Performance β β - Token usageβ
β - Alerts β β - Latency β
ββββββββββββββββββββββββββ βββββββββββββββββ
Container Image
Dockerfile:
FROM python:3.12-slim
WORKDIR /app
# Install dependencies
COPY requirements.txt .
RUN pip install -r requirements.txt
# Copy source
COPY src/ ./src/
COPY frontend/ ./frontend/
# Expose port
EXPOSE 8000
# Run with uvicorn
CMD ["uvicorn", "src.api:app", "--host", "0.0.0.0", "--port", "8000"]
Image stored in: Azure Container Registry (medishieldacr.azurecr.io)
π Security Architecture
API Security
- HTTPS only via Azure Container Apps
- CORS: Configured for frontend origin
- File validation: Size limits, MIME type checks
- Input sanitization: Filename validation, size bounds
Data Privacy
- No persistent storage: Processed files deleted after classification
- Transient memory: Results live only in response
- Encrypted in transit: TLS 1.2+
- Audit logs: All classifications logged to Azure Monitor
Secrets Management
- Environment variables via Azure Container Apps secrets
- Gemini API key: Never in code, via env
- LangSmith key: Via env, read-only service key
π Scalability Analysis
Current Setup
- Max throughput: ~1000 documents/hour (with 1 replica, max 3s per doc)
- Bottleneck: Gemini API rate limits (~100 requests/minute)
- With 10% of docs hitting Gemini β ~100 docs/min Γ 10 = 1000 docs/min
- Actual: limited by API quotas
Scaling Options
- Increase replicas: Auto-scaling to 3 replicas β 3x throughput
- Batch processing: Collect documents, process asynchronously β decoupled throughput
- Queue-based: Azure Service Bus β robust handling of traffic spikes
- Cache results: Store common patterns (frequent filenames) β reduce processing
π§ͺ Testing Strategy
117 Passing Tests
Unit tests (70):
- Stage 1 rules engine (10 test cases)
- Stage 2 OCR patterns (15 test cases)
- Stage 3 LLM prompt formatting (10 test cases)
- Async orchestration (15 test cases)
- Monitoring/tracing (10 test cases)
- Response validation (10 test cases)
Integration tests (30):
- End-to-end classification pipeline (10 test cases)
- Multi-file concurrent processing (5 test cases)
- Error handling & retry logic (5 test cases)
- API endpoint validation (5 test cases)
- LangSmith trace verification (5 test cases)
Performance tests (10):
- Latency benchmarks (Stage 1, 2, 3)
- Concurrency stress tests
- Memory usage profiling
Edge cases (7):
- Empty files
- Corrupt images
- Unicode filenames
- Very large files
- Timeout handling
π Cost Model
Per-Document Costs
| Stage | Cost | Trigger | Frequency |
|---|---|---|---|
| Stage 1 (Rules) | $0.00 | Filename pattern | 70% |
| Stage 2 (OCR) | ~$0.001 | easyOCR library | 20% |
| Stage 3 (LLM) | ~$0.01 | Gemini API call | 10% |
Expected cost per document: (0.7 Γ $0) + (0.2 Γ $0.001) + (0.1 Γ $0.01) = $0.0013
Annual cost (1M documents): 1,000,000 Γ $0.0013 = $1,300 (AI costs only)
Infrastructure: Azure Container Apps ~$50-100/month (compute + storage)
Total monthly: ~$200 (AI + compute + monitoring)
vs. Manual Labor
- Manual operator: ~$3,000/month salary
- 12 operators: $36,000/month
- 2 remaining operators: $6,000/month
- Savings: ~$30,000/month
ROI: Pays for itself in <1 week of savings.
π Continuous Improvement
Metrics to Monitor
- Accuracy by stage: Track Stage 3 confidence scores
- False negatives: Documents incorrectly classified at Stage 1 or 2
- Latency trends: Identify performance regressions
- Token usage: Monitor Gemini API efficiency
- Cost per document: Optimize stage cascade
Feedback Loop
Production metrics β Identify misclassifications
β
Add new rules β Retrain Stage 1/2
β
Deploy β Test in staging
β
Compare accuracy vs production
β
If better: deploy; else: revert
π Related Documentation
- WORKFLOW_DIAGRAM.md β Mermaid diagrams
- DIAGRAMS.md β Interactive Excalidraw diagrams
- README.md β Project overview
- src/api.py β API implementation
- src/classifier.py β Pipeline orchestration
Generated: April 26, 2026
Document version: 2.0 (architecture diagrams added)