Multimodal : Entrée Vidéo
Un agent en temps rĂ©el peut voir. ParallĂšlement au flux audio, vous pouvez envoyer des images d'une camĂ©ra (ou d'un Ă©cran) au modĂšle, afin que l'utilisateur puisse montrer quelque chose Ă l'agent â un produit endommagĂ© pour un retour, une erreur sur un Ă©cran, un document â au lieu de le dĂ©crire.
L'API
Une méthode sur le runner, reflétant send_audio :
runner.send_video_frame("image/jpeg", &base64_jpeg).await?;
Elle est également disponible sur le RealtimeRunner de niveau inférieur et sur RealtimeSession directement. L'implémentation de trait par défaut est une opération nulle (no-op), de sorte que les fournisseurs/modÚles qui n'acceptent pas d'entrée visuelle ignorent simplement les trames.
Comment chaque fournisseur gĂšre les trames
Les deux backends traitent la vision différemment, ce qui devrait influencer la fréquence d'envoi des trames :
| Fournisseur | Mécanisme | Cadence |
|---|---|---|
| Gemini Live | Blocs mĂ©dia realtimeInput continus | Diffusez-les â ~1â2 ips est naturel |
| OpenAI Realtime | Une partie input_image sur un Ă©lĂ©ment de conversation | InstantanĂ©s â limitez Ă ~1 toutes les quelques secondes |
Gemini est conçu pour la vidéo continue : les trames circulent comme l'audio et le modÚle raisonne sur l'image en mouvement. La vision en temps réel d'OpenAI est image-dans-contexte : chaque trame devient un élément de conversation, donc l'envoi de nombreuses trames par seconde inonde le contexte et coûte plus cher. Envoyez une trame à OpenAI lorsque c'est utile (par exemple, l'utilisateur a dit "regardez ça"), pas à chaque tic.
Les interfaces utilisateur d'exemple encodent ceci :
const intervalMs = provider === 'gemini' ? 700 : 2500; // ~1.4 fps vs ~0.4 fps
Capturer des trames dans un navigateur
Prenez la caméra, dessinez le <video> sur un canevas et envoyez les trames JPEG via votre WebSocket (le pont cÎté serveur les transmet à send_video_frame) :
const stream = await navigator.mediaDevices.getUserMedia({ video: { width: 640, height: 480 } });
video.srcObject = stream;
const canvas = document.createElement('canvas');
setInterval(() => {
if (ws.readyState !== WebSocket.OPEN) return;
canvas.width = 640; canvas.height = 480;
canvas.getContext('2d').drawImage(video, 0, 0, 640, 480);
const data = canvas.toDataURL('image/jpeg', 0.6).split(',')[1]; // strip data: prefix
ws.send(JSON.stringify({ type: 'video_frame', mime: 'image/jpeg', data }));
}, intervalMs);
Sur le serveur, transmettez au modĂšle :
ClientMsg::VideoFrame { mime, data } => {
let _ = runner.send_video_frame(&mime, &data).await; // non-fatal on error
}
Gardez les images modestes (640Ă480, qualitĂ© JPEG ~0.6) â la vision n'a pas besoin d'une rĂ©solution complĂšte, et des images plus petites signifient une latence et un coĂ»t infĂ©rieurs.
Inviter Ă la vision
Dites à l'agent qu'il peut voir, afin qu'il utilise la caméra naturellement :
You can hear the customer and SEE what they show their camera. When they show
you an item (e.g. a damaged product for a return), briefly describe what you see
and use it to help resolve the issue.
Essayez
L'exemple customer_service dispose d'un panneau de camĂ©ra : dĂ©marrez la camĂ©ra, tenez un objet, et dites "Je veux retourner ça â pouvez-vous le voir ?". Cela fonctionne mieux sur Gemini (vidĂ©o continue) ; sur OpenAI, l'agent raisonne sur les instantanĂ©s pĂ©riodiques.
Suivant : Dialogue affectif â