Kennisbank

Ik bouwde al rond AI voordat AI er klaar voor was

OmniTechs-oprichter Sina Esfahani over AI als systeemonderdeel: API's, geheugen, latentie, kosten en de engineering die nog steeds om het model heen zit.

Mijn ervaring met AI begon niet toen “AI-agents” een modewoord werden.

Ze begon eerder, toen de modellen duidelijk minder konden, context duur was, vision onbetrouwbaar, latentie een echte beperking, en een nuttige taak bijna altijd extra machinery rond het model vroeg.

En dat was juist het interessante deel.

Ik was nooit bijzonder geïnteresseerd in een AI-API aanroepen alleen om te bewijzen dat ik een AI-API kon aanroepen.

Wat me interesseerde was een andere vraag:

Hoe maak je van een model een onderdeel van een echt systeem?

Een systeem dat informatie ontvangt, er genoeg van begrijpt, een beslissing neemt, een andere dienst aanroept, iets opslaat, iemand waarschuwt of een workflow voortzet.

Die vraag loopt door het grootste deel van mijn ontwikkelwerk.

En ze begon lang voor moderne AI. Ik gebruik haar nog steeds bij maatwerksoftware en workflowwerk bij OmniTechs.

Voor AI waren er API’s

Meer dan tien jaar geleden, bij een van mijn eerste commerciële websites, was ik al gefascineerd door systemen koppelen.

De site draaide op WordPress, deed het uiteindelijk goed in Google en leverde echte verkopen op.

Maar ik wilde niet dat de website alleen als website bestond.

Telegram werd belangrijk, dus bouwde ik er een Telegram-bot omheen.

De bot las producten van de WordPress-site, toonde de catalogus in Telegram en liet gebruikers de winkel gebruiken zonder de gewone website te doorlopen.

Achteraf was de implementatie eenvoudiger dan de systemen die ik nu bouw.

Maar het idee zat er al in:

haal data uit het ene systeem, breng het in de vorm die een andere interface nodig heeft, en laat de koppeling aanvoelen als één product.

Jaren later werd dat een veel groter deel van mijn werk. Diezelfde vraag — één nuttig gesprek, één koppeling, duidelijke grenzen — staat nu op Telegram bot laten maken.

Leren door in problemen te worden gegooid die ik nog niet kon oplossen

Een project dat mijn werkwijze heeft gevormd heette Mita.

Het doel was ongewoon.

We wilden met een smartphone trillingen meten en de eigenfrequenties van muziekinstrumenten bepalen.

De accelerometer van de telefoon verzamelde de metingen. De mobiele app stuurde de data naar een server. De server verwerkte het signaal, analyseerde de trillingsrespons en gaf terug wat de app nodig had.

Ik zou oorspronkelijk de backend bouwen.

We spraken af dat iemand anders de mobiele applicatie zou maken.

Ik huurde een ontwikkelaar met ongeveer acht jaar ervaring.

Halverwege het project vertrok hij.

Destijds was ik vooral Python-ontwikkelaar.

Maar het project moest nog steeds opgeleverd worden.

Dus ging ik intensief JavaScript leren, leerde React en React Native, en nam de mobiele kant zelf over.

Het was niet de prettigste manier om een nieuwe stack te leren.

Het was wel buitengewoon effectief.

Die ervaring herhaalde zich later in andere vormen.

Bij een ander bedrijf draaide veel van mijn werk om externe API’s. Diensten zoals DataForSEO en andere databronnen gaven informatie in hun eigen formaat terug.

De frontend had geen boodschap aan hoe die diensten hun data voorstelden.

Die had onze representatie nodig.

Dus was mijn werk vaak: meerdere diensten aanroepen, antwoorden normaliseren, informatie aggregeren en een voorspelbare API voor de rest van de applicatie beschikbaar maken. Dat is nog steeds hoe ik API-koppelingen benader: het interne formaat van vijf leveranciers hoort niet in de rest van het product te lekken.

Toen kwam infrastructuur.

Op een gegeven moment kreeg ik een Docker-configuratie van grofweg duizend regels, en feitelijk de opdracht:

Dit werkt niet. Los het op.

Er was één klein probleem.

Ik kende Docker nauwelijks.

Dus leerde ik Docker.

Ik bouwde delen van de infrastructuur opnieuw, begreep wat het systeem deed en kreeg het draaiend.

