Onderzoek 2 — de Telefoonagent van xel en wat hij leert over stem

Bron: ssh z, map ~/Developer/telefoonagent (alleen gelezen, niets gewijzigd, niet gebeld). Live app bevraagd met alleen GET: https://z.tail188fd9.ts.net/ en /beheer/status, /beheer/kosten-per-dag.

Samenvatting

  1. De telefoonagent is een werkend Nederlandstalig spraaksysteem voor xel.nl-klanten: cascade STT→LLM→TTS (Claude Sonnet 5 + ElevenLabs Flash + Scribe), losse Pipecat-diensten op Mac z, alleen bereikbaar via tailnet.
  2. Bewust géén end-to-end spraakmodel: Nederlands + kennisbank-verankering + AI Act-logging wegen zwaarder dan de kortere kale latency van audio-native modellen.
  3. Het latency-doel (<1,5 s spraak-tot-spraak) is niet gehaald (p50 1,9–2,3 s); opgelost door de waargenomen wachttijd te verkorten met een "reflexlaag" (~400 ms), niet door het brein sneller te maken.
  4. Live gemeten kosten: €0,03–0,32 per gespreksminuut, in lijn met wat concurrerende platforms vragen.
  5. De persona zit vast in één statische systeemprompt met harde stijlregels en een expliciete "ik weet het niet"-regel; kennis wordt per beurt apart ingespoten, niet in de prompt gebakken.
  6. Het "derde-versie"-dossier is nadrukkelijk géén bouwopdracht maar een architectuurstudie naar één platform voor spraak + leersystemen + generatieve UI; het kernbesluit (welke lijn leidend wordt) staat nog open.
  7. De beste, best onderbouwde vondst tegen "dode stilte" is gecontextualiseerde vulling (een parafrase van de vraag, geen generiek "uhm"); dat is letterlijk toepasbaar op een pratend huis.
  8. iOS Safari is de harde grens: Web Speech API werkt niet in een als-app-geïnstalleerde PWA, en audio/microfoon vereisen altijd een user-gesture — precies waarom xel zelf een aparte native iOS-app bouwt in plaats van "alleen een web-app".
  9. Het project ligt sinds 24-08-2026 stil (laatste commit); alle cijfers zijn dus een momentopname van drie weken oud, geen doorlopend bewezen systeem.
  10. Herbruikbaar: het meetregime, de prompt-architectuur, de vulling-vondst. Niet herbruikbaar: de telefonie-infra (CallKit, WebRTC-belervaring) en de xel-specifieke kennisbank.

Bevindingen

1. Wat de telefoonagent is en doet

Een Nederlandstalige spraakassistent voor de kennisbank van xel.nl (hosting, domeinen, WordPress), bedoeld als web-app in de stijl van genui en later als iOS-app (docs/architectuurnotitie.md:6). Keten per beurt: microfoon → SmallWebRTC → gespreksregisseur; parallel STT (ElevenLabs Scribe v2 Realtime, config.yaml:97-99), smart-turn beurtdetectie, breinrouter (Claude Sonnet 5, config.yaml:123-127) gevoed door een lokale kennisindex (BGE-M3 + reranker in LanceDB), en TTS (ElevenLabs Flash v2.5, eigen getrainde stem, config.yaml:146-148). D1: bewust een cascade-pijplijn, géén end-to-end spraakmodel (Moshi, Qwen3-Omni) — reden: alleen een tekstlaag geeft een aanhaakpunt voor RAG, talker-contract en AI Act-logging, en die audio-native alternatieven spreken amper Nederlands (docs/architectuurnotitie.md:46-63). Framework: Pipecat + SmallWebRTC, elke provider verwisselbaar achter config.yaml (D5). Voor eigen gebruik koos het team een native iOS-app met CallKit boven SIP/telefonie, uitdrukkelijk óók omdat "alleen een web-app… de belervaring mist" (D6, docs/architectuurnotitie.md:90-108).

