The Gap Between AI Demo and Production: A Checklist for What Changes
The demo extracted party names, flagged title defects, and impressed the legal team. Then production arrived with six languages, decades of scan quality degradation, and a review queue that never emptied, and the model was the least of our problems.
We built a document intelligence system that went from a working demo on a handful of properties to processing over a thousand properties across six Indian metros. Every section below describes something that didn't exist in the demo and had to be built before production. This is the checklist we wish we'd had at the start.
Why Most AI PoCs Never Ship
The demo worked on a handful of properties with clean, recent documentation, and on that sample it was genuinely good.
Production meant over a thousand properties across the full complexity described in the case study: six languages, decades of document history, OCR that garbled every third page of older scans, entity resolution across naming conventions that varied by state and decade, and a human review queue that was never empty. The model was the same. Everything around it had to be built from scratch.
Security: From "We'll Add It Later" to Non-Negotiable
What the PoC had: Nothing. Documents uploaded to S3, processed by the LLM, results returned. No access control, no audit trail, no PII handling.
What production required:
- Pre-retrieval access filtering on every vector query (database-level WHERE clauses, not middleware). Users only see chunks from documents they're authorized to access.
- PII detection on outputs: national ID numbers, tax IDs, bank account numbers redacted where the downstream consumer doesn't need raw values.
- Audit trail for every query: who asked, what was retrieved, what was generated.
- Input sanitization: adversarial text pattern detection at the chunking layer to prevent indirect prompt injection via documents.
- Virus scanning on every document upload.
We learned why pre-retrieval filtering was non-negotiable early: without it, semantic similarity could surface chunks from properties outside the user's access scope: a query about "HDFC mortgage deeds" (HDFC being a major Indian lender) would retrieve similar chunks across all properties, not just the ones the user was authorized to see. Middleware checks wouldn't reliably prevent this because the vector database had already loaded and scored the restricted chunks. Details in the security deep dive.
We learned why proactive security documentation matters when an enterprise CISO assessment revealed gaps that took weeks to close because we couldn't answer the questions.
Minimum viable security for any production AI system: pre-retrieval access control, audit logging, PII detection on outputs, and input sanitization. These four. Build them before the first real user touches the system.
Observability: From Console Logs to Production Monitoring
The PoC had print statements and a developer watching the terminal. Production needed:
- Retrieval quality monitoring: median re-ranker scores per query, with alerts on sustained drops.
- Extraction accuracy tracking: weekly sampled comparison of system outputs against human-verified values.
- OCR confidence distribution: tracking how much potentially unreliable content is in the vector store.
- Latency monitoring per pipeline stage: ingestion, OCR, retrieval, re-ranking, generation.
- Cost tracking per request, per property, per document type.
- Flag overturn rate: how often human reviewers disagree with the system's assessments.
The PoC never returned a wrong answer we noticed, because we were testing on clean documents with known answers. In production, the system returned wrong answers we only caught because we had retrieval quality metrics that showed a drop before anyone on the legal team reported it. Without these metrics, wrong answers don't fail loudly. They ship.
What debugging actually looked like: when extraction errors appeared, we traced them across stages: OCR → chunking → retrieval → re-ranking → generation. Most "model errors" were actually retrieval or OCR failures upstream. A wrong consideration amount wasn't the LLM hallucinating; it was the OCR engine misreading a digit, which produced a garbled chunk, which the retrieval layer served confidently. Without per-stage tracing, we'd have spent weeks tuning prompts for a problem that existed three layers earlier. 60% of our debugging time was spent upstream of the LLM.
The full observability stack is described in the observability deep dive. The short version: traditional APM tells you the request succeeded. For AI systems, you need monitoring that tells you the answer was correct.
Cost Management: From "It's Just API Calls" to Per-Property Attribution
We started with a shared API key and reviewed the monthly AWS bill in aggregate. That broke down fast once we hit scale:
- Per-property cost tracking: OCR pages processed, LLM tokens consumed, human review time spent.
- Per-stage cost breakdown: how much of the per-property cost is OCR vs extraction vs retrieval vs human review.
- Batch API usage for non-latency-sensitive work (Anthropic's batch API roughly halved extraction costs).
- Right-sizing: compute task sizes based on actual profiling, not default overprovisioning.
Our PoC cost was negligible. We assumed linear scaling. It wasn't even close. Complex properties with long chains, older documents, and multiple extraction iterations cost 3-5x more than simple apartments. Without per-property cost attribution, the average masks the variance and your cost projections are wrong.
The economics breakdown is in the case study. The per-property cost varied widely, but the average was skewed by a portfolio that was 75-80% relatively clean apartments. Your numbers will differ based on document complexity.
Error Handling: From Retry Logic to Graceful Degradation
What the PoC had: Try/catch around the LLM call. Retry on failure.
In production, failures clustered into four categories: model-level errors (malformed extractions), retrieval failures (wrong or insufficient context), upstream data quality issues (OCR, chunking), and system-level bottlenecks (rate limits, queue backups). The first instinct is to fix them all in the generation layer. The right instinct is to handle each where it originates.
Model returns garbage. Not an error, not a timeout, just a malformed or nonsensical extraction. The schema validation layer catches extractions that don't match expected formats (dates that aren't dates, amounts that aren't numbers, party names that are sentence fragments). Failed validations route to human review instead of entering the pipeline.
Retrieval returns nothing relevant. Low re-ranker scores across all candidates mean the system doesn't have good content for this query. Instead of generating from poor context, the system returns "insufficient data for confident extraction on this field" and flags for human review. In a legal system, "I don't know" is better than a guess.
LLM provider rate-limited or unavailable. Queue the work and retry with exponential backoff. For batch processing (which most of our pipeline was), a 30-minute provider outage means a 30-minute delay, not a failure. The workflow orchestration handled retry logic per processing stage.
OCR produces unreliable output. The three-tier OCR approach (cloud OCR > multimodal LLM > human review) is itself an error handling strategy. Each tier is a fallback for the one above it.
Human review queue backs up. When the queue exceeded the reviewers' daily capacity, the pipeline didn't stop. It continued processing documents that didn't need human review and queued the rest. This meant the output was delivered incrementally (clean properties first, complex ones later) rather than blocked on the bottleneck.
Infrastructure: From Dev Account to Production AWS
The PoC ran on a single EC2 instance in a default VPC, API keys in environment variables, no isolation. Production required a real setup:
- VPC design: AI processing in private subnets with no public internet access for processing workloads.
- Secrets management: All API keys managed and rotated through a secrets service. Not in environment variables, not in code, not in config files.
- Container isolation: Each processing job isolated so that one property's documents are never accessible to another job in memory or on disk. We learned this one concretely: early in development, a shared process reused across jobs briefly held documents from multiple properties in memory simultaneously. Nothing leaked, but the window existed. Isolation wasn't optional after that.
- IAM least-privilege: Each service accessed with minimum required permissions. No admin keys shared across services.
- Auto-scaling for burst workloads: The pipeline processes documents in batches. Compute scales up during processing hours and scales to zero overnight. The PoC ran 24/7 on a single instance regardless of load.
- Environment separation: Dev, staging, production. The PoC ran in dev. Production requires a clean deployment pipeline with separate databases, separate API keys, and separate access controls.
The Checklist
Everything above, compressed into a pass/fail list. Each item is something that didn't exist in our PoC and had to exist before production.
If you do only five things before shipping, do these:
- Pre-retrieval access control (database-enforced, not middleware), because post-retrieval filtering leaks information and is architecturally unfixable later.
- Audit logging for all AI interactions, because you can't debug, comply, or answer a CISO questionnaire without it.
- Retrieval quality monitoring with automated alerts, because RAG failures are silent; the system returns confident wrong answers.
- Schema validation on all LLM extraction outputs, because the model will return garbage occasionally, and without validation it enters your pipeline as fact.
- Human escalation path with structured handoff, because every production AI system needs a "I don't know, ask a human" path that actually works.
These five are existential. The rest below are important, but if you're shipping Friday, start here.
Security:
- Pre-retrieval access control (database-enforced, not middleware)
- PII detection and redaction on system outputs
- Audit trail for all AI interactions (query, retrieval, generation)
- Input sanitization for adversarial text patterns
- Virus scanning on document uploads
Observability:
- Retrieval quality monitoring with automated alerts
- Extraction accuracy tracking (sampled comparison against human-verified values)
- OCR confidence distribution tracking
- Per-stage latency monitoring
- Cost tracking per property and per pipeline stage
Cost management:
- Per-property cost attribution
- Batch API usage for non-latency-sensitive LLM calls
- Right-sized compute (profiled, not default)
Error handling:
- Schema validation on all LLM extraction outputs
- Graceful degradation when retrieval confidence is low ("insufficient data" instead of guessing)
- Retry logic with backoff for provider rate limits
- Incremental delivery (don't block on bottlenecks)
Infrastructure:
- VPC with private subnets for AI processing
- Secrets management (no API keys in code or env vars)
- Container isolation per processing job
- IAM least-privilege per service
- Environment separation (dev/staging/prod)
Guardrails:
- Citation verification on extraction outputs
- Confidence thresholds per field with human escalation
- Human review queue with structured handoff (context, not just "please review")
- Feedback loop from human corrections back to threshold calibration
That's 26 items. Our PoC had zero of them. None of them involve changing the model.
Content on this page may not be reproduced, distributed, or republished without prior written permission. Sharing links is encouraged. See our Terms of Use for details.