1 From notebook to product
Imagine you baked an amazing cake at home, just for yourself. That's your notebook โ it works, but only on your kitchen table, only when you're standing there. Deployment is opening a bakery: now anyone can walk in, order a cake any time, and get one even when you're asleep. To do that you need a counter people can order from, a kitchen that keeps running, and a way to handle a crowd. That's the difference between "it works for me" and "it works for everyone."
So far, your GenAI code has probably lived in a Jupyter notebook or a script you run by hand. Deployment means packaging that code and putting it somewhere that runs continuously and is reachable over the internet, so other people (or other programs) can use it without you being involved each time.
Serving your app behind an API
The standard way to expose a GenAI app is behind an API (Application
Programming Interface) โ a fixed "front door" that accepts requests and returns responses.
Instead of running a cell, a user's browser or app sends an HTTP request like
POST /chat with their question, and your server returns the model's answer
as JSON. Your server sits in the middle: it receives the request, builds the prompt, calls the LLM
(recall the request โ response flow from Session 1), and sends the result back.
| Layer | What it does | Beginner analogy |
|---|---|---|
| Client | The user's browser, mobile app, or another service that sends questions. | The customer at the counter. |
| API / server | Your code: receives requests, builds prompts, calls the model, returns answers. | The bakery counter & kitchen. |
| Model | The LLM doing the actual generation (hosted API or self-hosted). | The oven that bakes. |
Demo vs production โ they are not the same
A demo just needs to work once, in front of a friendly audience, on your laptop. Production means real users depend on it, so it must be reliable, secure, observable, and affordable. This is the single biggest mindset shift of this session.
| Concern | Demo | Production |
|---|---|---|
| Who uses it | You, on your laptop. | Many real users, any time, anywhere. |
| Uptime | Only while you run it. | 24/7 โ it must survive crashes & restarts. |
| Errors | You see the traceback and fix it. | Must be caught, logged, and handled gracefully. |
| Secrets | API key pasted in a cell. | Stored securely (env vars / secrets manager), never in code. |
| Cost | A few cents of tokens. | Tracked, capped, optimised (caching, rate limits). |
| Observability | You watch the output. | Logging, metrics, tracing (recall Session 8). |
The number one beginner mistake is hard-coding your model API key into the source file and pushing it to GitHub. Keys get scraped within minutes and someone runs up a huge bill on your account. Always load secrets from environment variables or a secrets manager, and never commit them.
You wrap your RAG chatbot in a tiny web server (say FastAPI in Python). It exposes one endpoint:
POST /chat
{ "message": "What is our refund policy?" }
โ 200 OK
{ "answer": "You can request a refund within 30 days...",
"sources": ["policy.pdf p.4"] }
Now a website's "Ask us" box can call this endpoint. Your notebook just became a product others can build on โ without ever seeing your code.
2 Deploying on AWS
You don't need to buy your own building to open a bakery โ you can rent a shop space. The cloud (like AWS) is renting computers by the hour instead of buying them. AWS gives you the shop, the electricity, the security guards, and lets you grow from one tiny counter to a hundred shops overnight without ever owning a single brick. You just pay for what you use.
AWS (Amazon Web Services) is the most popular cloud provider. You don't need to memorise its hundreds of services โ for a GenAI app, a handful of patterns cover almost everything. Let's stay conceptual but concrete.
The big decision first: hosted model API vs self-hosting
| Approach | What it means | Good when |
|---|---|---|
| Hosted model API | You call someone else's model over the internet (e.g. the Claude API from Anthropic, or AWS Bedrock which hosts many models for you). You don't manage any GPUs. | Almost always for beginners & most products โ fastest, no GPU ops, pay per token. |
| Self-hosting | You run an open-weight model (e.g. Llama, Mistral) yourself on GPU servers. | Strict data-privacy needs, very high steady volume, or heavy customisation โ but you own all the ops & cost. |
Start with a hosted model API. You only deploy your own app code (the server that builds prompts and calls the model) โ the heavy GPU work stays with the provider. Self-hosting is a big jump in complexity and is rarely worth it until you have a specific reason.
Common patterns for deploying your app code
| Pattern | What it is | Best for |
|---|---|---|
| Containers on ECS / Fargate | You package your app in a container (a Docker image โ your code plus everything it needs). ECS runs it; Fargate means AWS manages the underlying servers for you ("serverless containers"). | Long-running services, agents that need steady warm processes, most GenAI APIs. |
| AWS Lambda (serverless) | You upload just a function. It runs only when called and scales to zero when idle. You pay per invocation. | Spiky, short, occasional workloads. Watch out for cold starts and time limits with long agent runs. |
| API Gateway | The managed "front door" that receives HTTP requests, handles auth, throttling, and routes them to your Lambda or container. | Putting a clean, secure public API in front of either of the above. |
A container is a sealed box holding your code and its exact dependencies and settings, so it runs identically on your laptop, a teammate's machine, and AWS. It kills the classic "but it works on my machine!" problem. Docker is the most common tool for building these boxes.
Your customer-support agent goes live like this:
- Code packaged as a Docker container, running on ECS Fargate (no servers to babysit).
- An Application Load Balancer spreads traffic across multiple copies of the container.
- API Gateway is the public front door, checking API keys and throttling abusers.
- The container calls a hosted model API (Bedrock or the Claude API) for the actual LLM work.
- Conversation state lives in DynamoDB (more on external state in Topic 3).
- Secrets (model API keys) live in AWS Secrets Manager, not in the code.
No GPUs to manage, scales automatically, and you only pay for traffic you actually get.
3 Scaling for many users
One day, one customer comes to your bakery โ easy. But what if a thousand show up at once? You have two choices: hire a stronger super-baker who works faster (that's "vertical"), or open ten identical counters with ten bakers (that's "horizontal"). Opening more counters works far better for big crowds โ and a friendly host at the door (load balancer) sends each customer to whichever counter is free.
Scaling is handling more users without your app slowing down or crashing. There are two directions.
Vertical vs horizontal scaling
| Type | What you do | Limits |
|---|---|---|
| Vertical (scale up) | Give one server more power (more CPU/RAM). | There's a ceiling โ you can only buy so big a machine, and it's a single point of failure. |
| Horizontal (scale out) | Run many copies of your server side by side. | Nearly unlimited, and if one copy dies the others keep serving. The preferred approach for web apps. |
Load balancing
When you run many copies, a load balancer sits in front and distributes incoming requests across them, so no single copy gets overwhelmed. If one copy becomes unhealthy, the balancer stops sending traffic to it. This is what makes horizontal scaling work.
Stateless services + external state
For horizontal scaling to work, each copy of your server must be stateless โ it shouldn't store anything important in its own memory, because the next request from the same user might land on a different copy. This connects directly to Session 1: the LLM itself is stateless, and to memory sessions from earlier in the course where we stored conversation history externally.
Keep your servers stateless and put any state that must persist โ conversation history, user memory, agent context โ in an external store (a database like DynamoDB/Postgres, a cache like Redis, or a vector DB for RAG). Then any copy can serve any user, because the truth lives outside the server. This is exactly the "memory sessions" pattern from earlier sessions, now deployed at scale.
Async & queues for long agent runs
A simple chat reply comes back in seconds. But an agent (Sessions on agents & tool use) might call many tools, do multi-step reasoning, and take minutes. Making a user's request "hang" for minutes is fragile โ connections drop, and one slow job ties up a server. The fix is asynchronous (async) processing with a queue.
A user asks your agent to "research five competitors and write a report." Instead of freezing
the page for three minutes, the API immediately returns { "job_id": "abc123",
"status": "running" }. A background worker picks the job off an SQS queue,
runs the agent (calling tools, the LLM, etc.), and writes the finished report to the database.
The user's screen checks GET /jobs/abc123 every few seconds and shows
the report the moment it's ready. Servers stay responsive; long work happens safely in the
background.
4 Caching to cut cost & latency
If ten people ask the baker "what time do you open?", the baker doesn't bake a fresh answer ten times โ they write it on a sign by the door. Caching is keeping a ready-made answer for questions you've already answered, so you can hand it back instantly and for free instead of doing all the work again. Less waiting, less money spent.
Every LLM call costs tokens (money) and time (latency) โ recall the token economics from Session 1. Caching stores previous results so repeated or similar work can skip the model entirely. A cache hit means the answer was found in the cache (fast, free); a cache miss means it wasn't, so you do the real work and store it for next time.
Three kinds of caching for GenAI
| Type | What it caches | When it helps |
|---|---|---|
| Response caching | The whole answer, keyed by the exact input (e.g. exact prompt). Identical request โ return stored answer. | Repeated identical questions (FAQs, popular queries). |
| Semantic caching | Caches by meaning, not exact text. Uses embeddings (Session 1) to find a past question that means the same thing, even if worded differently. | "What's your refund policy?" vs "How do I get my money back?" โ same answer. |
| Prompt / KV caching | The model reuses the computation for a long, unchanging chunk of the prompt (a big system prompt, RAG context). The provider caches the internal KV (key-value) state from the prefill. | Long, repeated system prompts or shared context across many requests. |
It turns the new question into an embedding vector and measures the distance to cached questions (just like RAG search). If a past question is within a similarity threshold, it returns that cached answer. Set the threshold carefully: too loose and you return wrong answers; too tight and you rarely get a hit.
Remember prefill from Session 1 โ the model reading your whole prompt before it generates. If 10,000 requests all share the same 2,000-token system prompt, the model would normally re-process those 2,000 tokens every single time. Prompt caching lets the provider remember that processed prefix, so repeated requests are cheaper and faster. Providers like Anthropic offer this on the Claude API, billing cached input tokens at a steep discount.
Say each support answer is a 1,000-token input + 500-token output call costing about $0.0125 (the worked example from Session 1). Your top FAQ โ "How do I reset my password?" โ gets asked 20,000 times a month.
- No cache: 20,000 ร $0.0125 = $250/month, and every user waits a couple of seconds.
- With caching: the model runs once (~$0.0125); the other 19,999 are cache hits โ near-zero cost and answered in milliseconds.
Add semantic caching and even reworded versions ("forgot my password", "can't log in") hit the same cached answer. One FAQ just went from $250 to a rounding error.
Don't cache things that should be fresh or personal โ anything user-specific, time-sensitive, or private. And give cached entries an expiry (a TTL โ time to live) so stale answers don't live forever after your policy changes.
5 Rate limiting
A water fountain has a small spout on purpose โ so one greedy person can't drink it all and leave none for everyone else. Rate limiting is that small spout: it caps how fast any one user (or your whole app) can make requests, so nobody floods the system, runs up a giant bill, or breaks it for others.
Rate limiting controls how many requests are allowed in a given time window. It protects you in two directions: limiting your users so no one abuses or overloads your app, and respecting the model provider's limits so your app doesn't get blocked.
Provider limits: RPM and TPM
Model providers cap your usage, usually as:
- RPM (requests per minute) โ how many API calls you can make per minute.
- TPM (tokens per minute) โ how many tokens (input + output) you can push through per minute.
Go over either and the provider returns a 429 Too Many Requests error.
Your app must handle this gracefully instead of crashing.
Strategies
| Strategy | How it works | Good for |
|---|---|---|
| Token bucket | Each user has a "bucket" that refills at a steady rate. Each request spends a token from the bucket; empty bucket = request rejected or delayed. Allows short bursts but caps the average. | Limiting your own users fairly. |
| Queue | Excess requests wait in line and are released at a safe pace instead of being rejected. | Smoothing spiky traffic against provider TPM/RPM limits. |
| Retry with backoff | On a 429, wait and retry โ and wait longer each time (exponential backoff), ideally with a little randomness (jitter) so all clients don't retry in sync. | Recovering from temporary provider limits. |
Picture a bucket that drips full at, say, 60 drops per minute. Each request scoops one drop. Quiet for a while? The bucket fills, so you can handle a sudden burst. Hammering it? You drain the bucket and get throttled until it refills. It's forgiving of bursts but firm on the long-run average.
Your app is briefly over the provider's TPM limit and gets a 429.
Instead of giving up or retrying instantly (which just makes it worse), it retries after 1s, then
2s, then 4s, then 8s (doubling each time, plus a touch of random jitter). By the third try the
limit window has reset and the request succeeds โ the user never even noticed, beyond a slight
delay.
Rate limiting isn't only about stability. A single buggy client (or a malicious one) looping requests could burn thousands of dollars of tokens in minutes. Per-user caps put a hard ceiling on how much any one actor can cost you โ pairing perfectly with the cost-tracking habits from token economics in Session 1.
429s. It's a key cost-control tool.
6 CI/CD & reliability practices
Imagine every time you change a recipe, a robot helper automatically bakes a tiny test cake, tastes it, and only lets the new recipe into the shop if it's good. CI/CD is that robot helper for your code: it automatically checks every change and safely ships it, so you don't accidentally serve a broken cake to customers.
CI/CD stands for Continuous Integration / Continuous Delivery (or Deployment). It's an automated pipeline that, whenever you change your code, runs your tests and (if they pass) deploys the new version โ without error-prone manual steps.
| Term | What it means |
|---|---|
| Continuous Integration (CI) | Every code change is automatically built and tested, so problems are caught early. |
| Continuous Delivery/Deployment (CD) | Passing changes are automatically packaged and released to staging and/or production. |
Testing a GenAI app โ it's not just normal tests
Regular software tests check exact outputs. But LLMs are non-deterministic, so you also need the AI-specific testing from earlier sessions:
- Normal unit/integration tests โ your non-AI code (API routes, parsing, database calls).
- Prompt / eval tests โ run each prompt across a set of example inputs and check the outputs are good. This is the prompt versioning & testing idea from Session 2 (prompt engineering), ideally at low temperature so results are repeatable.
- LLM-as-judge tests โ use another model to grade your app's outputs against criteria (helpfulness, correctness, tone). This is the LLM-judge approach from Session 5, automated into your pipeline so quality is checked on every change.
Version your prompts, your models (which model + version you call), and your app together. If quality drops after a change, you can see exactly what changed and roll back to the known-good combination.
Staged rollouts
Don't push a new version to everyone at once. Roll out gradually so a bad change hurts few people:
Monitoring & observability
Once live, you must see what's happening. This is exactly the tracing & observability from Session 8, now in production. Track:
- Logs โ what happened, including errors.
- Metrics โ request rate, latency, error rate, token usage & cost, cache hit rate.
- Traces โ follow a single request through every step (prompt build โ tool calls โ model call โ response), invaluable for debugging multi-step agents.
You tweak your system prompt and open a pull request. CI automatically runs your eval suite and an LLM-judge over 50 example questions. The judge flags that answer quality dropped from 92% to 78% โ the new prompt made replies vaguer. CI blocks the merge. You fix the prompt, tests pass, it deploys to a 5% canary, dashboards (Session 8) look healthy, and it rolls out to everyone. A bad change never reached your users.
7 Capstone project & viva prep
This is the school play at the end of term. You've practised all the songs (the sessions); now you get on stage and show what you built. The audience (your evaluators) doesn't just want a pretty show โ they want to see that you understand why you did each thing. So tell a clear story: here's the problem, here's what I built, here's why, and here's the proof it works.
Your capstone project is where you tie together everything from the course into one working GenAI application. The viva (oral examination / demo) is where you present and defend it. Here's how to do both well.
How to present a GenAI demo
- Lead with the problem. Start with who has the problem and why it matters โ not with your tech stack.
- Show it working, live, early. A real run beats slides. Have a known-good example ready.
- Tell the architecture story. Walk the request โ response flow (Session 1): prompt โ retrieval/RAG โ model โ tools/agent โ response. Show where caching, rate limiting, and observability fit.
- Explain your choices. Why this model? Why RAG vs fine-tuning? Why this temperature? Evaluators love reasoned trade-offs.
- Show the numbers. Token cost per request, latency, eval scores, cache hit rate โ this proves production thinking.
- Be honest about limits. Name what doesn't work yet and what you'd do next. It reads as maturity, not weakness.
What evaluators look for
| They check | How to show it |
|---|---|
| Do you understand the fundamentals? | Explain tokens, context window, embeddings, attention in your own words (Session 1). |
| Did you engineer the prompts? | Show versioned prompts and how you tested them (Session 2). |
| Did you evaluate quality? | Show eval results / LLM-judge scores (Session 5). |
| Is it observable? | Show traces/logs/metrics (Session 8). |
| Is it production-minded? | Talk deployment, scaling, caching, rate limiting, cost (this session). |
| Did you build real agent behaviour? | Show tool calls / multi-step reasoning from your agent sessions. |
- Demoing only on the laptop with no thought to deployment, cost, or failure handling.
- Hard-coded secrets in the repo (instant red flag).
- No evaluation โ "it looked good when I tried it" isn't evidence.
- Over-promising: claiming it "understands" rather than "predicts" (Session 1).
- Live demo with no backup โ always have a recorded run or screenshots in case of network issues.
- Ignoring cost: not knowing what one request costs in tokens.
- 0:00 โ "Support teams waste hours answering the same questions. My app answers them instantly from the company's own docs."
- 0:45 โ Live: ask a real question, get a grounded answer with cited sources (RAG).
- 1:30 โ Architecture: container on Fargate โ API Gateway โ Claude API; Redis semantic cache; SQS for long agent tasks.
- 2:30 โ Metrics: "92% eval score via LLM-judge, ~$0.01/request, 70% cache hit rate, p95 latency 1.2s."
- 3:30 โ Show a trace of one request and the CI pipeline running eval tests.
- 4:15 โ Honest limits + next steps; invite questions.
โ Course wrap-up โ you did it! ๐
That's the whole GenAI course. Take a second to appreciate how far you've come โ you started not knowing what a token was, and you can now design, build, evaluate, deploy, and defend a real GenAI product.
You began with the foundations (Session 1): GPT as a generative pre-trained transformer, tokens and their economics, embeddings, attention, and the request โ response flow. You learned to steer models with prompt engineering (Session 2), to compare prompting vs fine-tuning vs RAG, and to evaluate quality with metrics and LLM-judges (Session 5). You made systems you could actually see inside with tracing & observability (Session 8), then gave models hands and a brain with tools and agents, and let agents talk to systems and each other with MCP & agent communication (Session 12). Today you closed the loop: turning all of that into a deployed, scalable, cached, rate-limited, monitored product โ and learned how to present it. You now hold the entire arc, from a single predicted token to a production GenAI application serving real users. Congratulations โ go build something. ๐
Final self-check
What's the core difference between a demo and production?
A demo only has to work once, for you, on your laptop. Production must serve many real users reliably 24/7, with security, observability, graceful error handling, and cost control.
Why must your servers be stateless to scale horizontally?
Because a load balancer may send a user's next request to a different copy of the server. If state lived in one server's memory, that copy wouldn't have it. Keep servers stateless and store state externally (DB, Redis, vector DB) โ the memory-sessions pattern.
How does semantic caching differ from plain response caching?
Response caching matches the exact input text. Semantic caching uses embeddings to match by meaning, so differently-worded questions ("refund policy?" vs "how do I get my money back?") can hit the same cached answer.
You get a 429 Too Many Requests from the model provider. What should your app do?
Retry with exponential backoff (wait 1s, 2s, 4sโฆ) plus jitter, rather than retrying instantly or crashing. Use queues/token buckets to stay under the provider's RPM/TPM limits in the first place.
Which earlier-session tests belong in your CI/CD pipeline for a GenAI app?
Beyond normal unit tests: prompt/eval tests (Session 2) run across example inputs at low temperature, and LLM-as-judge tests (Session 5) to grade output quality automatically โ so a quality regression blocks the merge.
Name two things evaluators look for in a capstone demo that beginners often skip.
Any two of: evidence of evaluation (eval/LLM-judge scores, not "it looked good"); per-request token cost awareness; deployment/scaling/caching thinking; observability (traces/ metrics); and no hard-coded secrets in the repo.
๐ References & Further Reading
Class material
- ๐ Original course notes / handout (source sheet) โ open the shared GenAI class material for this session.
- Class handout: "Deployment, Scaling & Final Demos".
Papers, docs & deep dives
- AWS Well-Architected Framework โ the reliability, scaling, and cost pillars behind Topics 2โ3.
- OpenAI production best practices โ moving from notebook to production reliably.
- Anthropic prompt caching โ prompt/KV caching to cut cost and latency (Topic 4).
- Anthropic rate limits (RPM/TPM) โ provider limits and 429 handling from Topic 5.
- Amazon SQS developer guide โ async queues + background workers for long agent runs.