Gemeten (nulmeting, DECISIONS.md:110-135): 43–46/50 vragen correct (criterium 40), spraak-tot-spraak p50 2335 ms tegen doel 1500 ms — niet gehaald (DECISIONS.md:124). Na twee ingrepen (opgezochte passages uit de gespreksgeschiedenis halen; een reflexlaag met veertien vooraf gerenderde clips in de eigen stem, hoorbaar binnen ~400 ms terwijl het brein nadenkt): 45/50, eerste zin p50 1466 ms, spraak-tot-spraak p50 1902 ms — kaal doel nog steeds niet gehaald, maar de waargenomen wachttijd wordt de reflexlaag (DECISIONS.md:162-201). Live status vandaag (GET /beheer/status): 52 gesprekken, €1,43 totaal, €0,0311/min gemiddeld; individuele gesprekken in het kostenlog liggen tussen €0,069 en €0,32/min (data/logs/kosten.jsonl), in lijn met de raming €0,08–0,14/min (docs/implementatieplan.md:233). Gespreksregie: smart-turn (lokaal, ~200–400 ms i.p.v. een stiltetimer van ~800 ms), stop_secs: 0.9 (config.yaml:174), onderbrekingen verwerkt door alles stroomafwaarts te annuleren (D4/D9); buiten kennisbank of bij onzekerheid belt een collega terug (kb/eigen/gedrag-terugbellen.md). Voorbehoud: laatste commit is 24-08-2026 (git log) — een architectuurbewezen systeem, geen continu gemonitord productiesysteem.

2. Persona en toon

