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
- 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.
- Bewust géén end-to-end spraakmodel: Nederlands + kennisbank-verankering + AI Act-logging wegen zwaarder dan de kortere kale latency van audio-native modellen.
- 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.
- Live gemeten kosten: €0,03–0,32 per gespreksminuut, in lijn met wat concurrerende platforms vragen.
- 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.
- 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.
- 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.
- 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".
- Het project ligt sinds 24-08-2026 stil (laatste commit); alle cijfers zijn dus een momentopname van drie weken oud, geen doorlopend bewezen systeem.
- 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
- 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.
- Neem het meetregime-patroon over: elke stap i.p.v. "het voelt goed" een p50/p95-latency en een €/minuut-log, per gesprek/sessie.
- Kopieer de prompt-architectuur: een korte, statische persona-tekst + losse, per-beurt ingespoten context — niet één megaprompt met alle huisfeiten.
- 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).
- 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.
- 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".
- 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
- Welk stemmodel/TTS-aanbieder past bij Mi casa's merk (warm, verfijnd, nooit makelaarstaal) en budget, als tekst-eerst met TTS-optie het uitgangspunt wordt?
- Is er tijd/budget om vóór 1 november zelf een latency- en kostenmeting te draaien zoals de telefoonagent dat deed, of moet de pitch zonder eigen cijfer (dus zonder harde uitspraken over snelheid/kosten)?
- Wordt de pitch-demo op een iOS-apparaat getoond, en zo ja: in Safari-tab of als geïnstalleerde PWA (bepaalt of Web Speech API sowieso een optie is)?
Bronnen
Mac z (ssh z, ~/Developer/telefoonagent, alleen gelezen):
docs/architectuurnotitie.md(regels 6, 46-108, 144-160)docs/implementatieplan.md:233docs/plan-gespreksregie.md(regels 15-31, 99-107)docs/notitie-stiltes-en-vulling.md(regels 9-70)docs/derde-versie/LEESMIJ.md,02-doelarchitectuur.md,03-migratiepad.md,04-open-beslissingen.mddocs/integratie-genui.md(regels 43-75)DECISIONS.md(regels 110-201)HANDOVER.mdserver/brein/context.py(regels 1-13, 47, 79-80, 158)server/beheer.py(regels 181-573)config.yaml(regels 81-209)data/logs/kosten.jsonlgit log(laatste commit 24-08-2026)
Live app (alleen GET):
- https://z.tail188fd9.ts.net/ (titel "Telefoonagent — xel")
- https://z.tail188fd9.ts.net/beheer/status
- https://z.tail188fd9.ts.net/beheer/kosten-per-dag
Web: