Multimodal: Entrada de Vídeo

Um agente em tempo real pode ver. Juntamente com o fluxo de áudio, você pode enviar quadros de imagem de uma câmera (ou tela) para o modelo, para que o usuário possa mostrar algo ao agente — um produto danificado para devolução, um erro em uma tela, um documento — em vez de descrevê-lo.

A API

Um método no runner, espelhando send_audio:

runner.send_video_frame("image/jpeg", &base64_jpeg).await?;

Também está disponível no RealtimeRunner de nível inferior e diretamente no RealtimeSession. A implementação de trait padrão é uma no-op, então provedores/modelos que não aceitam entrada visual simplesmente ignoram os quadros.

Como cada provedor lida com os quadros

Os dois backends tratam a visão de forma diferente, e isso deve moldar a frequência com que você envia os quadros:

ProvedorMecanismoCadência
Gemini LiveContínuos realtimeInput blocos de mídiaTransmita-os — ~1–2 fps é natural
OpenAI RealtimeUma input_image parte em um item de conversaSnapshots — limitar a ~1 a cada poucos segundos

Gemini é construído para vídeo contínuo: os quadros fluem como áudio e o modelo raciocina sobre a imagem em movimento. A visão em tempo real da OpenAI é imagem-em-contexto: cada quadro se torna um item de conversação, então enviar muitos por segundo inunda o contexto e custa mais. Envie um quadro para a OpenAI quando for útil (por exemplo, o usuário disse "olhe para isto"), não a cada tick.

As UIs de exemplo codificam isso:

const intervalMs = provider === 'gemini' ? 700 : 2500;  // ~1.4 fps vs ~0.4 fps

Capturando quadros em um navegador

Pegue a câmera, desenhe o <video> para uma tela, e envie quadros JPEG pelo seu WebSocket (a ponte do lado do servidor os encaminha para 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);

No servidor, encaminhe para o modelo:

ClientMsg::VideoFrame { mime, data } => {
    let _ = runner.send_video_frame(&mime, &data).await;  // non-fatal on error
}

Mantenha os quadros modestos (640×480, qualidade JPEG ~0.6) — a visão não precisa de resolução total, e quadros menores significam menor latência e custo.

Prompting para visão

Diga ao agente que ele pode ver, para que ele use a câmera naturalmente:

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.

Experimente

O exemplo customer_service possui um painel de câmera: inicie a câmera, segure um objeto e diga "Quero devolver isso — você consegue ver?". Funciona melhor no Gemini (vídeo contínuo); no OpenAI o agent raciocina sobre os instantâneos periódicos.

Próximo: Diálogo afetivo →