Volledig vastgelegd in server/brein/context.py:47 (SYSTEEMPROMPT): korte eerste zin (max. acht woorden), 1–2 zinnen per beurt, geen opsommingstekens of markdown (het wordt hardop voorgelezen), bedragen en telefoonnummers uitgeschreven, één stap tegelijk bij instructies. Het "nooit-weten"-gedrag staat expliciet: "Staat het antwoord daar niet in, zeg dan gewoon dat je het niet weet... Verzin nooit prijzen, voorwaarden of technische stappen" (server/brein/context.py:79-80). Verboden: vragen buiten xel/hosting/ domeinen worden vriendelijk afgewezen. Kennis wordt nooit in de systeemprompt gebakken — die is bewust klein (~600 tokens gedrag) en de kennisbank komt per beurt apart binnen (server/brein/context.py:1-13, motivatie D12 heropend nadat xel.nl 436k tokens bleek, 4× het cachebudget). Regieknoppen (toon, beknoptheid, wedervraag-altijd/nooit, bevestigingsvariant) zijn bewust promptbouwstenen, geen vrij-tekstveld — "geen vrije prompt-editor als primaire interface: dan wordt de prompt een onbeheerbare lap tekst" (docs/plan-gespreksregie.md:29-31); instellingen leven in data/*.json en gelden direct, zonder herstart. Elke wijziging wordt gemeten via een stijl_hash in het gesprekslog (docs/plan-gespreksregie.md:101-107).

3. Het derde-versie-plan

Nadrukkelijk geen bouwopdracht: "er wordt niet gebouwd zonder expliciet akkoord van de eigenaar" (docs/derde-versie/LEESMIJ.md:3-4). Doel: één platform waarin de telefoonagent (spraak), leersystemen en generatieve interfaces van genui samenkomen. Vijf contracten moeten blijven staan ongeacht welke optie wint: RTVI/SmallWebRTC tussen client en spraakserver, een bron/tool-contract, een gedeelde UI-taal, de beheer-API (server/beheer.py:181-573, 21 routes, vrijwel uitsluitend GET) en het meetregime (docs/derde-versie/02-doelarchitectuur.md, tabel §1). Drie opties: (A) genui-tel als leidende belevingslaag met de telefoonagent als toolset + "Belkaart"-component, (B) hybride met de Luminous-motor erachter, (C) federatie (status quo). Aanbeveling: A als leidend scenario, met Luminous-patronen (Voorstel-patroon, assertions, designgovernance) als uitbouwpad, definitief te maken ná bronborging (docs/derde-versie/02-doelarchitectuur.md:200-213). Al bewezen: de telefoonagent-toolset is als voorstel uitgewerkt (tel_status, tel_gesprekken, tel_kosten, enz. — alle GET, docs/integratie-genui.md:43 e.v.); schrijfacties uitdrukkelijk pas via een bevestigingspatroon. Open: B0 (welke lijn), B1 (naam/domeinen), B4/B5 (auth/toegang, pas bij het servermoment), B7 (genui-tel hangt aan @ai-sdk/openai, telefoonagent en Luminous aan Anthropic) (docs/derde-versie/04-open-beslissingen.md). Besloten: alles draait voorlopig laptop-first, een server komt pas later.

4. Wat dit betekent voor "het huis praat"

Cascade-latency van 1,9–2,3 s is met een cloudbrein "niet realistisch" gebleken, zelfs met een goed getunede kennisindex, een smalle kennisbank (één site) en een gespecialiseerd team (DECISIONS.md:132-134). Voor "je huis praat met je" op 1 november is dat een directe waarschuwing: verwacht geen sub-seconde spraak-naar-spraak zonder een vergelijkbare reflexlaag-investering (veertien eigen stem-clips, een varianten-systeem, weken tuning) — die past niet in de tijd tot de pitch.

Voor de browser/PWA-kant: Web Speech API (webkitSpeechRecognition) wordt door Safari ondersteund sinds iOS/iPadOS 14.5, maar werkt niet in een standalone PWA (toegevoegd aan het beginscherm) — alleen in een gewoon Safari-tabblad, en altijd pas na een user-gesture. Audio afspelen en getUserMedia vereisen op iOS Safari eveneens altijd een directe gebruikersactie; geen autoplay, geen stille microfoon-start. Dat is precies de reden waarom xel zelf een losstaande native iOS-app met CallKit bouwde in plaats van "alleen een web-app" (D6, docs/architectuurnotitie.md:90-108) — een gespecialiseerd team liep tegen dezelfde muur op. De OpenAI/Gemini Realtime-route (WebRTC/websocket, geen cascade) heeft dezelfde iOS-PWA-beperking voor microfoon/autoplay, plus vergelijkbare kosten: ~$0,06–0,11/min op gpt-realtime-2.1 (zie Bronnen) — dezelfde orde van grootte als wat de telefoonagent zelf meet (€0,07–0,32/min).

5. Herbruikbaar versus niet herbruikbaar

Herbruikbaar: het meetregime (latency per schakel, kosten per gesprek in één plat JSONL-record, docs/architectuurnotitie.md §Datamodel); de prompt-architectuur (kleine statische systeemprompt + los ingespoten context); de vondst dat gecontextualiseerde vulling (een parafrase van de vraag) merkbaar beter werkt dan generieke vulwoorden — direct bruikbaar voor een huis dat een moment nadenkt (docs/notitie-stiltes-en-vulling.md:22-47); het "één onbewezen component tegelijk"-werkregime en presets-boven-losse-knoppen voor stijlsturing. Niet herbruikbaar: de telefonie/CallKit-infra (D6), de xel-specifieke kennisbank en het RAG-datamodel, en het hele genui/derde-versie-vraagstuk — een intern platformbesluit van een ander project zonder relevantie voor Mi casa's eigen PWA-stack (Vite + Svelte + Hono).

Wat dit betekent voor Mi casa

Het grootste risico voor 1 november is niet "kan een LLM converseren" maar "kan een iOS PWA op het podium betrouwbaar luisteren en meteen antwoorden, zonder geweigerde microfoon-permissie of rare stilte". xel's eigen, gespecialiseerde team had weken nodig voor een reflexlaag en een aparte native app om dát op te lossen, en haalde het kale latency-doel zelfs toen niet. Bouw "met je huis praten" daarom eerst als tekstgesprek met een uitgesproken antwoord (TTS-only, geen live STT): dat omzeilt de iOS-PWA-microfoonbeperking en het latency-risico, houdt kosten beheersbaar en werkt gewoon na een tik op "afspelen". Behandel live spraakinvoer als bewezen stretch-goal ná de kern staat, met dezelfde meetdiscipline (p50/p95, €/minuut, bron per cijfer) die de telefoonagent al toont.

Aanbevelingen

  1. Kies voor de pitch tekst-eerst met voorgelezen antwoord (TTS-only); bouw live-STT-gesprek pas als bewezen stretch-goal, getest ruim vóór 1 november.
  2. Neem het meetregime-patroon over: elke stap i.p.v. "het voelt goed" een p50/p95-latency en een €/minuut-log, per gesprek/sessie.
  3. Kopieer de prompt-architectuur: een korte, statische persona-tekst + losse, per-beurt ingespoten context — niet één megaprompt met alle huisfeiten.
  4. Neem het "nooit-weten"-patroon letterlijk over voor het huis: expliciet "dat weet ik niet, ik vraag het na" i.p.v. verzinnen, vooral bij prijzen/waarde (raakt harde grens 7).
  5. Test microfoon-permissie en autoplay expliciet op het pitch-apparaat (iOS Safari, wel/niet standalone) vóór 1 november — dit is een bekende faalmodus, geen randgeval.
  6. Gebruik "gecontextualiseerde vulling" (een korte parafrase van de vraag in dezelfde stem) als het huis een moment moet nadenken, in plaats van stilte of een generiek "uhm".
  7. Laat het derde-versie-dossier en de genui/Luminous-platformkeuze buiten scope voor Mi casa — het is een intern xel/vleugje-platformbesluit zonder overlap met de Svelte/Hono-stack.

Open vragen

Bronnen

Mac z (ssh z, ~/Developer/telefoonagent, alleen gelezen):

Live app (alleen GET):

Web: