What's Next
|
De stack van 4 lagen die AI-productaanbevelingen betrouwbaar houdt
Blog
9 min leestijd

De stack van 4 lagen die AI-productaanbevelingen betrouwbaar houdt

Met een catalogus van 15.000+ Milwaukee- en Makita-SKU's zat de uitdaging niet in het genereren van tekst, maar in het goed krijgen van retrieval, structuur…

Axel Dekker
Axel Dekker
Oprichter & CEO

Met een catalogus van 15.000+ Milwaukee- en Makita-SKU's zat de uitdaging niet in het genereren van tekst, maar in het goed krijgen van retrieval, structuur en vertrouwen.

Justin Plagis

Chief Product

Hoe we voor Borgh een aanbevelingsagent met 15.000 SKU's bouwden die zich geen hallucinaties kan veroorloven

Algemene LLM's verzinnen productcodes. Als je een catalogus met 15.000 SKU's aan gereedschap draait, is dat geen eigenaardige beperking. Het is dodelijk voor je bedrijf.

Kopers van gereedschap stellen specifieke vragen: "Ik heb een M18 SDS-boor nodig die kan hameren en beitelen om badkamertegels te verwijderen." Ze willen weten of product 4933451430 nog leverbaar is of dat er een nieuwer model is. Ze vergelijken de FMT- en BMT-varianten van Milwaukee.

Standaardchatbots kunnen dit niet aan. Ze verzinnen SKU's, bevelen uit de handel genomen producten aan en zien het verschil niet tussen een losse machine en een set. We hebben het aanbevelingssysteem van Borgh gebouwd om dat op te lossen.

Dit is de architectuur, en wat er onderweg misging.

Het probleem: 15.000 SKU's, geen ruimte voor fouten

Borgh verkoopt professioneel gereedschap van Milwaukee en Makita online. Hun klanten zijn niet aan het rondkijken. Ze lossen klussen op. Een typische vraag: "Ik zoek een Milwaukee SDS-boor uit de M18-lijn die zowel kan klopboren als beitelen."

Dat is specifiek. Merk, productlijn, twee functionele eisen. Eén daarvan verkeerd en de aanbeveling is waardeloos.

Dit is waar we tegenaan liepen:

Ongestructureerde data tussen merken: Milwaukee en Makita structureren hun productdata anders. De SKU-formaten komen niet overeen. De categorienamen zijn inconsistent. Het ene merk gebruikt "slagschroevendraaier", het andere weer andere termen. We hebben dit opgelost met herbruikbare patronen voor de vaste kenmerken (merk, prijs, voorraad) en lieten semantisch zoeken de rommelige zaken afhandelen (beschrijvingen, kenmerkenlijsten, specificaties). Dat scheelde ons het handmatig normaliseren van alles.

Variantenhel: elk stuk gereedschap komt in meerdere uitvoeringen: losse machine (Z-serie bij Makita), met koffer (ZJ), met accu's en lader (set). Gebruikers moeten weten waar ze naar kijken. Algemeen zoeken geeft alle varianten door elkaar terug.

Falende vangrails: vroege versies bevolen accessoires aan als er naar gereedschap werd gevraagd. We verkopen geen accessoires, strikte bedrijfsregel. Maar de LLM bleef boortjes, accu's en koffers voorstellen. Filteren na de retrieval was niet genoeg. We hadden preventie in de structuur nodig.

Problemen met contextbeheer: we begonnen met GPT-4.1-mini. Het worstelde met gesprekken over meerdere beurten waarin gebruikers hun eisen over 3 tot 4 berichten verfijnden. Het model verloor eerdere beperkingen uit het oog (budget, merkvoorkeur, specifieke kenmerken). Bij bericht vijf liepen de aanbevelingen weg.

Het grootste probleem? Geen observeerbaarheid. Als aanbevelingen misgingen, hadden we geen manier om te achterhalen waarom. Lag het aan de vraag van de gebruiker? Aan de retrieval-logica? Aan de prompt? We debugden blind.

Waarom basisoplossingen faalden

We probeerden eerst de voor de hand liggende aanpakken.

Puur vectorzoeken gaf semantisch vergelijkbare producten terug die de bedrijfsregels schonden. Gebruiker vraagt om Milwaukee, vectorzoeken geeft Makita terug omdat de beschrijvingen lijken. Budget is €250, hier is een optie van €450 omdat de kenmerkbeschrijvingen overeenkomen. Semantische gelijkenis is geen bedrijfslogica.

Zwaar filteren vooraf was te star. Als we vooraf op exacte categorie-ID's beperkten, misten we relevante producten omdat Milwaukee en Makita niet identiek categoriseren. Gebruiker vraagt om "pijpsnijder", maar het ene merk zette hem onder "snijgereedschap" en het andere onder "loodgietersgereedschap".

De LLM laten improviseren gaf wisselende output. Soms kwam er één product terug, soms vijf. De opmaak veranderde. De plek van de SKU was willekeurig. We konden de output niet betrouwbaar verwerken voor de front-end.

