Chapter 4.1 - Python Backend Questions / Backend
Info Comprehensive interview questions about Python Backend and scalable AI inference.
Interview Questions
Q1: How would you build a scalable FastAPI backend for AI inference?
Answer: A scalable FastAPI backend for AI inference requires a multi-layered approach:
- Asynchronous Framework: Use FastAPI's async capabilities to handle I/O-bound tasks without blocking the main thread.
- Decoupled Inference: Offload heavy ML model inference to background workers (like Celery or RQ) or a dedicated model serving framework (like vLLM, Triton, or Ray Serve) rather than running inference directly in the API process.
- Load Balancing & Orchestration: Deploy the API behind a load balancer (e.g., Nginx, HAProxy) using Kubernetes or AWS ECS to auto-scale based on CPU/GPU utilization or queue length.
- Caching: Implement Redis for semantic caching or exact-match caching to serve repeated queries instantly without hitting the model.
Q2: What is the difference between synchronous and asynchronous APIs?
Answer:
- Synchronous APIs process requests sequentially. If a request involves a long-running task (like generating text from an LLM), the worker thread is blocked until the task completes, meaning it cannot handle other incoming requests during that time.
- Asynchronous APIs (using
asyncioin Python) allow the server to handle other requests while waiting for I/O operations (like database queries, network calls, or external API calls) to complete. This drastically improves concurrency and throughput for I/O-bound workloads.
Q3: How would you handle concurrent inference requests?
Answer:
- Message Queues: Route requests through a message broker (RabbitMQ, Kafka, or Redis Pub/Sub). The API layer acts as a producer, and inference workers act as consumers.
- Dynamic Batching: Group concurrent requests together in a queue and process them in a single forward pass on the GPU to maximize hardware utilization.
- Concurrency Limits: Use semaphores or rate limiters to prevent overwhelming the GPU memory, returning
429 Too Many Requestsor queuing requests gracefully.
Q4: What causes memory bottlenecks in Python AI systems?
Answer:
- Global Interpreter Lock (GIL): Prevents true multi-threading in CPU-bound Python tasks, forcing the use of memory-heavy multi-processing.
- Object Overhead: Python objects have significant memory overhead compared to raw C/C++ data structures.
- Data Duplication: Passing large tensors or dataframes between processes (e.g., in a Celery queue) often requires serializing and deserializing data, doubling memory usage.
- GPU VRAM Spikes: Loading multiple large models into VRAM simultaneously or using unoptimized batch sizes without KV cache management.
Q5: How would you optimize API throughput?
Answer:
- Continuous Batching: For LLMs, process requests at the iteration level rather than the prompt level, allowing new requests to join a batch as soon as others finish.
- Model Quantization: Use INT8, INT4, or FP8 quantization (e.g., AWQ, GPTQ) to reduce model size and memory bandwidth bottlenecks.
- Optimized Runtimes: Replace standard Hugging Face
transformerswith optimized engines like TensorRT-LLM, vLLM, or ONNX Runtime. - Caching & CDN: Cache frequent API responses at the edge.
Q6: How would you implement request batching for GPU inference?
Answer: Instead of processing requests 1-by-1 (batch size = 1), you collect incoming requests over a small time window (e.g., 50ms) or until a maximum batch size is reached. For LLMs, Continuous Batching (or iteration-level batching) is the standard: the scheduler continuously injects new requests into the GPU's execution batch as soon as an older sequence completes its generation, maximizing GPU utilization without waiting for the longest sequence in a static batch to finish.
Q7: How would you design rate limiting for AI APIs?
Answer: AI APIs should be rate-limited by Tokens or Compute Time, not just by Request Count.
- Token Bucket Algorithm: Use Redis to maintain a token bucket for each user. Deduct tokens based on the prompt size + max generated tokens.
- Tiered Limits: Offer different limits for different user tiers (e.g., Free vs. Pro).
- Headers: Always return standard headers (
X-RateLimit-Limit,X-RateLimit-Remaining) so clients can implement backoff strategies.
Q8: How would you stream LLM responses token by token?
Answer:
In FastAPI, use StreamingResponse combined with Python asynchronous generators (yield).
The inference engine (or external API like OpenAI) must support streaming. As the model generates each token, the backend yields it in a Server-Sent Events (SSE) format, allowing the client frontend to render the text typewriter-style immediately without waiting for the entire generation to finish.
Q9: How would you handle background tasks in FastAPI?
Answer:
For lightweight tasks (e.g., sending a confirmation email, logging), use FastAPI's built-in BackgroundTasks which runs in the same event loop.
For heavy tasks (e.g., indexing a large PDF, running a long LLM chain), use a distributed task queue like Celery or ARQ with Redis/RabbitMQ. The API instantly returns a task_id (202 Accepted), and the client polls a status endpoint or listens via WebSockets for completion.
Q10: How would you secure an AI API in production?
Answer:
- Authentication: Require API keys (validate via Redis/DB) or OAuth2/JWT for user access.
- Input Validation & Sanitization: Strictly validate request schemas using Pydantic. Use prompt injection filters (like NeMo Guardrails) before passing inputs to the LLM.
- Network Security: Use HTTPS/TLS, place the API behind a WAF (Web Application Firewall), and restrict internal networking (e.g., the API can only talk to the vector DB via private subnets).
- Data Privacy: Ensure PII scrubbing before sending data to third-party APIs or logging systems.