Intégration téléphonique (Twilio, etc.)
Intégration téléphonique (Twilio, etc.)
Un agent vocal devient vraiment utile quand il est accessible par téléphone. Les plateformes de téléphonie cloud comme Twilio, LiveKit et Daily permettent de connecter la Realtime API au réseau téléphonique mondial. Un utilisateur appelle un numéro, et votre agent répond en temps réel.
Architecture téléphonique
L’intégration téléphonique ajoute une couche entre le réseau téléphonique et la Realtime API :
Appel entrant → Twilio
L'utilisateur appelle un numéro de téléphone. Twilio reçoit l'appel et envoie un webhook à votre serveur.
Votre serveur → Media Stream
Votre serveur répond avec des instructions TwiML qui ouvrent un Media Stream WebSocket bidirectionnel.
Media Stream ↔ Realtime API
Votre serveur relaie l'audio entre le Media Stream Twilio et la Realtime API OpenAI.
Réponse vocale → Utilisateur
L'audio de réponse de la Realtime API est renvoyé via Twilio au téléphone de l'utilisateur.
Implémentation avec Twilio
Webhook d’appel entrant
Quand un appel arrive, Twilio envoie une requête HTTP à votre serveur. Vous répondez avec du TwiML qui ouvre un Media Stream :
from fastapi import FastAPI, Request
from fastapi.responses import Response
app = FastAPI()
@app.post("/incoming-call")
async def incoming_call(request: Request):
"""Webhook Twilio pour les appels entrants."""
twiml = """<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Say language="fr-FR">
Bienvenue chez Corsen AI. Connexion à votre assistant vocal.
</Say>
<Connect>
<Stream url="wss://votre-serveur.com/media-stream">
<Parameter name="caller" value="{caller_number}" />
</Stream>
</Connect>
</Response>"""
form_data = await request.form()
caller = form_data.get("From", "inconnu")
twiml = twiml.replace("{caller_number}", caller)
return Response(content=twiml, media_type="application/xml")
Le pont Media Stream ↔ Realtime API
Le serveur WebSocket reçoit l’audio de Twilio et le relaie à la Realtime API :
from fastapi import WebSocket
import websockets
import json
import base64
import audioop
OPENAI_API_KEY = "sk-..."
REALTIME_URL = "wss://api.openai.com/v1/realtime?model=gpt-4o-realtime-preview"
@app.websocket("/media-stream")
async def media_stream(twilio_ws: WebSocket):
"""Pont entre Twilio Media Stream et la Realtime API."""
await twilio_ws.accept()
# Connexion à la Realtime API
headers = {
"Authorization": f"Bearer {OPENAI_API_KEY}",
"OpenAI-Beta": "realtime=v1"
}
async with websockets.connect(REALTIME_URL, extra_headers=headers) as openai_ws:
await openai_ws.recv() # session.created
# Configurer la session
await openai_ws.send(json.dumps({
"type": "session.update",
"session": {
"modalities": ["text", "audio"],
"instructions": "Vous êtes un assistant téléphonique. "
"Répondez en français, de manière concise.",
"voice": "nova",
"input_audio_format": "g711_ulaw",
"output_audio_format": "g711_ulaw",
"turn_detection": {
"type": "server_vad",
"threshold": 0.5,
"silence_duration_ms": 600
}
}
}))
await openai_ws.recv()
stream_sid = None
async def twilio_to_openai():
nonlocal stream_sid
async for message in twilio_ws.iter_text():
data = json.loads(message)
if data["event"] == "media":
audio_b64 = data["media"]["payload"]
await openai_ws.send(json.dumps({
"type": "input_audio_buffer.append",
"audio": audio_b64
}))
elif data["event"] == "start":
stream_sid = data["start"]["streamSid"]
async def openai_to_twilio():
async for message in openai_ws:
event = json.loads(message)
if event["type"] == "response.audio.delta":
await twilio_ws.send_json({
"event": "media",
"streamSid": stream_sid,
"media": {
"payload": event["delta"]
}
})
await asyncio.gather(twilio_to_openai(), openai_to_twilio())
Format audio G.711
La téléphonie utilise le format G.711 μ-law (8 kHz, 8 bits). La Realtime API supporte ce format nativement avec g711_ulaw, ce qui évite toute conversion côté serveur.
Alternatives à Twilio
| Plateforme | Force | Intégration Realtime API |
|---|---|---|
| Twilio | Écosystème complet, numéros dans 100+ pays | Media Streams WebSocket natif |
| LiveKit | Open source, faible latence, WebRTC | Plugin OpenAI officiel |
| Daily | Simple, hébergé, bon pour prototypage | SDK Pipecat avec connecteur Realtime |
Points clés à retenir
- Twilio Media Streams permet de connecter un appel téléphonique à un WebSocket bidirectionnel
- Le serveur fait office de pont entre le Media Stream Twilio et la Realtime API OpenAI
- Le format G.711 μ-law est natif en téléphonie et supporté directement par la Realtime API
- LiveKit (open source, WebRTC) et Daily (hébergé) sont des alternatives viables à Twilio
- L’architecture est toujours la même : téléphonie → votre serveur → Realtime API