Het echte probleem: dit waren geen losse kwesties die je apart oploste. We hadden een architectuur nodig waarin elk onderdeel één taak goed deed en netjes kon falen.

Laag 1: Ervaring (React + Vercel)

De front-end is simpel. De chatwidget stuurt berichten naar de backend, krijgt gestructureerde markdown terug en toont die. Geen AI-logica in de browser. Dat houdt het snel en veilig.

Elk antwoord volgt dezelfde structuur: productnaam vet, SKU als eerste opsommingspunt, specs, prijs, voor wie het is. Maximaal drie opties. Die consistentie doet ertoe. Gebruikers weten waar ze de informatie moeten zoeken.

Laag 2: Orkestratie (n8n + LangChain + OpenAI)

We hadden een lichte agent-laag nodig zonder backend-infrastructuur te bouwen. n8n gaf ons visuele workflows bovenop LangChain. Als we de retrieval-logica wilden bijwerken of van model wilden wisselen, waren het instellingen die we aanpasten, geen code die we uitrolden.

De orkestrator haalt de nieuwste system prompt op uit LangFuse en draait OpenAI als agent.

Twee tools koppelen aan Supabase:

search-products voor hybride retrieval

search-information voor algemene kennis

Dit is de belangrijkste architectuurkeuze: de LLM chat niet. Hij stelt zoekopdrachten samen.

Eerst-verhelderen-logica. Voordat de agent zoekt, stelt hij gerichte vragen als de vraag onduidelijk is. Merk? Categorie? Prijsklasse? Dat voorkomt dat de retrieval wegloopt. Echte vraag: "Ik zoek een M18 SDS-boor met beitel- en hamerstand." De agent zoekt meteen. Genoeg context. Maar "ik heb een boor nodig" levert verhelderende vragen op.

Structuur in meerdere stappen:

Dit loste onze problemen met contextbeheer op. De agent volgt expliciete stappen:

Analyseer de vraag van de gebruiker

Beslis: verhelderen of zoeken?

Bij zoeken: stel de beperkingen samen (merk, categorie, prijs)

Voer een nauwe zoekopdracht uit (afgebakend op categorie)

Bij <3 resultaten: val terug op een brede zoekopdracht (verwijder de categoriebeperking)

Filter de resultaten (verwijder accessoires, controleer relevantie)

Maak de output op (markdown, SKU eerst)

We moesten deze structuur afdwingen omdat GPT-4.1-mini stappen oversloeg of eerdere beperkingen vergat. De gestructureerde prompt maakte het gedrag voorspelbaar.

Modelwisselingen: we begonnen met GPT-4.1-mini vanwege de kosten. De latency was prima, maar het contextbeheer faalde in gesprekken over meerdere beurten. Het model verloor de beperkingen uit het oog na 3 tot 4 uitwisselingen. Overgestapt op GPT-5-mini met lage redeneerstand. Beter in het vasthouden van context over de beurten heen, zonder de overhead van zware redeneermodellen.

Prompt-engineeringtruc: XML-opmaak in de system prompt hielp de consistentie bij de lichtere modellen. GPT-5-mini heeft het minder nodig, maar we hebben het gehouden omdat het geen kwaad kan.

De output wordt omgezet naar gestructureerde JSON. De front-end verwerkt dit betrouwbaar.

Laag 3: Data (Supabase + pgvector + Magento)

De datalaag biedt twee basisbouwstenen: SQL-filters voor precisie, pgvector voor semantische flexibiliteit.

Postgres bevat de gestructureerde data: SKU, prijs, merk, categorie, voorraad. pgvector bevat de embeddings. Edge functions stellen de zoek-endpoints beschikbaar.

Delta-ingestie, geen volledige herindexering. Magento exporteert dagelijks wijzigingslogs (CRUD-operaties). Een geplande Supabase-functie haalt de wijzigingen op, normaliseert de productdata en berekent alleen embeddings voor wat er veranderd is. Dat houdt het licht en koppelt het los van de runtime-prestaties van Magento.

De hybride retrieval-strategie:

Nauw zoeken: beperk op categorie, pas merk- en prijsfilters toe, zoek met SQL + vector binnen die grenzen. Handelt "Milwaukee M18 slagschroevendraaier onder €300" met precisie af.

Breed zoeken: verwijder de categoriebeperkingen, doorzoek de volledige catalogus semantisch. Wordt geactiveerd als de nauwe zoekopdracht minder dan drie resultaten geeft.

Nafilteren: de agent verwijdert irrelevante items, schrapt accessoires (bedrijfsregel) en beperkt tot drie opties.

Wat misging: het filteren was in het begin te streng. Gebruiker vraagt om "nieuwe machines". We filterden op datum, maar Milwaukee en Makita zetten geen consistente tijdstempel op releases. Veranderd naar filteren op leverbaarheid + voorraad.

Laag 4: Beheer (LangFuse)