Later leerde ik Angular omdat ik een groter deel van de applicaties wilde kunnen dragen, in plaats van tot de backend beperkt te blijven.

Uiteindelijk stelde ik ook een klein team samen en probeerde ik grotere projecten aan te nemen.

Die poging mislukte nogal spectaculair.

Maar het leerde me iets wat succesvolle projecten zelden zo efficiënt leren:

softwareontwikkeling is niet alleen weten hoe je code schrijft.

Architectuur, scope, communicatie, contracten tussen onderdelen, testen, deployment, eigenaarschap en kunnen herstellen wanneer iets misgaat, wegen even zwaar.

Die lessen werden verrassend belangrijk toen ik serieus met AI ging werken. Hoe ik die grenzen nu zet wanneer AI code schrijft, staat in Software bouwen met AI.

Toen AI in de architectuur kwam

Mijn serieuze experimenten met moderne AI-API’s begonnen grofweg een paar jaar geleden.

De modellen waren indrukwekkend, maar vergeleken met nu ook frustrerend.

Ze konden de algemene betekenis begrijpen en tegelijk het detail missen dat ertoe deed.

Vision was beperkt.

Contextvensters waren kleiner en relatief duur.

Gestructureerd werken met externe systemen was nog in ontwikkeling.

Realtime spraakworkflows waren moeilijk.

En hallucineren was geen theoretische randzaak. Als je een model in een echte workflow wilde laten meedoen, moest je aannemen dat het iets kon misverstaan.

Dus in plaats van te verwachten dat het model alles magisch zou oplossen, ging ik er structuur omheen bouwen.

Dat werd het patroon.

Het model deed waar het goed in was.

Software beheerste alles eromheen.

Experiment 1: een intelligente documentinbox

Een van mijn vroege concepten was een documentbeheersysteem.

Ik wilde foto’s of scans van documenten kunnen sturen zonder ieder bestand handmatig te organiseren.

De workflow was grofweg:

document → AI-interpretatie → classificatie → beslissing → actie

Het model bekeek het document en bepaalde wat het leek te zijn.

Daarna besliste het omringende systeem wat er moest gebeuren.

Als het document een actie vroeg, kon het systeem een melding maken.

Als er geen directe actie nodig was, kon het document gearchiveerd worden.

Belangrijke informatie kon worden geëxtraheerd en in een index gezet, zodat het later terug te vinden was.

Vandaag klinkt dat redelijk gewoon.

Toen was het betrouwbaar maken daarvan helemaal niet gewoon.

Het interessante was niet alleen vragen:

“Wat is dit document?”

Het interessante was het antwoord omzetten in iets waar software veilig mee kon werken.

Dat onderscheid werd steeds belangrijker:

AI-output is nog geen workflow.

Het wordt pas een workflow wanneer je vastlegt wat software met die output mag doen.

Experiment 2: een Telegram-bot die een relatie vertaalde

Een ander experiment kwam uit een heel praktisch probleem.

Mijn toenmalige vriendin sprak Pools en nauwelijks Engels.

Ik sprak Perzisch.

Gewone vertaalapps werkten technisch, maar een gesprek erdoorheen was traag en onnatuurlijk.

Ik bouwde al jaren Telegram-bots, dus bouwde ik er nog een.

Alleen zat er deze keer AI tussen.

Ik kon een Perzisch spraakbericht sturen.

Het systeem verwerkte het, vertaalde de betekenis en maakte iets dat zij in het Pools kon ontvangen.

Zij kon vanaf de andere kant antwoorden.

In plaats van voortdurend tekst tussen apps te kopiëren, werd de vertaallaag deel van het gesprek zelf.

Maar vertalen gaf een ander probleem.

Losse zinnen zijn vaak dubbelzinnig.

Mensen lossen dat bijna onzichtbaar op omdat we onthouden waar we vijf berichten geleden, gisteren of vorige week over spraken.

Een model zonder die context kan een technisch geldige vertaling maken die voor het gesprek volledig fout is.

Dus ging ik met geheugen experimenteren.

Het systeem haalde nuttige informatie uit eerdere gesprekken en gaf het model genoeg van die context om te begrijpen waar een dubbelzinnige zin waarschijnlijk over ging.

Dat verbeterde de ervaring aanzienlijk.

Het gaf ook een nieuw probleem:

kosten.

Meer context betekende meer tokens.

Meer tokens betekenden meer kosten.

En grote contexten konden de hele interactie ook trager maken.

