Skip to main content

Building the AI travel agent at Kitt

An AI agent that booked flights, hotels and cars on production infrastructure, shipped in beta by a four-person team.

Context

Kitt was a corporate travel platform for companies in the GCC. Flights, hotels and cars on our own inventory, a policy engine underneath, and a ledger that handed finance their books at the moment of booking. The self-serve flows were the part customers used every day, and they worked.

Problem and opportunity

They all opened the same way, though: translate what you want into a form. Origin, destination, dates, cabin, traveller, then read sixty results and sort them in your head. A form can only answer the question it was built to ask. "Cheapest flight that gets me in before a nine o'clock meeting" is three filters and a sort. "Where is my team travelling next week" has no form at all, even though the platform already knew the answer.

So the opportunity was not a chatbot bolted onto a travel site. It was to let someone type an ordinary sentence and get a real, bookable answer back, without losing the things a sentence is bad at: comparing sixty fares, seeing where a hotel actually sits, reading an itinerary end to end.

Scope

Four of us, and a booking stack that already worked. So the agent had to sit on top of production rather than replace any of it: the same search, the same policy engine, the same payment on file. It shipped in beta across flights, hotels, cars and trips, and everything else in Kitt stayed exactly where it was.

That constraint set the design question, and every section below is a version of it. What belongs in the conversation, and what needs a surface of its own.

Solution

A two-panel shell with a stack of sheets over it.

The conversation holds the answer and a short strip of recommendations, and nothing heavier. Anything that needs room opens as a sheet on top of the chat: the full result set, a hotel's rooms, a trip itinerary. Sheets stack, and closing one puts you back exactly where you were. That is the whole navigation model, and following a result never costs you the thread you were on, because there is nowhere else to go.

The composer

The smallest surface in the product and the most used one. Typing @ opens a people picker that sets who the booking is for, and a message will not send without one. Corporate travel is nearly always booked by someone other than the traveller, so naming them at the point of writing took a whole class of ambiguity out of every turn that followed. It also gave the composer somewhere to put structure when a request needs it: a booking template with fillable chips, and voice, for when typing an itinerary is the slow way to say it.

Flights, hotels, cars - type @ for travellers

Adarsh Panikkar

Default composer

New booking

Show me flights fromOrigintoDestinationforDatesinEconomy
Neha Sharma

Add-a-flight template with fillable chips

Recording…

Omar Al Hashimi

Voice recording — waveform loops while listening

A thirty-second wait

A flight search fans out to three suppliers at once and can take thirty seconds to come back. A spinner stops being believable at about three. So the wait is a sequence rather than a state. It says what it is doing in the order it does it, then streams airline logos past while results are curated, and resolves on a count. The thirty seconds are unchanged. They read as work rather than as a hang.

Thinking...
Searching flights...
Loaded your profile
Reviewed travel history
Checked travel policy
Searching flights...
Curating results...
EK
EY
UX
Found 68 flights.

Every state a search moves through, from thinking to results

Flights in the conversation

Recommended flights come back as cards, cheapest direct first, with refinement chips underneath so the next turn is a tap instead of a sentence. Three or four cards, never sixty. The conversation is for the answer.

The all-flights browser

"Show all flights" opens the complete result set, not a longer version of the agent's picks. That one was deliberate. An agent that only ever shows you what it chose is asking for trust it has not earned yet, and the sheet carries the same filters and sort as the rest of Kitt, so the way out of the conversation is the product people already knew.

Hotels on a map

Hotels broke the card pattern. The first question anyone asks about a hotel is where it is, and no amount of prose answers that. So a hotel search resolves onto a map with the list beside it, hovering either one lighting up the other.

Trips in a split view

Trips get more room than anything else. The card in the conversation stays compact, and View splits the layout rather than opening a modal, so the itinerary and the conversation are both live at once. It is the one place where the answer is something you read instead of something you buy, and reading wants a column of its own.