Alle artikelen

AI8 min leestijd

Wat we hebben geleerd bij het aansluiten van een lokale LLM op een telefoonlijn

Bij managed voice-AI wordt per minuut gefactureerd, dus een agent die goed presteert, zorgt voor een hogere factuur. Door Vocale zo te ontwikkelen dat het zowel op een gehoste API als op een eigen GPU kan draaien, hebben we vier dingen geleerd over wat er daadwerkelijk lastig wordt wanneer de modellen intern worden ingezet.

Sequentiediagram van één gespreksbeurt in Vocale: WebRTC-audio bereikt een LiveKit-server en -agent, Silero-spraakactiviteitsdetectie identificeert spraak, Faster-Whisper retourneert een transcript, de beurt wordt opgeslagen in PostgreSQL, de chatcontext gaat naar Qwen3-8B, aangeboden door vLLM, gestreamde tokens worden genormaliseerd en samengevoegd tot zinnen, XTTS-v2 zet deze om in audio, en de audio wordt teruggestuurd naar de beller.

Elk beheerd spraak-AI-platform rekent per minuut. Dat klinkt redelijk, totdat je er een op een helpdesk zet en het aan het werk gaat, want de beloning voor een goede medewerker is het beantwoorden van meer telefoontjes, en de beloning voor het beantwoorden van meer telefoontjes is een hogere factuur. Hoe beter het werkt, hoe meer het kost. Bij een helpdesk, die juist bedoeld is om grote volumes op te vangen, is dat de verkeerde prikkel om voor te kiezen.

We hebben Vocale ontwikkeld om de telefoonlijn van een bedrijf te beantwoorden: het neemt op bij de eerste beltoon, beantwoordt vragen op basis van de eigen handleidingen en het beleid van het bedrijf, en opent een supportticket wanneer het eerlijke antwoord is dat iemand hiernaar moet kijken. Al vroeg hebben we een beslissing genomen die alles daarna heeft bepaald. Het platform biedt twee engines achter één product. Op het gehoste niveau zijn spraakherkenning, redenering en spraaksynthese afkomstig van een API van een derde partij en wordt elke minuut in rekening gebracht. Op het zelfgehoste niveau draaien alle drie de componenten op een GPU die het bedrijf zelf beheert; er verlaat geen audio of document het gebouw, en een gesprek kost alleen stroom.

Beide niveaus gedragen zich identiek vanuit het perspectief van de beller. Het was het interessante deel om ze zover te krijgen, en dit is wat we hebben geleerd.

Wat zit er daadwerkelijk in de lus

Een spraakagent is geen chatbot waaraan een microfoon is vastgeschroefd. Eén gespreksbeurt is een keten, en elke schakel zorgt voor een vertraging die een mens kan horen.

Audio komt via WebRTC binnen in een LiveKit-ruimte. Spraakactiviteitsdetectie (wij gebruiken Silero) bepaalt wanneer de beller daadwerkelijk is begonnen en gestopt met spreken. De gebufferde audio gaat naar Faster-Whisper en komt terug als een transcript. Die beurt wordt weggeschreven naar Postgres, waarna de chatcontext en -geschiedenis naar het taalmodel gaan, dat op onze zelfgehoste laag Qwen3-8B is, aangedreven door vLLM. Tokens stromen terug, worden genormaliseerd en samengevoegd tot volledige zinnen, en bereiken pas daarna XTTS-v2, dat ze weergeeft als audioframes die via WebRTC weer worden teruggestuurd.

Vier modellen, één schrijfbewerking in de database en een roundtrip, allemaal binnen de pauze die een beller tolereert voordat hij ‘hallo?’ zegt in de stilte.

Het lokaal draaien van die keten is niet het moeilijke deel. Het lokaal draaien op gesprekssnelheid, op hardware die een middelgroot bedrijf daadwerkelijk zou aanschaffen, dat is waar de technische uitdaging ligt.

Les één: onderbreking is een productfunctie, geen uitzonderingsgeval

