Token-kosten bijhouden in productie-AI

Het moment waarop je beseft dat je AI-kosten niet meer bijhoudt, is altijd het verkeerde moment. Dat is namelijk wanneer de factuur binnenkomt.
In een systeem dat GPT-4o gebruikt voor chat, text-embedding-3-small voor vector search, en WhatsApp via Twilio voor delivery, zijn er drie aparte kostenmechanismen actief — elk met hun eigen berekening, elk met hun eigen valkuilen. Hier is hoe we dat per bericht doorrekenen en opslaan.
GPT-4o: prompt versus completion
De basisberekening is simpel. Azure OpenAI retourneert token-tellingen in elke API-response:
// GPT-4o: $2.50/1M input tokens, $6.25/1M output tokens
export function calcCost(
promptTokens: number,
completionTokens: number,
): number {
return promptTokens * 0.0000025 + completionTokens * 0.00000625;
}
De asymmetrie tussen input en output tokens is belangrijk. Een agent die RAG-context meestuurt, heeft typisch 2.000–4.000 prompt-tokens per aanroep (inclusief kennisbank-chunks, toon-van-stem, en conversatiegeschiedenis) en 200–400 completion-tokens. Bij 1.000 berichten per dag loopt dat snel op: de prompt-tokens zijn goedkoper per token, maar er zijn er meer van.
Concrete vuistregel: elke euro aan completion-tokens staat tegenover gemiddeld €2,50 aan prompt-tokens voor een RAG-intensieve chatbot.
Embedding-kosten: klein maar cumulatief
Elke zoekopdracht triggert een embedding-call:
// text-embedding-3-small: $0.020/1M tokens
export function calcEmbedCost(tokens: number): number {
return tokens * 0.00000002;
}
Per embedding is dat verwaarloosbaar — een query van 20 tokens kost $0.0000004. Maar je embedt niet alleen queries: elk geüpload document wordt geëmbed bij indexering, en elk persoonsprofiel wordt opnieuw geëmbed als de data wijzigt. In een systeem met wekelijkse imports van 500 medewerkers, 50 documenten, en 2.000 chat-queries per dag, zijn embedding-kosten misschien 5–10% van de totale AI-kosten — maar ze worden invisible als je ze niet apart bijhoudt.
WhatsApp-kosten: drie regels, één CSV
WhatsApp-berichten via Twilio hebben twee kostlagen die onafhankelijk van elkaar werken.
Twilio flat fee — $0.005 per bericht, altijd, ongeacht categorie of land:
export const TWILIO_FLAT_FEE = 0.005;
Meta-fee — per categorie, per land, opgeslagen in een CSV die Twilio publiceert. De regels zijn:
MARKETING→ altijd de Meta-fee betalenAUTHENTICATION→ altijd de Meta-fee betalenUTILITY→ alleen buiten het 24-uurs conversatievenster; binnen dat venster is de Meta-fee gratis- Chat-kanaal (geen WhatsApp) → geen Meta-fee
export function calcMessageCost(input: CalcCostInput): CostBreakdown {
if (input.channel === "chat") {
return {
twilioFee: 0,
metaFee: 0,
total: 0,
withinWindow: false,
recipientCountry: null,
};
}
const twilioFee = TWILIO_FLAT_FEE;
const recipientCountry = countryFromPhone(input.recipientPhone);
const rates = lookupRates(recipientCountry ?? FALLBACK_KEY);
let metaFee: number;
if (input.category === "UTILITY") {
metaFee = input.withinWindow ? 0 : rates.utility;
} else if (input.category === "MARKETING") {
metaFee = rates.marketing ?? 0; // US heeft geen marketing-support → null → 0
} else {
metaFee = rates.authentication;
}
return {
twilioFee,
metaFee,
total: twilioFee + metaFee,
withinWindow: input.withinWindow,
recipientCountry,
};
}
Het 24-uurs venster-mechanisme is de meest onderschatte regel in WhatsApp Business-pricing. Een UTILITY-template die verzonden wordt naar een medewerker die net heeft geantwoord (binnen het venster), is gratis voor de Meta-fee. Dezelfde template 25 uur later kost de volledige Meta-fee bovenop de Twilio flat fee.
Landopzoeking via E.164
De Meta-fee varieert sterk per land. Nederland heeft andere tarieven dan India of Brazilië. We leiden het land af uit het telefoonnummer via libphonenumber-js:
export function countryFromPhone(phone: string): string | null {
const parsed = parsePhoneNumberFromString(
phone.startsWith("+") ? phone : `+${phone}`,
);
return parsed?.country ?? null;
}
Als het land niet in de CSV staat — of als het telefoonnummer ongeldig is — valt de berekening terug op de "All other countries"-rij. De CSV wordt bij module-load geparst en gecached als een Map<string, MetaRateRow> zodat er geen bestandslees-overhead is per bericht.
Alles opslaan per aanroep
Elke AI-aanroep logt zijn kosten in ai_call_logs:
{
prompt_tokens: 2847,
completion_tokens: 312,
total_tokens: 3159,
cost_usd: 0.009, // calcCost(2847, 312)
latency_ms: 1840,
agent: 'global',
model: 'gpt-4o',
}
WhatsApp-kosten worden per verzonden bericht opgeslagen met het CostBreakdown-object, inclusief het ontvangend land en of het bericht binnen het conversatievenster viel. Dat maakt het later mogelijk om te analyseren hoeveel van je WhatsApp-budget wordt besteed aan templates die je goedkoper had kunnen inplannen.
Aggregaties in het dashboard
Op basis van de ruwe logs bouw je aggregaties: kosten per agent, per intent, per dag, per medewerker. Dat zijn eenvoudige Postgres GROUP BY-queries. De interessantste query is "kosten per intent" — die onthult welke soorten vragen structureel duur zijn, zodat je kunt besluiten om die te cachen, te herformuleren, of op een goedkoper model te draaien.
Kosten-bewustzijn bouw je in, niet achteraf. Als je wacht tot de eerste grote factuur, mis je weken aan data om te begrijpen waarom de kosten zo hoog zijn. Vertel ons over je AI-project als je wilt sparren over cost-architectuur.