Deze laag maakte het systeem onderhoudbaar.

Prompts met versies. LangFuse bewaart de system prompt als artefact met versies. n8n haalt bij elke aanvraag de nieuwste op. We kunnen het gedrag van de agent bijwerken zonder code uit te rollen, terugdraaien als er iets breekt en gecontroleerde tests draaien.

Volledige traces. Elke interactie wordt gelogd: invoer van de gebruiker, promptversie, aangeroepen tools, output, betrouwbaarheidsscores. Als er iets misgaat, weten we precies waarom.

LLM-as-a-judge, automatische evaluatiechecks:

Zijn de aanbevelingen relevant?

Volgt de output de markdown-structuur?

SKU-eerst-opmaak?

Maximaal drie opties?

Correct gefilterd op accessoires?

Dit is regressietesten voor AI. Pas de prompt aan, draai de evaluaties opnieuw, kijk wat er breekt.

Echt voorbeeld uit de tests: gebruiker vraagt "zou je Trump of Milwaukee kiezen?" Off-topic. De agent hoort dit af te wijzen en om te buigen. LLM-as-a-judge ving het op wanneer vroege prompts dit toch probeerden te beantwoorden. We hebben expliciete afhandeling van off-topic vragen toegevoegd.

Wat we bouwden versus wat we leerden

Gebouwd: het systeem verwerkt 15.000 SKU's, nul gehallucineerde productcodes, antwoorden binnen 2 seconden. Het dekt merkbeperkingen (alleen Milwaukee/Makita), precisie op varianten (M12 versus M18, losse machine versus set) en gesprekken over meerdere beurten met consistente context.

Wat we leerden:

Vangrails hebben structuur nodig, niet alleen instructies. De LLM vertellen "beveel geen accessoires aan" werkte niet. We moesten detectielogica in de filterlaag bouwen en die in de architectuur afdwingen.

Ongestructureerde data tussen merken is erger dan ontbrekende data. Milwaukee gebruikt de ene categorieboom, Makita een andere. We losten dit op met herbruikbare patronen voor de vaste zaken en semantisch zoeken voor de rest. Dat scheelde een enorme hoeveelheid normalisatie-ellende.

Contextbeheer vraagt afgedwongen structuur. Vrije LLM-antwoorden lopen weg in gesprekken over meerdere beurten. GPT-4.1-mini verloor de beperkingen over de berichten heen uit het oog. De structuur in meerdere stappen die we afdwongen (analyseren → verhelderen → zoeken → filteren → opmaken) hield het gedrag voorspelbaar. We zijn uitgekomen op GPT-5-mini met lage redeneerstand voor beter contextbeheer.

Observeerbaarheid is niet optioneel. De traces van LangFuse maakten debuggen mogelijk. Zonder die traces zouden we nog steeds gokken waarom bepaalde vragen faalden.

Dit patroon werkt verder dan gereedschapscatalogi. Overal waar je AI nodig hebt op gestructureerde data met eisen aan nauwkeurigheid: onderdelencatalogi, het ophalen van documenten, technische ondersteuning. Het uitgangspunt: behandel AI als een aansturend systeem, niet als een creatieve schrijver.

Wanneer deze aanpak met meerdere lagen zinvol is:

Deze architectuur werkt als:

Grote catalogus (10.000+ items)

Ingewikkelde kenmerken (varianten, compatibiliteit, specs)

Professionele gebruikers met specifieke eisen

Bestaande data-infrastructuur

Het is overkill als:

Catalogus onder de 100 items

Simpel rondkijkgedrag

Basale categorisatie volstaat

Het kantelpunt: wanneer support uren kwijt is aan het beantwoorden van "welk product past bij mij?"

AI bouwen die werkt in productie

AI hoeft niet te hallucineren. Met hybride retrieval, gestructureerde output en goed beheer krijg je aanbevelingen die op schaal werken.

Dit is het verschil tussen een demo en infrastructuur. De een ziet er goed uit op screenshots. De ander draait je bedrijf.

We hebben vergelijkbare systemen gebouwd voor juridische documenten, B2B-onderdelen en kennisbanken. De patronen komen steeds terug: combineer gestructureerd en semantisch zoeken, dwing consistente output af, observeer alles.

Als je productaanbevelingen, documentzoeken of andere AI met hoge inzet bouwt waar nauwkeurigheid telt, dan horen we graag waar je tegenaan loopt.

Wil je het systeem in actie zien? Probeer het hier: https://korting.onlinebouwgereedschap.nl/

Kopen of bouwen: waarom maatwerk-AI beter is dan SaaS-abonnementen

De "vibe shift" in AI: waarom Gemini 3 Pro en Antigravity het spel veranderen

Axel Dekker, founder of What's Next

Have an AI question worth answering?

Tell us what you are trying to ship, we will point you at the fastest path.

  • Gratis adviesgesprek van 30 min
  • Geen verplichtingen
  • Een specialist, geen sales rep