Bellers praten door de medewerker heen. Ze doen dat voortdurend, en ze doen het op het moment dat ze genoeg hebben gehoord, wat meestal na ongeveer zes woorden van een antwoord van twintig woorden is. Een medewerker die blijft praten, klinkt zo storend dat het moeilijk is om daar nog uit te komen, omdat het menselijke instinct is om harder te praten, en nu spreken twee partijen tegelijk in een systeem dat naar geen van beiden luistert.

Het afsnijden van de uitgaande audio is de voor de hand liggende helft. De helft die gemakkelijk over het hoofd wordt gezien, is het transcript. Als het eigen verslag van de medewerker nog steeds de volledige zin bevat die hij of zij wilde zeggen, gaat het gesprek verder vanuit een gesprek dat nooit heeft plaatsgevonden. Stel een vervolgvraag over „de tweede optie die u noemde“ en de medewerker zal vol vertrouwen antwoorden over iets wat de beller nooit heeft gehoord.

Dus wanneer spraak wordt gedetecteerd, stoppen we de audio en korten we vervolgens de opgeslagen beurt van de agent in tot wat er daadwerkelijk hardop is gezegd. Het gesprek gaat verder vanaf het gesprek dat de beller heeft meegemaakt, niet vanaf het gesprek dat het systeem had gepland.

Les twee: je kunt de vertraging niet wegnemen, dus moet je deze opvullen

Wanneer een vraag om een feit vraagt, doorzoekt de agent de geïndexeerde documenten voordat hij antwoordt. Die zoekopdracht duurt lang genoeg om als een weggevallen zin te worden ervaren. Stilte tijdens een telefoongesprek is niet neutraal. Het is alarmerend.

We voegen na ongeveer een halve seconde een korte, natuurlijke opvulzin toe, en annuleren die als het ophalen van gegevens eerder klaar is. Het is een klein stukje gedrag met een buitenproportioneel groot effect: het geeft het ophalen van gegevens de tijd die het nodig heeft, en geen van die tijd komt bij de beller over als stilte.

Dit is juist belangrijker op het zelfgehoste niveau, niet minder. Een lokaal model op een enkele GPU heeft andere latentie-eigenschappen dan de API van een hyperscaler, en die zijn niet per se slechter. Ze zijn anders verdeeld. Door het gesprek zo te ontwerpen dat het variabele latentie tolereert, konden we de locatie waar de modellen draaien aanpassen zonder dat dit invloed had op de beleving van de agent.

Les drie: geschreven tekst wordt niet hardop voorgelezen

Geldbedragen, datums als cijfers, tijden, afkortingen, productcodes. Dit alles is prima op een scherm, maar klinkt verkeerd in een stem. Geef een spraakmodel „1.250,00 op 15/03“ en je hoort iets wat nog nooit iemand hardop heeft gezegd.

We hebben een normalisatielaag tussen het taalmodel en het spraakmodel geplaatst die bedragen, datums, tijden en afkortingen per taal herschrijft naar hun gesproken vorm, voordat ze de synthese bereiken. Vocale draait in zes talen, en deze laag is per taal omdat de regels daadwerkelijk verschillen. Er is geen snelkoppeling waarbij je het één keer voor het Engels schrijft en vervolgens vertaalt.

Dit is het minst glamoureuze onderdeel van het systeem en een van de factoren die de grootste invloed hebben op de vraag of de agent klinkt als een mens of als een telefoonmenu.

Les vier: VRAM is de beperkende factor, niet de rekenkracht

Dit is het punt dat ons het meest verraste, en het punt dat de grootste invloed heeft op hoe je de hardware dimensionneert.

Tijdens een live spraaksessie worden de modellen gedurende het hele gesprek in het videogeheugen bewaard. De gelijktijdigheid wordt daarom beperkt door VRAM, niet door de ruwe doorvoercapaciteit. Je kunt een kaart hebben met rekenkracht over, die toch geen extra gesprek aankan omdat er geen ruimte is om de modellen op te slaan.

Erger nog: als je het framework standaard GPU’s laat poolen, komen spraak- en taalverwerking op dezelfde kaart terecht en belemmeren ze elkaar. Het symptoom is geen foutmelding. Het is een gesprek dat onder belasting langzamer wordt op een manier die niet aan één enkele component te wijten is. We begroten nu sessies per kaart en verdelen de werklast expliciet over de kaarten.

