Bot di chiamata Graph
Panoramica
Questo approccio rende l’agente un’identità Teams chiamabile. Un utente lo cerca per nome e lo chiama in 1:1, e l’agente risponde in tempo reale — senza numero di telefono, PSTN o Communications Credits. È l’unico approccio chiamabile per nome e il più complesso da gestire.
Usa un bot Microsoft Graph per contenuti multimediali in tempo reale (la piattaforma di chiamata Cloud Communications). L’SDK multimediale (Microsoft.Skype.Bots.Media) funziona solo con .NET su Windows Server — non esiste un percorso Linux o non .NET per l’audio raw nelle chiamate Teams.
Questo è l’unico approccio chiamabile per nome all’interno di Teams. Per una configurazione più semplice, usa la scheda widget, oppure ACS se vuoi specificamente un numero di telefono.
Come funziona
Il bot risponde con contenuti multimediali ospitati dall’applicazione, riceve 50 frame audio/sec (PCM 16 kHz da 20 ms), li inoltra all’agente ElevenLabs tramite WebSocket e trasmette l’audio dell’agente nella chiamata.
Requisiti
- Una registrazione Azure Bot + app (registrazione dell’app Entra).
- Autorizzazioni dell’applicazione Graph con consenso dell’amministratore:
Calls.AccessMedia.All(contenuti multimediali raw) piùCalls.Initiate.All. - Una VM Windows Server (≥ 2 core fisici — ad esempio
Standard_D4s_v3) con IP pubblico e porte multimediali aperte. - Un certificato TLS firmato da una CA su un FQDN pubblico per l’endpoint di contenuti multimediali/segnalazione (la piattaforma multimediale rifiuta i certificati autofirmati).
- Un agente ElevenLabs impostato su PCM 16000 Hz su entrambi i lati: formato di output TTS nella scheda Voce, formato audio di input utente nella scheda Avanzate.
Un D2s_v3 (2 vCPU = 1 core fisico) non funziona e restituisce MediaPlatform needs a system with at least 2 cores. Usa una dimensione con ≥ 2 core fisici (ad esempio D4s_v3).
Autorizzazioni e ruoli
Passaggio 1 — Registra il bot + autorizzazioni Graph
Crea una registrazione dell’app e un Azure Bot associato, quindi concedi e approva le autorizzazioni di chiamata (per il consenso ti servono i ruoli Amministratore globale / Amministratore ruoli con privilegi):
Concedi i due ruoli dell’applicazione Graph e il consenso dell’amministratore (richiede Amministratore globale / Amministratore ruoli con privilegi), quindi verifica che le assegnazioni siano state applicate:
Se admin-consent restituisce Consent validation failed, concedi invece i ruoli dell’app direttamente nel service principal:
Nel portale, verifica nell’interfaccia di amministrazione Entra in Registrazioni app → la tua app → Autorizzazioni API: entrambe le autorizzazioni dovrebbero mostrare Concesso con segni di spunta verdi.

