GPT-Live Voix en temps réel
Effectuez des interactions vocales full-duplex et gérez les événements de session via GPT-Live WebSocket.
GPT-Live fournit des interactions vocales full-duplex sur une connexion WebSocket persistante. Une même session permet d’envoyer de l’audio d’entrée, de recevoir la voix et les transcriptions, de mettre à jour sa configuration et de conserver le contexte sur plusieurs tours. Le modèle actuellement référencé est gpt-live-1 ; sa disponibilité réelle dépend du compte actuel et du catalogue de modèles.
wss://api.agtcloud.cn/v1/live/sessionsAuthentification et connexion
Les applications serveur utilisent une clé API Bearer dans les Headers du handshake WebSocket. Le modèle n’est pas placé dans les paramètres de requête de l’URL : il est configuré dans le premier événement session.start après connexion.
| Élément | Valeur |
|---|---|
| Adresse de connexion | wss://api.agtcloud.cn/v1/live/sessions |
| Authorization | Bearer your-api-key |
| Emplacement du modèle | session.start.session.model |
| Audio d’entrée | Blocs audio PCM bruts encodés en Base64 |
Conservez les clés API de longue durée uniquement sur un serveur de confiance. Cette page couvre uniquement cette route WebSocket, pas WebRTC, SIP, sideband, les forks de session ni le téléchargement d’enregistrements.
Déroulement de la session
- Établissez une connexion WebSocket avec une clé API Bearer.
- Envoyez
session.startpour configurer le modèle, les instructions, l’audio et la délégation. - Commencez à envoyer de l’audio ou des commandes applicatives après réception de
session.started. - Traitez continuellement les transcriptions d’entrée et de sortie, l’audio de sortie et les événements d’utilisation.
- Envoyez
session.closeaprès avoir arrêté la production audio. - Continuez à recevoir les événements et finalisez avec l’utilisation finale de
session.closed.
La fin d’un tour de parole ne signifie pas la fin de la session. Conservez la même connexion pour une conversation à plusieurs tours et n’envoyez session.close que lorsque l’application est prête à terminer toute la session.
Paramètres de démarrage
| Champ | Type | Description |
|---|---|---|
type | string | Toujours session.start dans la première trame |
event_id | string | Identifiant d’événement client facultatif pour associer les erreurs |
session.model | string | Obligatoire, ID du modèle GPT-Live |
session.instructions | string | Instructions facultatives de l’assistant vocal |
session.audio.format | object | Format audio, par exemple audio/pcm, 24 kHz |
session.audio.output.voice | string | Voix de sortie, par exemple marin ; les valeurs disponibles dépendent du modèle |
session.delegation | object | Mode de délégation, client ou responses |
Après démarrage, utilisez session.update pour modifier les paramètres autorisés, comme les instructions, puis attendez la confirmation session.updated. N’envoyez pas de nouveau session.start sur la même connexion et ne changez pas le modèle vocal de premier niveau via un événement de mise à jour.
Préparation audio et exemples d’appel
L’exemple utilise de l’audio PCM signed 16-bit little-endian, 24 kHz, mono, sans en-tête WAV. Chaque intervalle de 100 ms représente 4800 octets ; encodez chaque bloc audio en Base64 avant l’envoi.
# Préparer l’audio d’entrée
ffmpeg -i input.wav -f s16le -acodec pcm_s16le -ac 1 -ar 24000 audio.pcm
# Lire l’audio de sortie enregistré
ffplay -f s16le -ar 24000 -ac 1 reply.pcmL’exemple Node.js nécessite ws ; l’exemple Python vérifie uniquement le démarrage et la fermeture de la session, sans envoyer d’audio.
npm install ws
python -m pip install websocket-clientimport fs from 'node:fs';
import WebSocket from 'ws';
const key = process.env.API_KEY;
const file = process.argv[2];
if (!key || !file) throw new Error('API_KEY et le chemin du fichier PCM sont requis');
const pcm = fs.readFileSync(file);
if (!pcm.length || pcm.length % 2) {
throw new Error('Le PCM ne doit pas être vide et doit être aligné sur des échantillons de 16 bits');
}
const ws = new WebSocket('wss://api.agtcloud.cn/v1/live/sessions', {
headers: { Authorization: 'Bearer ' + key },
handshakeTimeout: 10000,
});
const audioOut = fs.createWriteStream('reply.pcm');
let started = false;
let closing = false;
let finalized = false;
let sender;
let drain;
let offset = 0;
let inputFinishedAt = 0;
let lastOutputAt = 0;
function send(event) {
ws.send(JSON.stringify(event));
}
function closeSession(reason) {
if (closing) return;
closing = true;
clearInterval(sender);
console.error('Demande de fermeture :', reason);
if (started && ws.readyState === WebSocket.OPEN) {
send({ type: 'session.close' });
drain = setTimeout(() => ws.terminate(), 15000);
} else {
ws.terminate();
}
}
const limit = setTimeout(() => closeSession('Limite de 60 secondes de l’exemple'), 60000);
ws.on('open', () => send({
type: 'session.start',
event_id: 'start_001',
session: {
model: 'gpt-live-1',
instructions: 'Répondez brièvement en chinois et attendez que l’utilisateur parle en premier.',
audio: {
format: { type: 'audio/pcm', rate: 24000 },
output: { voice: 'marin' },
},
delegation: { type: 'client' },
},
}));
ws.on('message', (raw) => {
let event;
try {
event = JSON.parse(raw.toString());
} catch {
closeSession('JSON non valide');
return;
}
if (event.type !== 'session.output_audio.delta') {
console.log(raw.toString());
}
if (event.type === 'session.started' && !started) {
started = true;
sender = setInterval(() => {
if (closing || ws.readyState !== WebSocket.OPEN) return;
if (offset >= pcm.length) {
inputFinishedAt ||= Date.now();
if (
lastOutputAt &&
Date.now() - Math.max(lastOutputAt, inputFinishedAt) >= 3000
) {
closeSession('Sortie terminée');
return;
}
send({
type: 'session.input_audio.append',
audio: Buffer.alloc(4800).toString('base64'),
});
return;
}
const chunk = pcm.subarray(offset, offset + 4800);
offset += chunk.length;
send({
type: 'session.input_audio.append',
audio: chunk.toString('base64'),
});
}, 100);
}
if (event.type === 'session.output_audio.delta') {
lastOutputAt = Date.now();
audioOut.write(Buffer.from(event.delta, 'base64'));
}
if (event.type === 'session.output_transcript.delta') {
lastOutputAt = Date.now();
}
if (event.type === 'session.closed') {
finalized = true;
ws.close(1000);
}
if (event.type === 'error') closeSession('Erreur de service');
});
ws.on('error', (error) => {
console.error(error.message);
closeSession('Erreur de connexion');
});
ws.on('close', (code, reason) => {
clearTimeout(limit);
clearTimeout(drain);
clearInterval(sender);
audioOut.end();
console.error({ code, reason: reason.toString(), finalized });
if (!finalized || code !== 1000) process.exitCode = 1;
});
process.on('SIGINT', () => closeSession('Arrêt demandé par l’utilisateur'));Référence des événements
Cette interface envoie l’audio Base64 avec session.input_audio.append. Elle n’utilise ni les trames audio binaires et endStream de Muse, ni les événements input_audio_buffer.* d’OpenAI Realtime.
session.start
Démarre une session GPT-Live et transmet la configuration du modèle, des instructions, de l’audio et de la délégation.
| Champ | Type | Obligatoire | Description | Exemple |
|---|---|---|---|---|
type | string | Oui | Valeur fixe : session.start. | session.start |
event_id | string | Non | Identifiant d’événement client. | start_001 |
session.model | string | Oui | ID de modèle GPT-Live. | gpt-live-1 |
session.instructions | string | Non | Instructions de conversation de l’assistant vocal. | Répondez brièvement en chinois. |
session.audio.format.type | string | Non | Type de format audio. | audio/pcm |
session.audio.format.rate | integer | Non | Fréquence d’échantillonnage audio. | 24000 |
session.audio.output.voice | string | Non | Voix de sortie. | marin |
session.delegation.type | string | Oui | Mode de délégation. | client |
{
"type": "session.start",
"event_id": "start_001",
"session": {
"model": "gpt-live-1",
"instructions": "Répondez brièvement en chinois.",
"audio": {
"format": {
"type": "audio/pcm",
"rate": 24000
},
"output": {
"voice": "marin"
}
},
"delegation": {
"type": "client"
}
}
}Utilisation et facturation
session.usage.updated renvoie l’utilisation cumulée de la session actuelle, pas un ajout propre à un tour. Le client ne doit pas additionner les valeurs des différentes étapes ; lors d’une fermeture normale, session.closed.usage.seconds représente la durée finale de la session.
Les frais peuvent inclure la durée de session vocale, l’utilisation des modèles en arrière-plan et les appels d’outils. Consultez le catalogue de modèles pour les tarifs et la disponibilité du modèle actuel.
Modes de délégation
client: l’application traite elle-même les tâches à déléguer ; ce mode convient pour vérifier d’abord la session vocale de base.responses: le serveur exécute le modèle Responses et les outils configurés. Le modèle cible, les outils et les autorisations doivent être disponibles sur le site actuel.
Avec responses, les modèles et outils des tâches en arrière-plan peuvent générer des frais séparés. Ne considérez pas MODEL_ID dans l’exemple comme un modèle garanti disponible.
Gestion des erreurs
| Symptôme | Vérification et traitement |
|---|---|
| Handshake renvoyant 401 | Vérifiez la clé API Bearer, les autorisations du modèle et l’adresse du site actuel |
| Route renvoyant 404 | Vérifiez que le chemin final est /v1/live/sessions, sans duplication de /v1 |
| Première trame incorrecte ou délai de démarrage dépassé | Envoyez d’abord session.start après connexion et attendez session.started avant l’audio |
| Aucun son ou son déformé | Vérifiez la profondeur PCM, l’ordre des octets, la fréquence, les canaux et l’en-tête WAV |
session.closed non reçu | Conservez la dernière utilisation cumulée, les erreurs et le motif de fermeture ; ne renvoyez pas automatiquement tout l’audio |
| Délégation en arrière-plan refusée | Vérifiez le modèle Responses, les outils, les autorisations et la prise en charge sur le site actuel |