Vandaag praten mensen voortdurend over “AI-geheugen” en “context engineering.”

Voor mij kwamen die ideeën oorspronkelijk uit een veel minder glamoureus probleem:

ik wilde simpelweg dat twee mensen zonder gedeelde taal natuurlijker konden praten.

We gebruikten het systeem maandenlang.

Uiteindelijk verdween het project omdat de behoefte verdween.

Maar de architectuur bleef in mijn hoofd.

En ik wilde verder.

Wat als taal uit een gesprek verdween?

Het Telegram-experiment leidde tot een veel ambitieuzer concept.

Wat als twee mensen een spraak- of videogesprek konden voeren, ieder in hun eigen taal, terwijl de ander het gesprek in zijn taal hoorde?

Geen ondertitels.

Niet stoppen om handmatig te vertalen.

Een communicatielaag die ertussenin draaide.

Dat vroeg een zwaardere pijplijn.

Audio moest tekst worden.

Het systeem moest de uiting begrijpen.

Het model moest die met genoeg gesprekscontext verwerken.

Het resultaat moest vertaald worden.

Daarna weer stem worden.

En dat alles snel genoeg om nog als gesprek aan te voelen.

Die laatste eis was de moeilijke.

Latentie verandert alles.

Een model kan razend intelligent zijn, maar als het enkele seconden duurt om mee te doen, stort de gebruikerservaring in.

We moesten vaak kleinere, snellere modellen gebruiken.

Die waren sneller, maar de kwaliteit was niet altijd goed genoeg.

Dus wachtten delen van het project feitelijk tot de technologie bijtrok.

Dat is in mijn loopbaan vaker gebeurd.

Soms is een idee verkeerd.

Soms is de implementatie verkeerd.

En soms is het idee gewoon te vroeg.

Een AI-recruitmentsysteem

Dezelfde architectuur groeide later uit tot een ander concept.

In plaats van gesprekken tussen twee mensen te vertalen: kon AI zelf aan het gesprek deelnemen?

We experimenteerden met een recruitmentworkflow.

Stel dat er een nieuwe vacature in het systeem komt.

In plaats van handmatig ieder profiel te doorzoeken, zoekt het systeem in de database naar kandidaten van wie de informatie relevant lijkt.

Die kandidaten kunnen dan naar de volgende stap.

Het grotere concept ging verder.

Een AI-interviewer kon een eerste gesprek met de kandidaat voeren.

De stem werd getranscribeerd, het gesprek ging door het model, en het systeem kon het interview voortzetten op basis van eerdere antwoorden en informatie over de functie.

Opnieuw: de architectuur was mogelijk.

Maar realtime prestaties waren de beperking.

De beste modellen waren niet per se snel genoeg.

De snelste modellen waren niet per se goed genoeg.

En als je met een mens praat, worden die verschillen pijnlijk zichtbaar.

Dus stopten we met sommige onderdelen van het project.

Niet omdat ik niet meer in het interactiemodel geloofde.

Omdat de infrastructuur rond de modellen het punt had bereikt waarop verbeteringen in de modellen zelf onevenredig veel productwinst zouden opleveren.

Soms is wachten ook een ontwerpkeuze.

Wat ik van al deze projecten leerde

Werken met AI-API’s leerde me bijna het tegenovergestelde van wat veel AI-demo’s suggereren.

Het model is zelden het hele product.

Meestal is het één component in een veel groter systeem.

Er omheen heb je nog steeds nodig:

  • authenticatie;
  • API’s;
  • databases;
  • status;
  • rechten;
  • wachtrijen;
  • WebSockets;
  • bestandsopslag;
  • zoeken;
  • indexeren;
  • foutafhandeling;
  • retries;
  • validatie;
  • logging;
  • gebruikersinterfaces;
  • fallbackgedrag;
  • en regels die precies bepalen wat de AI wel en niet mag doen.

Een krachtig model kan het systeem dramatisch beter maken.

Maar het haalt de noodzaak van engineering niet weg.

In veel gevallen vergroot het die.

Omdat het systeem nu een component bevat waarvan de output probabilistisch is.

Waarom ik meestal eerst de moeilijke verbindingen bouw

Deze geschiedenis verklaart een principe dat ik nog steeds gebruik.

Voordat ik te lang besteed aan iets dat eruitziet als een af product, wil ik de moeilijke technische relaties bewijzen.

