Design a hotel booking agent end to end
Monday, 9:00: Karim's brief fits on one slide. StayHub's booking agent goes live on WhatsApp in 6 weeks, in 6 languages, for 40,000 conversations a day, at no more than €0.15 each, with zero double bookings and no card number ever in a prompt.
Put it all together: design a real hotel booking agent where the AI is one part among many, and most of the safety lives around it.
- Problem
A hotel group wants guests to search, book, change and cancel stays by chatting, on the website and WhatsApp, in six languages, 24/7, without double bookings, leaked data or surprise bills.
- Why the simple system fails
Wiring a model to the booking API with a long prompt works in a demo. In production it double-books on retries, quotes prices it invented, stores card numbers in chat logs, can't be debugged when a guest complains, and costs far more per booking than anyone estimated.
- Engineering concept
Design the agent like any system: requirements and non-goals first, narrow intent-level tools, policies and approvals in code, scoped memory, deterministic money and inventory handling, and evals, traces and cost budgets from day one.
- Technology
AI gateway with model routing, an agent runtime with budgets, typed tools over PMS and payment APIs, inventory holds and idempotency keys, policy RAG with citations, a policy engine, session and profile memory with consent, OpenTelemetry-style traces, and offline task suites with a simulated PMS.
- Trade-offs
Autonomy vs control, conversion vs safety, latency vs model quality, cost per conversation vs helpfulness, and personalisation vs privacy. Each is a dial you set from requirements, not a default.
The problem
2 min- Karim · CTOBooking agent on web and WhatsApp, 6 languages, live in 6 weeks. 40,000 conversations a day.
- Karim · CTOHard limits: ≤ €0.15 per conversation, zero double bookings, no card number ever in a prompt, log or trace.
- Lina · Senior engineer, your mentorYou lead this one, end to end. I'll only check your answers.
The big idea
4 minThe model decides what to ask for; deterministic systems decide prices, availability, permissions and money.
Every episode of this module added a piece: cost control, grounded answers, the agent loop, gates. Here they meet, and every hard limit gets an owner in code.
How it works
5 min- 1Requirements and non-goals
In: search, policy answers with citations, book with confirmation, change and cancel in policy, hand off the rest. A non-goal is something v1 deliberately won't do: group bookings, loyalty transfers, rate negotiation.
- 2Estimates
8 turns × (5,400 in, 250 out) on the large model, uncached: ≈ €0.26 per conversation. Prompt caching plus 70% of turns on a small model: ≈ €0.05; ≈ €0.09 with agent runs and RAG. Under €0.15.
- 3Narrow tools
search_stays,get_quote,hold_room(quote_id),confirm_booking(hold_id),start_checkout,policy_search,handoff. Intent-level, validated, permission-checked. Changes and cancellations stay workflows. - 4Quotes and holds
A quote is a price our pricing system calculated, with an id. A hold reserves the room for 15 minutes; one per conversation. The guest's YES is bound to the hold id; every write carries an .
- 5Card data out
Hosted checkout: the payment provider's page takes the card, so it never touches chat, model or logs. Card numbers typed in chat are redacted at the gateway, before anything else sees them.
- 6Observability
A trace per conversation, cost per conversation and per booking, and 250 scripted conversations in 6 languages run on every change.
Watch a decision being made
4 minYou turn the brief into a design; Lina checks each limit
Meet every hard limit: ≤ €0.15 per conversation, zero double bookings, no card numbers in AI systems.
- 1Price it before building
Uncached large model: ≈ €0.26, over budget. Caching the 3,000-token stable prefix + routing 70% of turns to the small model: ≈ €0.05.
pythonlarge = (2400*5 + 3000*0.5 + 250*25) / 1e6 # ≈ €0.0198 per turn, cached small = (2400*0.2 + 3000*0.02 + 250*0.8) / 1e6 # ≈ €0.0007 per turn, cached print(8 * (0.3*large + 0.7*small)) # ≈ €0.05 per conversationWhy: The budget is only reachable with caching and routing, so they're in the design from day one.
- 2Make money and inventory deterministic
Prices only from quotes;
confirm_bookingtakes a hold id the guest said YES to; expired hold → new quote shown first.Why: These rules in code make "zero double bookings" and "no invented prices" true whatever the model says.
- 3Keep card data out
Redact at the gateway; pay on a hosted page.
Why: The safest card number is one the agent never receives.
- 4Make it testable
Traces, cost dashboards and a 250-conversation task suite. Lina: "Checked. Every limit has an owner."
Why: When a guest complains, the trace shows what happened; when a prompt changes, the suite shows what broke.
In real life
3 minBooking.com AI Trip Planner: plan, then normal checkout
- Launched in 2023 to explore destinations and stays in conversation
- Booking completes in the regular flow
- Low risk; the conversation ends before the transaction
Fits a first version that proves demand.
OpenAI Operator: acts, hands back for sensitive steps
- Launched in 2025 as an agent that carries out tasks like bookings on websites
- Publicly described handing control back for logins and payment details
Acts for the user, keeps payment in the user's hands.
What goes wrong
3 minIt rounds, averages or invents, and prices change between turns.
Prices and rooms only from tools; bookings reference quote and hold ids.
A generic request tool can reach anything, including other guests' data.
Intent-level tools with schemas and ownership checks.
Complaints can't be debugged; prompt changes break things silently.
Trace every conversation; run scripted conversations on every change.
Trade-offs
3 min| Design | Choose when | You pay |
|---|---|---|
| Assistant, then normal checkout | Proving demand | Guests leave the chat to book |
| One transactional agent with gates | Clear booking tasks, strong backend guarantees | A tool list that grows with features |
| Multi-agent system | Many capabilities and teams, a measured problem | More calls and cost; context lost in handoffs |
- Maya · Product managerMake it multi-agent: search, booking and support agents, plus an orchestrator. Easier to grow.
- Karim · CTOFour agents in six weeks? One agent with narrow tools, plus our existing workflows.
- Maya · Product managerOne agent with every tool will get confused as we add features.
Your job as tech lead
4 minClaude Code can write the code for this. You still own the decisions, and you have to know what good looks like to accept or reject its work.
- The scope and non-goals: search, quote, hold, book on web and WhatsApp in 6 languages; no group bookings, no rate negotiation.
- Which system owns each fact: prices from
get_quote, rooms from the PMS, policies from cited RAG. The model computes none of them. - The budgets: ≤ €0.15 per conversation, first token < 2 s, zero double bookings.
Before writing code, write `docs/booking-agent.md` for StayHub's booking agent (web + WhatsApp, 40,000 conversations a day): the tools, which are reads and writes, and where the guest confirms. Rules: prices only from `get_quote`, 15-minute holds, one active hold per conversation, idempotency keys on every write, hosted checkout, card numbers redacted before the model, logs or traces. Include a cost estimate per conversation against €0.15 and the evals you'll run. Don't touch PMS or payment code.
- Follow one booking: search → quote → hold → guest confirms the exact total → checkout →
confirm_booking. Every write is after the confirmation. - The total in the confirmation comes from the quote id, never a number the model computed.
- A fake card number pasted in a test chat is absent from the prompt, logs and traces.
- The model adds up nightly rates, taxes and fees itself.
- Raw guest messages with card numbers kept in logs "for debugging".
- A generic "call any backend endpoint" tool.
Check yourself
2 minNext: the next part of the course opens up tool calling itself, starting with 41 guests who arrived holding a "Booked!" message for bookings that never existed.
Mark it complete to update your skill map.
Go deeper (optional)You don't need this to understand the lesson. Open it when a project needs the details: Technical deep dive, Interactive simulation, Visual diagram, Analogy, Why it matters, Check your understanding, Practice ladder, Explain it yourself.
Technical deep dive
You design the booking agent end to end
Turn Karim's one-slide brief into an architecture that meets every hard limit: ≤ €0.15 per conversation, zero double bookings, no card numbers in prompts, logs or traces, in 6 languages on web and WhatsApp.
- 1Write requirements and non-goals
In: search, policy Q&A with citations, book with confirmation, change and cancel in policy, handoff. Out (v1): group bookings, loyalty transfers, rate negotiation, other companies' inventory.
Why: Requirements decide every later choice; non-goals remove whole failure classes before any code exists.
- 2Estimate cost and latency
8 turns × (5,400 in / 250 out). Large, uncached: ≈ €0.26 per conversation, over budget. Prompt caching on the stable 3,000-token prefix + 70% of turns on the small model: ≈ €0.05; with agent runs, RAG and reranking ≈ €0.09. Stream replies for a first token < 2 s.
pythonlarge = (2400*5 + 3000*0.5 + 250*25) / 1e6 # ≈ €0.0198 per turn, cached small = (2400*0.2 + 3000*0.02 + 250*0.8) / 1e6 # ≈ €0.0007 per turn, cached turn = 0.3*large + 0.7*small # ≈ €0.0065 print(8 * turn) # ≈ €0.05 per conversationWhy: Estimating before building shows the budget is only reachable with caching and routing, so they're in the design from day one.
- 3Design narrow tools
search_stays,get_quote,hold_room(quote_id),confirm_booking(hold_id),start_checkout(booking_id),policy_search,handoff. Changes and cancellations run as workflows. Every argument is schema-validated; ownership is checked by the policy engine.Why: Intent-level tools expose only what the agent needs. A generic "call any API" tool would let it reach other guests' data.
- 4Make money and inventory deterministic
Prices only from quotes. Holds last 15 minutes, one active hold per conversation.
confirm_bookingneeds the guest's YES bound to the hold id. Every write has an idempotency key. Expired hold → new quote shown before booking.Why: These rules, in code, are what make "zero double bookings" and "no invented prices" true, regardless of what the model says.
- 5Protect data and gate risk
Redact card numbers and other sensitive data at the gateway, before the model, logs and traces. Payment only on a hosted checkout page. Policy answers grounded with citations; out-of-policy refunds go to staff.
Why: The safest card number is one the agent never receives. Redaction at the edge protects every system downstream.
- 6Make it observable and testable
A trace per conversation (model calls, tools, costs, stop reasons); dashboards for cost per conversation and per booking; a task suite of 250 scripted conversations against a simulated booking system in 6 languages, run on every change. Lina: "Checked. Every limit has an owner."
Why: When a guest complains, the trace shows what happened; when you change a prompt or model, the suite shows what broke.
| Hard limit | Owned by | How |
|---|---|---|
| ≤ €0.15 per conversation | AI gateway + runtime | Model routing, prompt caching, step budgets, cost metered per conversation |
| Zero double bookings | Booking tools | One active hold, idempotency keys, confirmation bound to hold id |
| No invented prices | Pricing system | Prices only from get_quote; bookings reference quotes |
| No card numbers in AI systems | Gateway + hosted checkout | Redaction before model, logs, traces; payment on a hosted page |
| Correct policy answers | Policy RAG | Filtered retrieval, citations, verification, refusal + handoff |
| Debuggable | Observability | Trace per conversation, task suite, sampled reviews |
Interactive simulation
Visual diagram
A production hotel booking agent. Click components for details, or follow one booking end to end.
Analogy
- A new front-desk employeeLooks things up in the reservation systemThe booking agentRead-only tools
- A new front-desk employeeQuotes prices from the system, not memoryThe booking agentPrices from the pricing service
- A new front-desk employeeHolds a room while the guest decidesThe booking agentA short inventory hold
- A new front-desk employeeGuest signs before any chargeThe booking agentExplicit guest approval
- A new front-desk employeeCard goes through the terminal onlyThe booking agentPayment data never reaches the AI
- A new front-desk employeeOdd cases go to the duty managerThe booking agentEscalation to staff
Where the analogy breaks: a new employee learns from each shift. The agent doesn't, so your team reviews traces and tests regularly and improves it themselves.
Why it matters
A booking agent is a great capstone because it has every hard part at once.
There's real money, so approvals and safe retries matter. There's live data, prices and availability that change by the minute, so the model must never answer from memory. There's fine print, so policy answers must be grounded and cited.
There's personal data, so memory needs limits and consent under rules like . And there are messy humans who change their minds mid-chat, so the agent must re-plan without losing the thread.
If you can design this one well, you can design most business agents: support, purchasing, scheduling, insurance claims.
Check your understanding
Practice ladder
Trace a booking conversation
Scenario Warm-upA guest on WhatsApp, in Portuguese: "Two nights in Porto, 14–16 Nov, two adults, under €180, near the river."
Trace the conversation through the design: which component handles each step, which tools are called, and what the guest sees, up to a paid booking. Then trace what happens if she replies "SIM" (yes) 20 minutes after the hold was created.
Finish the tool table
Design PracticeLina's tool table has gaps. For each tool, fill in its type (read / write), its key argument(s), and its guard (what code checks before running it).
| Tool | Type | Key arguments | Guard |
|---|---|---|---|
| search_stays | read | city, dates, guests, max_price | ___ |
| get_quote | ___ | hotel_id, room_type, dates, guests | — |
| hold_room | write | ___ | ___ |
| confirm_booking | write | hold_id | ___ |
| cancel_booking | write | booking_id | ___ |
Fit the Japanese market in budget
Estimation PracticeStayHub launches the agent in Japanese. From an earlier episode, Japanese uses about 1.8× the tokens of English for the same conversation. Japanese conversations also average 10 turns instead of 8.
Using the English per-turn costs (cached large ≈ €0.0198, cached small ≈ €0.0007, 70% small routing ≈ €0.0065 per turn) and treating token counts as scaling cost linearly:
Estimate the cost per Japanese conversation (model calls only), compare it with the €0.15 budget (about €0.04 of the €0.09 total is agent runs, RAG and reranking, assume it scales the same way), and propose what to do if it's over.
Go or no-go for WhatsApp
Decision StretchIt's week 6. Results from the WhatsApp pilot (2,000 conversations a day):
- Cost per conversation: €0.11 (budget €0.15).
- Double bookings: 0 (one hold rule + idempotency held up under 3 provider timeouts).
- Card numbers reaching the model since redaction shipped in week 2: 0; 37 from week 1 purged.
- Task suite: 96% pass in English, Portuguese, Spanish, French, German; 88% in Japanese (mostly wrong date formats).
- Handoff rate: 9%; staff answer within 20 minutes in the daytime, 2 hours at night.
Write a short launch decision: "We chose X over Y because…, accepting the risk of…", including what you'd launch, where, and the conditions for the rest.
Explain it yourself
Walk an interviewer through your design for a hotel booking agent in five minutes: what's in it, where the safety lives, and how you'd know it works.