We built an AI-powered travel interface from scratch. Not a chatbot. Not a widget. Not a GPT-wrapper with a smiley face bolted onto a website. A full conversational interface with its own vector database, its own retrieval pipeline, its own memory system, and real-time streaming over WebSockets. Not to replace anything. To test whether conversational AI is actually ready to be a meaningful channel for travel. Here's what we found, what it took, and why we think most of what's out there right now is noise.
Lang artikel. Pak een kop koffie. Dit artikel gaat over architectuurdiagrammen, vectordatabases, geheugensystemen en waarom de meeste AI-chatbots in de reisbranche gewoon dure zoekbalken zijn.
Wat probeer je eigenlijk op te lossen?
That's the question I keep coming back to. Every time I see a company announce their new "AI-powered" feature. Every time someone asks me what we're doing with AI. Every time a vendor pitches yet another plugin.
It's the most important question in tech right now. And almost nobody is asking it.
Instead, the entire industry is working backwards: starting with "we need to do something with AI" and then looking for a place to put it. Companies hear "AI" and they hear "innovation" and they think they're falling behind if they don't do something. Anything. Now.
The pressure to "do something with AI" has become louder than the question of whether you should.
And so what happens? Companies reach for the most visible, most marketable application they can find: a chatbot. Slap it on the website. Give it a name and a friendly face. Connect it to a knowledge base. Done. "We do AI now."
In travel, this plays out in a specific way. Most travel companies don't have their own developers or IT infrastructure. So third-party vendors, companies that often have nothing to do with travel, throw tools against LLMs, wrap them in a widget, and sell them as plugins. "Install our AI assistant on your website! Smart. Conversational. 24/7 availability."
Here's the problem with these chatbots: the integration is always limited. Some connect to a product database. Some pull live availability. But as a plugin, there's a ceiling to how deep that integration goes. It's always working with whatever data it gets access to, in whatever format the API provides, with whatever context the vendor decided was enough. The result is an interaction that looks conversational on the surface but is fundamentally constrained in what it can actually do.
And because it's a plugin, it can never be fully seamless. It will always be a separate layer on top of your website, not part of it. A different design language. A different interaction model. A different experience. The customer feels it, even if they can't articulate it. It's the difference between a room that was designed as a whole and a room where someone placed a kiosk in the corner.
A smarter gatekeeper is still a gatekeeper. Your customers wanted a door.
We've been down this road before. The old-fashioned chatbot was already universally hated because it stood between the customer and an actual answer. Making it "smarter with AI" doesn't fix the fundamental problem. And these plugins often make things worse. They hallucinate. They give wrong information about trips. They make promises that travel companies can't keep. And when something goes wrong, the travel company has zero control, because the chatbot isn't theirs. It's the vendor's. Built on someone else's tech. With someone else's priorities.
The real question was never "how do we add AI to our website?" The real question is: where does AI genuinely create value that wasn't possible before? That's what we set out to test.
Waarom ik heb gebouwd wat ik heb gebouwd
Ik heb dit niet gebouwd omdat ik denk dat websites dood zijn of omdat ik wilde zeggen dat we "iets met AI doen". Ik heb al eerder geschrevenhoezeer ik die mentaliteit verafschuw.
Ik heb dit gebouwd vanwege een vraag die ik nu al meer dan een jaar aan het testen ben: is conversationele AI klaar om een echt kanaal te worden?
In mijnWeb 4.0-bericht schreef ik over het autonome web, een wereld waarin AI-assistenten niet alleen informatie ophalen, maar ook als onafhankelijke digitale consumenten optreden namens hun mensen. Waar Lisa tegen haar AI-assistent zegt: "Zoek een reis van twee weken naar Thailand voor me", en de AI via MCP-interfaces contact opneemt met reisorganisaties, onderhandelt, verfijnt en opties presenteert. Zonder dat Lisa ooit een browser hoeft te openen.
That might sound like removing humans from the equation. It's not. The technology enables full autonomy. But that doesn't mean we want to remove the human. It means the customer gets to choose. And our default will always be: lead to a person. More on that later.
Die toekomst komt eraan. Misschien niet morgen, maar hij komt eraan. En als je een reisorganisatie bent die technologie serieus neemt, kun je je daar maar beter op voorbereiden.
Dus bouwde ik Joy, een AI-reisassistent voor 333travel, niet als productlancering, niet als marketingtruc, maar als serieuze test. Kunnen we een conversatie-interface bouwen die echt waarde toevoegt? Die onze producten begrijpt? Die niet hallucineert? Die een echt nuttige ervaring creëert als extra kanaal naast onze website, telefoon en e-mail?
I didn't build this to replace our website. I built this to answer a question: can a conversation be a meaningful channel for personalized travel?
Wat dit eigenlijk is
Laat me even precies uitleggen wat Joy doet, want het een chatbot noemen zou hetzelfde zijn als een restaurant een automaat noemen.
Joy sits on top of our entire product database. Every roundtrip, every hotel, every excursion, every destination, all embedded as vectors in a dedicated database. When a customer says "I want something adventurous in Southeast Asia, around two weeks, not too touristy," Joy doesn't keyword-match against a PDF. She searches semantically across hundreds of products, reranks results based on actual relevance, and responds with options that genuinely fit the intent.
En ze herinnert zich het gesprek. Niet alleen het laatste bericht, maar de hele conversatie. Ze weet welke reizen er zijn besproken, wat er is bewaard en wat is afgewezen. Als je zegt: "die reis waar je het eerder over had, met die kookcursus", begrijpt ze wat je bedoelt, ook al noemde ze het oorspronkelijk een "culinaire ervaring".
Maar hier is het belangrijkste verschil met alle andere chatbots: ze weet niet alleen alles over reizen in het algemeen. Ze weet alles overonzereizen. Onze specifieke producten. Onze specifieke reisroutes. Onze bestemmingen die ons team persoonlijk heeft bezocht. Dat is niet iets wat je met een plug-in kunt toevoegen, hoe slim de LLM erachter ook is.
En op het moment dat u menselijk contact wilt? Joy geeft de volledige context door aan een reisspecialist die precies weet wat u heeft onderzocht. Geen "kunt u uw vraag nog eens beschrijven". Geen koele doorverbinding. Volledige continuïteit.
Joy doesn't replace conversations with specialists. She makes them better.
What she doesn't do (yet): she doesn't handle customer service queries, airline information, or post-booking support. This is purely a product discovery and inspiration channel. We'll get there. But we're not going to pretend she does things she doesn't. And honestly, when someone reaches out to customer service, there's already a problem. That's the worst possible moment to put a machine between you and your customer. That's when you show up. Personally.
Why you can't get here with a plugin
You cannot achieve what we built by installing a plugin. Not with ChatGPT. Not with any off-the-shelf "AI widget." Not with any SaaS tool that promises to "make your website smarter in minutes."
En dat komt niet omdat de AI-modellen niet goed genoeg zijn. De modellen zijn prima. Het probleem zit hem in alles eromheen.
Het hele systeem is gebaseerd op gegevens die wij bezitten, structureren en beheren. Elke reis in onze database is door ons eigen team bezocht. Elke reisroute is door ons zelf samengesteld. Alle kennis over de bestemmingen is opgeslagen in onze eigen systemen. Dit zijn geen gegevens die we van derden hebben gekocht of uit een brochurecatalogus hebben gehaald.
Dat betekent dat we het kunnen inbedden. We kunnen het precies zo structureren als we nodig hebben voor semantisch zoeken. We kunnen het bijwerken zodra er iets verandert. We beheersen de volledige pijplijn, van ruwe productgegevens tot vectorinbedding, opvraging en respons.
You can't build a smart interface on top of dumb data.
If your product data lives in someone else's system, if you're reselling trips from a catalog, if your "database" is a collection of PDFs from tour operators, you're at the mercy of whatever structure someone else decided on. You can't build intelligence on top of data you don't understand or control.
And many companies aren't just starting from zero, they're starting from a deficit. Their existing systems are already inadequate. The booking engine is outdated. The product data is scattered across spreadsheets and PDFs. The customer journey has gaps everywhere. And instead of fixing those fundamentals, they layer an AI chatbot on top of it. As if intelligence on top of dysfunction creates something functional. It doesn't. It just makes the dysfunction harder to diagnose.
Je lost geen probleem op. Je versiert er een.
Building what we built required three things:
Wij zijn eigenaar van onze gegevens.Elke reis is van ons. Bezocht door ons team. Gestructureerd in onze database. Opgeslagen in onze vectoropslag. Je kunt geen semantische zoekfunctie bouwen op basis van gegevens die je niet zelf structureert.
Eigenaar zijn van onze systemen.Onze eigen backend, frontend, implementatiepijplijn. Toen we een dubbele reranker nodig hadden, hebben we die gebouwd. Toen we semantisch gespreksgeheugen nodig hadden, hebben we dat gebouwd. Toen we een MCP-server nodig hadden voor Web 4.0-gereedheid, hebben we die gebouwd. Geen goedkeuring van leveranciers nodig. Geen verzoeken om functies. Geen afhankelijkheden van roadmaps.
Wij beheersen de hele keten.Vanaf het moment dat een klant de chat opent tot het moment dat hij met een specialist praat, is elke stap van ons. De AI kent onze producten omdat wij die producten hebben gemaakt. De specialist kent het gesprek omdat ons systeem hem daarmee voedt.
De AI is de laatste 10% van het werk. De eerste 90% is het opbouwen van een bedrijf dat dit kan ondersteunen.
De architectuur
Since this is a tech blog and not a marketing page, let me show you what's actually running. This isn't a weekend project. This is production infrastructure.
Frontend (React + TypeScript + Zustand)
│
│ WebSocket (real-time, bidirectioneel)
│
FastAPI Backend
├── Query Orchestrator
│ ├── LLM Client (multi-provider met automatische fallback)
│ ├── Tool Registry (9 gespecialiseerde tools)
│ └── Observer System (asynchrone post-turn analyse)
├── RAG Pipeline
│ ├── Vector Database (3 contentcollecties)
│ ├── Embedding Engine
│ └── Reranker (dubbele provider, schakelbaar)
├── Conversation Memory (dubbele opslag)
│ ├── SQL (berichtgeschiedenis)
│ └── Vector DB (semantisch zoeken in eerdere berichten)
├── Beveiligingslaag
│ ├── Input Guard (bescherming tegen prompt-injectie)
│ ├── Output Guard (preventie van lekken van inloggegevens)
│ └── Rate Limiting + IP Management
├── MCP Server (Model Context Protocol)
│ └── Toegang van externe AI-assistent tot onze gegevens
└── Lead Management
├── Proposal Generation
└── Specialist Handoff
De frontend is een React-applicatie met realtime WebSocket-communicatie. Geen polling. Geen request-response-cycli waarbij je op een volledig antwoord moet wachten. Elk token wordt in realtime gestreamd, gebufferd met intervallen van 50 ms om flikkering van de gebruikersinterface te voorkomen. Productresultaten worden naar de interface gepusht op het moment dat ze worden gevonden, nog voordat de LLM begint met het samenstellen van zijn antwoord.
The backend is Python/FastAPI with a modular tool system. The LLM doesn't just generate text. It has access to 9 specialized tools and decides autonomously which ones to use. Need to search trips? It calls producten zoeken. Informatie over de bestemming nodig? Het belt zoek_kennis. Wilt u zich herinneren wat eerder is besproken? Het doorzoekt het gespreksgeheugen semantisch.
The LLM isn't generating answers. It's orchestrating tools and composing a response from real data.
De LLM-laag werkt met een multi-provider fallback-systeem. Als de primaire provider uitvalt, schakelt het systeem automatisch over naar de secundaire provider. Geen downtime. Geen fouten. De klant merkt er niets van.
And there's already an MCP server running, the protocol I described in my Web 4.0 post. External AI assistants can already query our travel data through it. It's early days, but the infrastructure for AI-to-business communication is live. When Lisa's AI assistant wants to search 333travel's products, the endpoint already exists.
De RAG-pijplijn: waar het echte werk gebeurt
RAG, Retrieval-Augmented Generation, is the backbone of the entire system. It's what separates "GPT with a knowledge base" from "an AI that actually knows your product." But RAG done poorly is almost worse than no RAG at all.
We onderhouden drie afzonderlijke vectorcollecties:
| Collectie | Wat zit erin? | Wat het doet |
|---|---|---|
| Reisproducten | Retourreizen, hotels, rondleidingen, cruises | Aandrijving van de belangrijkste productzoekfunctie |
| Reisgegevens | Dagprogramma's, excursies, inclusies | Beantwoordt specifieke vragen over de reisroute |
| Reiskennis | Blogs, reisgidsen, reisinformatie | Biedt context en inspiratie |
Wanneer een klant iets vraagt, wordt de zoekopdracht ingebed en tegen de relevante verzameling afgezet. Maar hier komt het cruciale deel:we nemen niet zomaar de beste vectorovereenkomsten en laten het daarbij.
Vectorovereenkomst alleen is middelmatig. Het brengt je in de juiste richting, maar levert niet de beste resultaten op. Daarom voeren we een brede zoekopdracht uit, die aanzienlijk meer resultaten oplevert dan we nodig hebben, en voeren we deze vervolgens door een reranker. De reranker evalueert elk query-documentpaar op daadwerkelijke semantische relevantie, niet alleen op basis van de nabijheid van de embedding.
Het verschil is enorm. Het is het verschil tussen "hier zijn reizen die enkele van de woorden bevatten die u hebt gebruikt" en "hier zijn reizen die daadwerkelijk overeenkomen met wat u zoekt".
But it goes further than that. Before the query even hits the vector database, it runs through a fuzzy normalization layer. Every country and location name in our system is cached at startup. When a customer types "Bali" we know they mean Indonesia. When they write "Tailand" we know what they meant. Aliases, spelling variations, language differences, all resolved before the search begins. Without this, you get empty results for queries that should have matched. And on top of that, the search combines semantic similarity with metadata filters. Country, duration, product type, these aren't just fields in a database. They're active filters that work together with the vector search to narrow results before the reranker even starts.
After reranking, the results split into two paths. The full set of reranked results pushes to the frontend immediately, but blurred. The customer sees product cards appearing while Joy is still thinking. It signals progress without overwhelming. Meanwhile, the LLM gets a stripped-down version of those results, only the fields it needs to reason about. This projection saves roughly a third of the tokens, which means faster responses and lower costs. Then Joy composes her answer and discusses the 4 or 5 trips that genuinely fit the question. Only those discussed products become visible to the customer. The rest disappear. What the customer sees is a curated, considered selection. Not a list of 10 search results. A recommendation of 5 that actually make sense.
Breed zoeken → Opnieuw rangschikken → Project. Drie stappen die het verschil maken tussen een zoekresultaat en een aanbeveling.
Het geheugenprobleem
Dit is het onderdeel dat de meeste herhalingen nodig had om goed te krijgen.
Conversational AI heeft een smerig geheim: het onthoudt niets. Elke LLM-oproep is stateless. Het model heeft geen idee wat er eerder is gezegd, tenzij je het expliciet de context voorschotelt. En er is een limiet aan hoeveel context je kunt voorschotelen.
Voor een eenvoudige Q&A-chatbot maakt dit niet uit. Iemand stelt een vraag, krijgt een antwoord, klaar.
Maar voor een reisassistent? Geheugen is alles.
A customer says: "I want to go to Thailand for two weeks." Three turns later: "Actually, make it three weeks. And I want to include Laos." Five turns later: "What was that trip you showed me earlier with the cooking class?"
Zonder geheugen is elke beurt een schone lei. Het systeem weet niets over Thailand. Het weet niets over de verandering van drie weken. Het weet niets over Laos. Het weet zeker niets over de kooklesreis van zeven berichten geleden.
Without memory, every message is a first date. Your AI has no idea what happened before.
Dit werd niet in één poging opgelost. Ook niet in twee pogingen.
The first version only had SQL-based memory. We stored every message and fed the recent history back to the LLM. Simple, but brittle. You could reference things that were said recently, but the moment a customer said "that trip you mentioned" without being specific, the system was lost. It could recall what was said. It couldn't understand what was meant.
So we added a second layer: semantic memory. Every message gets embedded as a vector in the same database we use for product search. When the customer says "that trip with the cooking class," the system runs a semantic search across all past messages in that session. It finds the relevant message even if the exact words don't match. Maybe Joy called it a "culinary experience" three turns ago. Vector search doesn't care about word matching. It matches meaning.
AI-geheugen is niet één probleem, maar twee: herinneren wat er gezegd is en begrijpen wat er bedoeld werd. We hadden twee verschillende systemen nodig om deze problemen op te lossen.
UI/UX: het onzichtbare werk
Hier sterven de meeste AI-projecten, zonder dat ze het zelf doorhebben.
Je kunt beschikken over de meest geavanceerde RAG-pijplijn ter wereld. De slimste zoekfunctie. De beste herrangschikking. Maar als de interface onhandig, traag of verwarrend aanvoelt, doet dat er allemaal niet toe. De klant geeft niets om je architectuur. Ze geven om hoe het aanvoelt.
If your AI is smart but your interface is slow, you built a genius locked in a closet.
We zijn hier diep op ingegaan. En 'diep' betekent tientallen herhalingen van dingen die de meeste mensen als details zouden beschouwen. Maar details zijn het product.
Streaming that feels natural. LLM responses stream token by token over WebSocket. But raw streaming creates flickering text that's jittery and unpleasant. We buffer at 50ms intervals. Fast enough to feel real-time, slow enough to prevent constant re-renders. The difference is subtle but massive in terms of perceived quality.
Joy shows her work. While Joy is thinking, the interface doesn't just show a spinner. It shows what she's doing. Searching products. Looking up destination information. Checking conversation history. Every tool call is visible to the customer as it happens. Product cards push in blurred while she's still composing. Once the answer is ready, only the trips Joy actually discusses become visible. The rest fade away. The customer never stares at a blank screen wondering if something is broken. They see the process. It builds trust in a way that a loading animation never will.
De pagina wordt direct geladen.Het hele gesprek blijft bewaard in de lokale opslag. Wanneer u de pagina ververst of later terugkomt, staat uw gesprek er al. Direct. Geen laadspinner. Geen bericht 'opnieuw verbinden...'. De synchronisatie met de server gebeurt stil op de achtergrond.
Markdown-normalisatie.LLMs hebben een mening over opmaak. Claude houdt van vetgedrukte kopteksten met emoji's. GPT houdt van genummerde lijsten. We normaliseren alles aan de serverzijde. Vetgedrukte tekst met emoji's wordt omgezet in strakke kopteksten. Horizontale regels worden verwijderd. Het lettertype wordt aangepast. Het resultaat ziet eruit alsof het is ontworpen, niet gegenereerd. De klant mag nooit het gevoel hebben dat hij AI-output leest.
Sessiecontinuïteit.Uw sessie blijft bestaan, ook als u de pagina ververst, de browser sluit en opnieuw verbinding maakt. Kom een dag later terug en uw gesprek is er nog steeds. Uw opgeslagen reizen zijn er nog steeds. En als een sessie toch verloopt, krijgt u een duidelijke uitleg over wat er is gebeurd, geen kapotte pagina of cryptische foutmelding.
De leadflow.Wanneer een klant klaar is om met een specialist te praten, genereert het systeem een reisvoorstel dat vooraf is ingevuld met wat het al weet uit het gesprek. Besproken bestemmingen, reisvoorkeuren, bekeken producten. Minimale wrijving. Maximale context. De menselijke specialist gaat precies verder waar Joy was gebleven. Dat is geen overdracht. Dat is juist het hele punt. Joy bestaat om dat menselijke gesprek te verbeteren, niet om het te vermijden.
Every one of these details took multiple iterations. Every one of them is invisible to the customer. That's the point.
Beveiliging: een eerlijke opmerking
Ik zal dit gedeelte kort en eerlijk houden. Beveiliging is niet iets wat je afmaakt. Het is iets waar je constant aan werkt.
We have a dual guard system. An input guard before the LLM that catches prompt injection attempts, and an output guard after the LLM that catches credential leaks or system prompt fragments in responses. Both are deterministic and add virtually zero latency.
De systeemprompt is beveiligd met anti-jailbreak-instructies. Er is snelheidsbeperking, botbescherming en IP-beheer voor aanhoudend misbruik.
Is het kogelvrij? Nee. Niets is dat. Maar het is gebouwd met het oog op diepgaande verdediging, en we passen het aan naarmate er nieuwe aanvalspatronen ontstaan. Als je een AI-interface bouwt die met het publiek communiceert en je hebt niet nagedacht over input/output-beveiligingen, dan wacht je op problemen.
Het grotere plaatje: een extra kanaal, geen vervanging
Laat me heel duidelijk zijn over wat Joy wel en niet is.
Joy is not a replacement for our website. She's not a replacement for phone or email. She's an additional channel. A new way for customers to explore our products, alongside everything that already exists. She will never be a replacement for human contact. We value that the most in our company. Joy is a means to human contact. She helps customers figure out what they want, so that when they talk to one of our specialists, that conversation starts at a completely different level.
Joy isn't the destination. She's the road that leads to a real conversation with a real person.
The MCP server is already live, which means AI assistants can already query our data programmatically. When the Web 4.0 vision materializes, when Lisa's AI assistant autonomously searches for her perfect Thailand trip, the endpoint already exists. Not because we scrambled to build it. Because we've been exploring this infrastructure for over a year.
Joy isn't the end product. She's the beginning of a channel strategy that includes both human-to-AI and AI-to-AI interactions. And she's a test, a very serious, very thorough test, of whether conversational AI can actually add value for travel customers today.
De resultaten tot nu toe zijn beter dan ik had verwacht. Niet perfect, niets is perfect, maar echt indrukwekkend door hoe natuurlijk de gesprekken aanvoelen en hoe relevant de aanbevelingen zijn. Als je ziet hoe een klant een vaag idee beschrijft en Joy komt terug met reizen die echt passen, dan besef je dat dit geen gimmick is. Het werkt. En het werkt vanwege alles wat erachter zit.
So, what are we trying to solve again?
I opened with this question because it's the one that guided everything we built. Not "how do we use AI?" Not "what's our competitors doing?" Not "how do we look innovative?"
Just: what problem are we solving, and does this solution actually work?
We built Joy to find out. With real architecture. Real data. Real customers. And the willingness to say "it's not ready yet" if that turned out to be the case.
If you're thinking about adding AI to your product, start there. Not with the tool. Not with the vendor pitch. Not with the pressure to keep up.
Start with the question. Build from the answer.
Your customers deserve better than a FAQ with a face. They deserve a real conversation. And eventually, a real person. Make sure your AI leads them there, not away from it.