De architectuur
Een productieklare AI-chatbot bestaat uit vier onderdelen:
1. Chat UI: Invoerveld, weergave van de berichtengeschiedenis, laadstatus, foutafhandeling 2. Conversatiestatus: Een array van berichten (rol + inhoud) opgeslagen in de frontend-status 3. Backend-proxy: Een Supabase Edge Function of Xano-endpoint dat OpenAI aanroept 4. Systeemprompt: De instructieset die de persona, kennis en beperkingen van je chatbot bepaalt
De conversatiestatus is het belangrijkste concept. De API van OpenAI is stateless, elk verzoek moet de volledige gespreksgeschiedenis bevatten. Je frontend beheert deze geschiedenis en stuurt ze mee bij elk bericht.
Architectuurkeuzes voor chatbots: retrieval versus generatie
Bepaal voordat je begint met bouwen of je chatbot voornamelijk retrieval-gebaseerd of generatie-gebaseerd is. Deze keuze bepaalt de architectuur, de kosten en de kwaliteitskenmerken van het eindproduct.
Een retrieval-gebaseerde chatbot zoekt het best passende antwoord uit een vooraf gedefinieerde kennisbank. De gebruiker stelt een vraag, je systeem zoekt de meest gelijkende vraag-en-antwoordcombinatie in de database op en toont die, eventueel geherformuleerd door het LLM. Dit is snel, goedkoop (weinig tokens verbruikt) en zeer nauwkeurig binnen het kennisdomein. Het faalt wanneer gebruikers vragen stellen die niet in de kennisbank staan, wat resulteert in nutteloze antwoorden zoals "daar heb ik geen informatie over".
Een generatie-gebaseerde chatbot stuurt de vraag van de gebruiker samen met context naar een LLM en laat het model een nieuw antwoord opstellen. Dit werkt voor vragen die nooit expliciet zijn vastgelegd, kan informatie uit meerdere bronnen combineren en levert natuurlijke, conversationele antwoorden op. De kosten zijn hoger (meer tokens per bericht) en het model kan af en toe aannemelijk klinkende maar onjuiste informatie genereren. Voor de meeste klantgerichte supportchatbots is de juiste architectuur hybride: begin met retrieval (haal de 3 meest relevante kennisbankartikelen op) en gebruik generatie om op basis van die bronnen een samenhangend antwoord te formuleren, met de harde beperking dat het model niet buiten die bronnen om mag antwoorden. Dit is RAG, Retrieval Augmented Generation, en het combineert nauwkeurigheid met flexibiliteit.
De chat-UI bouwen in WeWeb
Maak in WeWeb een pagina-variabele messages (array, standaard leeg). Voeg twee componenten toe:
Berichtenlijst: Een Repeating Group gekoppeld aan messages. Elk item heeft een conditionele stijl: berichten van de gebruiker rechts uitgelijnd met een achtergrond in de primaire kleur, berichten van de assistent links uitgelijnd met een neutrale achtergrond. Koppel de tekst aan item.content.
Invoerveld: Een tekstinvoer gekoppeld aan een userInput-variabele, plus een "Verstuur"-knop. Bij een klik op de knop: (1) voeg {role: "user", content: userInput} toe aan messages, (2) maak userInput leeg, (3) roep de API-actie aan, (4) voeg de respons toe als {role: "assistant", content: response}.
Voeg een laadanimatie toe die zichtbaar is zolang de API-aanroep loopt.
De chatbot trainen op je documentatie
Een chatbot die alleen weet wat GPT-4o tijdens de training heeft geleerd, kent de specifieke functies, prijzen, beleidsregels of procedures van jouw product niet. Om de chatbot echt nuttig te maken voor je gebruikers, moet je je kennisbank in het gesprek injecteren. Er zijn twee benaderingen: statische injectie (documentatie rechtstreeks in de systeemprompt plakken) en dynamische injectie via RAG.
Statische injectie werkt voor kleine kennisbanken onder de 5.000 woorden. Schrijf je documentatie als gestructureerde tekst, voeg ze toe aan de systeemprompt, en het model gebruikt dit als primaire referentie. Nadeel: de statische aanpak is duur op schaal (je stuurt de volledige kennisbank mee bij elk bericht), en de systeemprompt actueel houden terwijl de documentatie verandert is handmatig werk.
Voor grotere kennisbanken is RAG de juiste aanpak. Verwerk je documentatie tot chunks van 200-500 woorden, embed elke chunk met het text-embedding-3-small model van OpenAI, en sla de embeddings op in Supabase met de pgvector-extensie. Bij elke vraag embed je het bericht van de gebruiker, zoek je via cosine-similarity search de 3-5 meest gelijkende chunks op, en injecteer je alleen die chunks in de systeemprompt. Dit kost drastisch minder per bericht en schaalt naar duizenden documentatiepagina's. Wanneer je de documentatie bijwerkt, embed je opnieuw de gewijzigde chunks en werk je de database bij, de chatbot neemt de wijzigingen automatisch mee bij de volgende vraag.
De Supabase Edge Function
Je Edge Function ontvangt de berichtenarray en systeemprompt, roept OpenAI aan en retourneert de respons:
```typescript
serve(async (req) => {
const { messages, systemPrompt } = await req.json()
const completion = await openai.chat.completions.create({
model: "gpt-4o",
messages: [
{ role: "system", content: systemPrompt },
...messages
],
max_tokens: 500,
temperature: 0.7
})
return new Response(
JSON.stringify({ content: completion.choices[0].message.content }),
{ headers: { "Content-Type": "application/json" } }
)
})
```
De systemPrompt kan worden meegegeven vanuit de frontend (handig voor apps met meerdere persona's) of hardcoded in de functie staan (veiliger).
Een effectieve systeemprompt schrijven
De systeemprompt bepaalt alles aan het gedrag van je chatbot. Een goede systeemprompt voor productie bevat:
- Rol: "Je bent een klantenservicemedewerker voor Acme SaaS, een projectmanagementtool." - Kennis: "Je helpt gebruikers met: projecten aanmaken, teamleden uitnodigen, integraties instellen en facturatievragen." - Beperkingen: "Beantwoord alleen vragen over Acme SaaS. Verwijs bij niet-gerelateerde vragen beleefd door. Bespreek nooit producten van concurrenten. Verzin nooit functies die niet bestaan." - Formaat: "Houd antwoorden onder de 100 woorden. Gebruik opsommingstekens voor stappen. Eindig supportantwoorden altijd met: 'Laat het me weten als dit helpt!'" - Escalatie: "Als de gebruiker frustratie uit of een factureringsfout vermeldt, zeg dan: 'Ik breng je in contact met ons team' en start de escalatieflow."
Escalatie naar menselijke support afhandelen
Elke productiechatbot heeft een duidelijk escalatiepad nodig voor vragen die de AI niet zelfverzekerd kan afhandelen, emotioneel geladen gesprekken, en situaties die accountniveau-acties vereisen die een bot niet zou moeten uitvoeren (terugbetalingen verwerken, accounts verwijderen, uitzonderingen op facturatie maken). Ontwerp dit vanaf het begin in de systeemprompt en de UI, niet als een bijgedachte achteraf.
Definieer escalatietriggers expliciet in de systeemprompt: "Als de gebruiker een factureringsgeschil, een mislukte betaling of een accountopschorting vermeldt, probeer dit dan niet zelf op te lossen. Antwoord in plaats daarvan exact met: ESCALATE: [korte reden], en verder niets." Je Edge Function detecteert het ESCALATE-voorvoegsel en maakt een supportticket aan in je helpdesk (Intercom, Zendesk, of zelfs een Supabase-tabel) in plaats van het bericht aan de gebruiker te tonen.
Vervang in de UI, wanneer een escalatie wordt gedetecteerd, de chatbot-interface door een bericht: "Ik breng je in contact met ons supportteam. Zij nemen binnen [SLA] contact op. Je kunt ook mailen naar support@jouwbedrijf.com." Stuur het supportteam een e-mail met het volledige gesprekstranscript. Deze aanpak houdt de overdracht soepel voor de gebruiker en geeft de supportmedewerker volledige context. Meet het escalatiepercentage als kernstatistiek van je chatbot, een percentage boven de 20% duidt erop dat de kennisbank uitgebreid moet worden.
Persistente context toevoegen
De basischatbot vergeet alles zodra de pagina wordt vernieuwd. Om hem slimmer te maken:
Gebruikerscontext injecteren: Haal bij het starten van de chatbotsessie de accountgegevens van de gebruiker op (abonnement, gebruik, recente activiteit) en voeg ze toe aan de systeemprompt: "Het huidige abonnement van de gebruiker is Pro. Zijn laatste activiteit was 3 dagen geleden. Hij heeft 2 actieve projecten."
Gespreksbehoud: Sla berichten op in een Supabase-tabel (chatbot_sessions) met user_id en session_id. Haal bij het laden van de pagina de laatste N berichten op en vul de messages-array daarmee vooraf in.
Kennisbank: Sla voor productdocumentatie artikelen op in Supabase met embeddings (via pgvector). Voer voordat je GPT-4o aanroept een similarity search uit en injecteer de meest relevante artikelen in de systeemprompt. Dit heet RAG (Retrieval Augmented Generation) en verbetert de nauwkeurigheid van antwoorden drastisch.
Het succes van je chatbot meten
Een chatbot zonder meting is een functie zonder feedbackloop. De statistieken die het meest van belang zijn voor een support- of productchatbot: oplossingspercentage (het percentage gesprekken waarbij de gebruiker niet escaleerde of geen apart supportticket indiende), gesprekslengte (gemiddeld aantal berichten per gesprek, te kort duidt erop dat de bot vroeg faalt, te lang duidt erop dat hij niet efficiënt oplost), en beoordeling na afloop (een eenvoudige duim omhoog/omlaag na elk gesprek, opgeslagen in Supabase en te bekijken in een dashboard).
Instrumenteer naast deze gebruikersgerichte statistieken ook je Edge Function om te loggen: welke kennisbankartikelen het vaakst werden opgehaald (vertelt je waar gebruikers het meest naar vragen), welke vragen geen sterke match hadden in de vectordatabase (vertelt je waar de kennisbank hiaten heeft), en modellatentie en tokenaantallen per sessie (vertelt je wat je kosten per gesprek zijn). Bekijk deze statistieken wekelijks gedurende de eerste maand na lancering.
De meest bruikbare statistiek is de lijst met vragen zonder match in de kennisbank. Exporteer deze wekelijks, schrijf antwoorden voor de top 20 onbeantwoorde vragen, voeg ze toe aan de kennisbank en embed opnieuw. Een chatbot die zijn oplossingspercentage de eerste maand met 5% per week verbetert, bereikt vaak 80%+ oplossing binnen 6 weken na lancering, aanzienlijk beter dan de meeste mens-eerst supportworkflows bij SaaS-bedrijven in een vroege fase.
Kosten en prestaties in productie
Voor een SaaS met 500 actieve gebruikers die elk 10 berichten/dag versturen:
- Gemiddeld bericht: 50 tokens input + 100 tokens output - GPT-4o-prijzen: $2.50/M input + $10/M output - Dagelijkse kosten: 500 × 10 × 150 tokens = 750,000 tokens = ~$2.50/dag = ~$75/maand
Om de kosten te beheersen: implementeer een tokenbudget per sessie (stop met het toevoegen van geschiedenisberichten zodra het gesprek 2.000 tokens overschrijdt, en begin dan met het samenvatten van oude berichten). Gebruik GPT-4o mini voor eenvoudige vragen ($0.15/M input) en bewaar GPT-4o voor complexe vragen.
Responstijd: GPT-4o antwoordt binnen 1-3 seconden. Voeg een typindicator toe om verwachtingen te managen. Implementeer streaming voor een UX onder de seconde.