Passaggio 2 — Configura la VM Windows, il certificato e le porte
Sulla VM (il codice nativo della piattaforma multimediale li richiede — Windows Server non li include per impostazione predefinita):
Apri le stesse porte nel firewall di Windows e annota l’impronta digitale del certificato — il bot associa Kestrel (443 + una porta per le notifiche) e la piattaforma multimediale (8445) al certificato.
L’FQDN *.cloudapp.azure.com della VM funziona con un certificato Let’s Encrypt — non serve un dominio
separato.
Passaggio 3 — Compila ed esegui il bot
Parti da PublicSamples/EchoBot di microsoft-graph-comms-samples di Microsoft — usa net6.0 e viene compilato con l’SDK .NET (non servono Visual Studio Build Tools):
Configura la sezione AppSettings di appsettings.json con AadAppId, AadAppSecret, ServiceDnsName/MediaDnsName (l’FQDN della VM), CertificateThumbprint e le porte (chiamate 443, notifiche 9441, contenuti multimediali 8445). Aggiungi due impostazioni per il bridge ElevenLabs indicato sotto: ElevenLabsAgentId e ElevenLabsOrigin (wss://api.elevenlabs.io, o il tuo host di residenza). Eseguilo come attività/servizio pianificato di Windows, così rimane attivo dopo i riavvii.
Il limite di tempo di esecuzione predefinito (72 ore) dell’Utilità di pianificazione termina silenziosamente le attività a lunga esecuzione — un bot avviato all’avvio si arresta tre giorni dopo e le chiamate non riescono con “we couldn’t connect you”. Disabilita il limite e aggiungi il riavvio in caso di errore:
EchoBot standard si arresta in modo anomalo per una chiamata effettuata sulla porta standard 443: HttpHelpers.SetAbsoluteUri
chiama req.Host.Port.Value, che è null quando l’header Host non ha una porta esplicita. Correggilo in
req.Host.Port ?? (req.IsHttps ? 443 : 80).
Sostituisci l’eco con ElevenLabs
Il collegamento audio di EchoBot è semplice: SpeechService.AppendAudioBuffer(in) e un evento OnSendMediaBufferEventArgs(out). Sostituisci il corpo Azure Speech con un bridge WebSocket dell’agente ElevenLabs che mantiene la stessa interfaccia:
Entrambi i lati sono PCM mono a 16 kHz, quindi è un passthrough base64 — imposta l’agente su pcm_16000. In caso di interruption di ElevenLabs (barge-in), il bridge genera FlushMedia; collegalo al tuo stream multimediale affinché elimini eventuali AudioMediaBuffer in coda, altrimenti l’agente continua a parlare sopra il chiamante. Il riferimento completo dei messaggi è nella documentazione WebSocket. La chiusura al termine della chiamata e il trasferimento assistito sono descritti nelle sezioni seguenti.
L’URL in Connect() raggiunge un agente pubblico. Per un agente privato, richiedi lato server un
URL firmato a breve scadenza — GET /v1/convai/conversation/get-signed-url?agent_id=... con la tua chiave API
— e connettiti invece all’URL restituito. Per la residenza dei
dati, imposta ElevenLabsOrigin sul tuo host di residenza
(wss://api.eu.residency.elevenlabs.io, .in. o .sg.) — le richieste di URL firmati usano l’host
https:// corrispondente.
Passaggio 4 — Rendilo chiamabile in Teams
-
Abilita le chiamate nel canale Teams dell’Azure Bot e imposta il webhook di chiamata su
https://YOUR_FQDN/api/calling:Nel portale si trova nella risorsa Azure Bot → Canali → Microsoft Teams → scheda Chiamate:

Azure Bot → Canali — il canale Microsoft Teams connesso 
Canale Microsoft Teams → Chiamate — chiamate abilitate con il webhook del bot -
Crea un manifest dell’app Teams con
bots[0].supportsCalling: truee l’ID app del bot, quindi caricalo localmente (App → Gestisci le tue app → Carica un’app personalizzata), oppure pubblicalo a livello di organizzazione senza l’interfaccia:New-TeamsApp -DistributionMethod organization -Path ./bot-app.zip(modulo PowerShell MicrosoftTeams).
Cerca l’app per nome in Teams e chiamala — il bot risponde e l’agente ElevenLabs parla.

Per le chiamate 1:1 per nome non servono né un numero di telefono né un account risorsa — sono necessari
solo per le chiamate PSTN in ingresso. Calls.AccessMedia.All abilita il bridge audio raw.
Chat di testo (stesso bot)
Lo stesso Azure Bot può anche rispondere ai messaggi di testo in Teams — gli utenti possono quindi chiamare l’agente oppure chattare con lui. Le chiamate e la messaggistica sono canali indipendenti del bot: il webhook di chiamata gestisce la voce, mentre un endpoint di messaggistica Bot Framework (/api/messages) gestisce la chat.

Indirizza l’endpoint di messaggistica del bot all’host che lo serve (il bot multimediale o qualsiasi altro servizio — non deve essere necessariamente la VM Windows):
Implementa l’endpoint con l’SDK Bot Framework e inoltra ogni messaggio all’agente in modalità testo tramite lo stesso WebSocket di conversazione usato per la voce — invia un evento user_message, leggi l’evento agent_response. Prima abilita il campo primo messaggio nelle impostazioni delle sostituzioni dell’agente — il codice seguente lo sostituisce con un valore vuoto, affinché la risposta risponda al messaggio dell’utente anziché al saluto dell’agente:
Registralo nel modo standard (un CloudAdapter, il bot tramite AddTransient<IBot, ChatBot>() e un controller /api/messages) e aggiungi gli ambiti di chat alla voce del bot nel manifest:
Lo snippet apre una nuova conversazione per ogni messaggio, quindi ogni turno è indipendente. Per la
memoria della chat, mantieni un WebSocket aperto per ogni conversation.id di Teams (riutilizzalo tra i turni) ed elimina le
sessioni inattive — l’agente ricorderà quindi i messaggi precedenti in quella chat. La sostituzione first_message
deve essere abilitata nelle impostazioni delle
sostituzioni dell’agente — il server
chiude la conversazione se viene inviata una sostituzione non consentita. Se non puoi abilitarla, ometti la
sostituzione e ignora invece il primo agent_response di ogni sessione (il saluto), quindi restituisci
quello successivo.
Se le risposte della chat non arrivano mai, abilita l’evento client
agent_response nelle impostazioni Avanzate dell’agente — le risposte di testo vengono recapitate tramite quell’evento.
Fine della chiamata
Quando ElevenLabs termina la conversazione (il relativo strumento Termina chiamata chiude il WebSocket), riaggancia la parte Teams:
Trasferimento assistito a una persona
L’agente attiva uno strumento client personalizzato transfer_to_human; il bot invita un utente Teams nella chiamata in corso (aggiunta consultiva), quindi si ritira:
Il trasferimento consultivo (replacesCallId) richiede che entrambe le parti siano utenti Teams nello **stesso
tenant **; le destinazioni di trasferimento PSTN richiedono un’istanza dell’applicazione. Per informare prima la persona, passa un
parametro reason dall’agente e riproducilo alla persona prima di collegarla.
Risoluzione dei problemi
MediaPlatform needs a system with at least 2 cores
MediaPlatform needs a system with at least 2 cores
La VM ha un solo core fisico. Ridimensionala a ≥ 2 core fisici (ad esempio D4s_v3) e riavviala.
Unable to load DLL 'NativeMedia'
Unable to load DLL 'NativeMedia'
Installa VC++ Redistributable (vcredist140) e la funzionalità Windows Server-Media-Foundation,
quindi riavvia il bot.
La chiamata in ingresso restituisce 500 / non si connette
È il bug della porta null di EchoBot su 443 — correggi HttpHelpers.SetAbsoluteUri (vedi il passaggio 3). Verifica anche che
il certificato sia firmato da una CA e raggiungibile sulla porta 443.
Chiamando il bot viene visualizzato 'we couldn't connect you'
Verifica che le chiamate siano abilitate nel canale Teams con il webhook /api/calling corretto, che l’autorizzazione
Graph Calls.AccessMedia.All abbia ricevuto il consenso e che le porte 443/8445/9441 siano aperte sia nel
NSG sia nel firewall di Windows. Se le chiamate funzionavano e hanno smesso di funzionare, verifica che il processo del bot
sia ancora in esecuzione sulla VM — il limite di esecuzione predefinito di 72 ore dell’Utilità di pianificazione lo termina alcuni
giorni dopo l’avvio (vedi l’avviso nel passaggio 3).