Kunnen we de API aanroepen?

Kunnen we de benodigde data krijgen?

Kunnen de systemen authenticeren?

Als iets realtime moet, kunnen we de WebSocket-verbinding opzetten?

Kan de data van A naar B?

Kan het model informatie teruggeven in een structuur die de rest van de applicatie echt kan gebruiken?

Blijft de latentie acceptabel?

Blijven de kosten acceptabel?

Die vragen wil ik eerst beantwoord hebben.

Zodra de onzekere technische verbindingen werken, is de rest van de applicatie veel makkelijker tot product te vormen.

Zo denk ik ook over AI.

Ik ben minder geïnteresseerd in een interface die intelligent lijkt dan in het bewijs dat die intelligentie daadwerkelijk in het systeem erachter kan meedoen.

Zo stel ik mezelf ook voor op Sina Esfahani — over de oprichter.

AI werd beter. Het engineeringprobleem verschoof.

De afgelopen jaren gebeurde er iets interessants.

Veel ideeën die door modelbeperkingen moeilijk waren, werden geleidelijk praktisch.

Modellen gingen beter redeneren over grotere hoeveelheden informatie.

Vision verbeterde enorm.

Gestructureerde output werd betrouwbaarder.

Toolgebruik werd geavanceerder.

Spraak werd sneller.

Kleinere modellen werden verrassend capabel.

Context werd beter beheersbaar.

De bottleneck verschoof.

Eerder moest ik vaak vragen:

“Is het model hier goed genoeg voor?”

Steeds vaker is de vraag:

“Hoe architectureren we het systeem zodat het model dit veilig, snel en nuttig kan doen?”

Dat is een veel interessanter probleem.

Ik zie AI niet als een feature

Dit is waarschijnlijk de grootste verandering in hoe ik naar software kijk.

Ik zie AI niet als een doos die je op een product plakt omdat ieder product blijkbaar een AI-knop nodig heeft.

Ik zie modellen als een extra rekenmogelijkheid.

Soms heb je een database nodig.

Soms een zoekmachine.

Soms een deterministisch algoritme.

Soms een API.

En soms heb je een probleem met taal, dubbelzinnigheid, classificatie, extractie, waarneming of redeneren waarbij een model het juiste onderdeel is.

Dan wordt de engineeringvraag:

Waar hoort het in het systeem?

Welke informatie mag het ontvangen?

Welke tools mag het hebben?

Wat blijft deterministisch?

Wat gebeurt er bij onzekerheid?

Wat wordt onthouden?

Wat mag nooit onthouden worden?

Wat gebeurt er wanneer het model faalt?

Wat gebeurt er wanneer de API verdwijnt?

Kunnen we het model vervangen zonder het hele product opnieuw te bouwen?

Dat zijn de vragen die me nu interesseren.

De mislukte experimenten waren geen weggegooid werk

Verscheidene systemen waar ik aan werkte, werden nooit commerciële producten.

Sommige waren te vroeg.

Sommige werden te duur.

Sommige hingen af van modellen die niet snel genoeg waren.

Sommige faalden gewoon.

Ik bouwde ook teams voordat ik echt wist hoe je softwareteams goed aanstuurt.

Ik nam projecten aan voordat ik hun operationele complexiteit volledig begreep.

Ik leerde technologieën omdat ik opeens geen andere keuze had.

Maar daar komt een groot deel van mijn huidige werkwijze vandaan.

Docker was ooit iets dat ik niet kon gebruiken.

React Native leerde ik omdat de ontwikkelaar van de applicatie halverwege verdween.

AI-contextbeheer werd interessant omdat letterlijke vertaling niet genoeg was voor een echt gesprek.

Realtime AI-architectuur werd belangrijk omdat vijf seconden vertraging een natuurlijk gesprek kapotmaken.

API-abstractie werd normaal omdat de frontend de interne structuur van vijf verschillende leveranciers niet hoeft te begrijpen.

Geen van die lessen kwam uit een tutorialproject.

Ze kwamen uit systemen die écht moesten werken.

En dat is nog steeds de engineering die ik het liefst doe.

Niet technologie toevoegen omdat de technologie in de mode is.

Iets moeilijks vinden, het onzekere deel eerst bewijzen, de systemen koppelen die moeten praten, en die verbindingen tot een product maken dat iemand écht kan gebruiken.

Dat is de standaard die ik meeneem naar maatwerksoftware bij OmniTechs.