Als je een zelfgehoste implementatie plant, bereken dan eerst het aantal gelijktijdige verzoeken op basis van het videogeheugen. Al het andere vloeit voort uit dat getal.

Wat blijft hetzelfde

De basis blijft hetzelfde. Op beide niveaus geeft de agent antwoorden op basis van de door het bedrijf geüploade documenten in plaats van vanuit het geheugen van het model: een vraag die een feit vereist, activeert een zoekopdracht, en het antwoord wordt samengesteld uit de gevonden resultaten. Als er niets relevants wordt gevonden, is het de juiste reactie om dat te melden en aan te bieden een ticket aan te maken – en dat is ook wat de agent doet. Een kleiner lokaal model is geen vrijbrief om meer te verzinnen, omdat het model sowieso niet de bron van de waarheid is. Het ophalen van informatie is dat wel.

Ook de overdracht verandert niet. Wanneer de agent niet kan helpen, legt hij het onderwerp, de contactpersoon en de prioriteit vast terwijl de beller nog aan de lijn is, maakt hij het ticket aan en voegt hij het gespreksverslag toe.

Dat is het nut van het bouwen van beide niveaus volgens één gedragspatroon. Het schakelen tussen beide is een kwestie van configuratie, geen herbouw.

Dus welke moet je gebruiken?

Eerlijk gezegd: begin met de gehoste versie.

De gehoste laag is de snelste manier om erachter te komen of een agent op je lijn überhaupt nuttig is, en dat is een productkwestie, geen infrastructuurkwestie. Onze eigen console schatte de kosten op ongeveer zes cent per gespreksminuut. Voor een bedrijf dat enkele tientallen gesprekken per dag ontvangt, is dat niet de kostenpost die als eerste geoptimaliseerd moet worden.

Zelfhosting verdient zich terug bij twee drempels, en dat zijn verschillende drempels.

De eerste is het volume. De gemeten kosten stijgen naarmate het gebruik toeneemt, de hardwarekosten niet, dus er is een omslagpunt waarop de GPU simpelweg goedkoper is. Waar dat punt ligt, hangt af van uw gespreksvolume, en u moet dit meten in plaats van er zomaar vanuit te gaan. Vocale meet de kosten per model en per organisatie juist om deze reden: de keuze tussen een API en een kaart moet een beslissing zijn op basis van cijfers, geen discussiepunt.

De tweede drempel heeft niets met geld te maken. Sommige bedrijven mogen gespreksaudio, transcripties of interne documenten niet naar een derde partij sturen, vanwege regelgeving, een klantcontract of een beleid. Voor hen is de lokale versie geen optimalisatie. Het is de enige versie van dit product die mag bestaan, en het ontbreken van een prijs per minuut maakt de gehoste versie onaanvaardbaar.

Het is de moeite waard om deze twee drempels apart te bekijken wanneer je een leverancier van spraak-AI evalueert. Een platform dat alleen metered inference aanbiedt, heeft stilletjes besloten dat de tweede drempel niet op jou van toepassing is.

De korte versie

Lokale inferentie op een helpdesk is tegenwoordig praktisch haalbaar. De modellen zijn goed genoeg en de tooling (vLLM, Faster-Whisper, XTTS-v2, LiveKit) is zo ver ontwikkeld dat de pijplijn geen risico vormt.

De risico’s zijn juist diegene die niemand demonstreert: onderbroken worden, aanwezig klinken terwijl je nadenkt, getallen hardop voorlezen zoals een mens dat doet, en weten hoeveel gesprekken er daadwerkelijk op een kaart passen. Zorg dat je die onder de knie hebt, en de keuze tussen een gehoste API en je eigen GPU wordt wat het altijd al had moeten zijn: een post op de begroting in plaats van een beperking op wat je mag bouwen.

  • Local LLM
  • Voice AI
  • Self-hosted
  • RAG
  • vLLM
  • LiveKit
  • Support automation