Assistente vocale AI
In questo progetto ho sviluppato, end-to-end, un agente telefonico in lingua italiana che risponde alle chiamate al posto dell’utente, dialoga con il chiamante in linguaggio naturale e modula il proprio comportamento congedandosi in modo educato ma asciutto dai chiamanti con pattern da spam (la valutazione se “spam” deriva dal proprio giudizio e dai temi segnalati dall’utente). Notifiche in tempo reale sul contenuto della conversazione in corso vengono trasmesse a un’app mobile abbinata, dove l’utente può lasciar proseguire l’agente, prenderne il posto o riattaccare; dopo la chiamata partono trascrizione, classificazione ed estrazione di entità; all’utente vengono inoltre proposte azioni strutturate, come eventi del calendario, da approvare.
Stack: Python, FastAPI, OpenAI Realtime API, Silero VAD (PyTorch), Twilio, React Native, Redis, Loki, Grafana, dietro una configurazione Docker irrobustita e un reverse proxy Caddy.
Cosa lo ha reso interessante:
Due motori di conversazione intercambiabili dietro un’interfaccia unificata: OpenAI Realtime (audio to audio, naturale e in grado di comprendere e rispondere contestualmente all’intonazione della voce umana) e una pipeline STT+LLM+TTS personalizzata (4 volte più economica, più complessa e potenzialmente fragile, a causa del bisogno di gestire i turni di conversazione, l’eco, pause e speech detection, ma anche più personalizzabile). I due approcci sono selezionabili per deployment tramite variabile d’ambiente e con interruttore per utente. Entrambi condividono lo stesso orchestratore di chiamata, la pipeline post-chiamata e il registro dei costi per singola chiamata.
Il comportamento audio ha richiesto una messa a punto seria: gestione dei turni, soppressione dell’eco, rilevamento del barge-in, soglie del VAD. Tutto diagnosticato con test end-to-end su chiamate telefoniche reali, non su benchmark sintetici. Il DSP sottostante (Silero VAD tramite PyTorch, soppressione dell’eco basata su FFT, finestre di barge-in a doppia soglia) è reale.
Ho puntato molto sull’astrarre i tre costrutti principali:
- Provider di AI, per evitare lock-in ad un solo vendor, specialmente in uno scenario dinamico ed in evoluzione come quello audio-audio
- Provider di telefonia, per lo stesso motivo, evitare lock-in e offrire un’interfaccia più astratta e naturale al resto del codice
- Gestione della conversazione, per consentire la convivenza delle due modalità di gestione della conversazione (realtime vs stt/llm/tts)
Ho prestato molta attenzione all’hardening e alla sicurezza ad un livello pronto per un rilascio in produzione, nonostante sia un repo sviluppato solo da me: filesystem Docker in sola lettura, capabilities ridotte, container non-root, rate limiting Redis multilivello (IP/chiamata/telefono/utente/dispositivo/globale) con finestre scorrevoli e circuit breaker. CI/CD orientata alla sicurezza: Bandit, Semgrep, Safety, pip-audit, Trufflehog, Trivy, Codecov.
Trasparenza richiesta dall’AI Act europeo applicata nel saluto iniziale e al gate audio. Conservazione dell’audio per 7 giorni, conforme al GDPR.