Transcripción en vivo con la API de Gemini Live

La API de Gemini Live admite la transcripción de voz a texto en tiempo real y con baja latencia a través del modelo gemini-3.5-transcribe-live. Si te conectas a la API de Live a través de WebSockets o usas el SDK de IA generativa de Google, puedes transmitir entrada de audio continua y recibir transcripciones de texto incrementales en tiempo real a medida que se produce el habla.

Con la API de Gemini Live, las plataformas para desarrolladores, como Agora, Fishjam, LiveKit, Pipecat, Vercel y Vision Agents, permiten a los desarrolladores crear e implementar interfaces de alto rendimiento controladas por voz con facilidad. Estas plataformas administran una infraestructura compleja de transmisión de contenido multimedia en tiempo real tras bambalinas, lo que permite que los desarrolladores se enfoquen por completo en crear la experiencia del usuario.

Agente en vivo frente a transcripción en vivo

Si bien ambos usan la conexión de transmisión bidireccional de la API de Live, la Transcripción instantánea funciona como una canalización de reconocimiento de voz dedicada y de baja latencia en lugar de un agente conversacional.

Función Agente en vivo Transcripción en vivo
Rol principal Asistente conversacional que escucha, razona y responde. Es una canalización de voz a texto en tiempo real que transcribe el audio entrante.
Modalidad de respuesta Audio hablado y texto (response_modalities=["AUDIO"]). Transcripciones de texto de transmisión (response_modalities=["TEXT"])
Estilo de interacción Diálogo por turnos con detección de pausas e interrupciones. Procesamiento continuo de la transmisión a medida que habla el orador
Funciones admitidas Llamadas a funciones, Búsqueda de Google, instrucciones del sistema Adaptación del sesgo del habla (custom_vocabulary), detección de idioma, VAD manual e híbrido, transcripción inteligente.
Flujo de entrada Multimodal: audio, video, imágenes y texto Entrada de audio (PCM sin procesar de 16 bits).

Comenzar

En los siguientes ejemplos, se muestra cómo abrir una sesión de transmisión bidireccional con gemini-3.5-transcribe-live y recibir transcripciones en tiempo real.

Python

import asyncio
from google import genai
from google.genai import types

client = genai.Client()
model = "gemini-3.5-transcribe-live"

config = types.LiveConnectConfig(
    response_modalities=["TEXT"],
    input_audio_transcription=types.AudioTranscriptionConfig(
        language_codes=[],  # Automatic language detection
    ),
)

async def main():
    async with client.aio.live.connect(model=model, config=config) as session:
        print("Session established with Live Transcription")

        # Receive transcription events
        async for response in session.receive():
            server_content = response.server_content
            if server_content and server_content.input_transcription:
                print("Transcript:", server_content.input_transcription.text)

if __name__ == "__main__":
    asyncio.run(main())

JavaScript

import { GoogleGenAI, Modality } from '@google/genai';

const ai = new GoogleGenAI({});
const model = 'gemini-3.5-transcribe-live';

const config = {
  responseModalities: [Modality.TEXT],
  inputAudioTranscription: {
    languageCodes: [], // Automatic language detection
  },
};

async function main() {
  const session = await ai.live.connect({
    model: model,
    config: config,
    callbacks: {
      onopen: () => console.log('Connected to Live Transcription'),
      onmessage: (message) => {
        const content = message.serverContent;
        if (content?.inputTranscription) {
          console.log('Transcript:', content.inputTranscription.text);
        }
      },
      onerror: (e) => console.error('Error:', e.message),
      onclose: (e) => console.log('Connection closed:', e.reason),
    },
  });
}

main();

WebSockets

const API_KEY = "YOUR_API_KEY";
const MODEL_NAME = "gemini-3.5-transcribe-live";
const WS_URL = `wss://generativelanguage.googleapis.com/ws/google.ai.generativelanguage.v1beta.GenerativeService.BidiGenerateContent?key=${API_KEY}`;

const websocket = new WebSocket(WS_URL);

websocket.onopen = () => {
  console.log('WebSocket connected');

  const setupMessage = {
    setup: {
      model: `models/${MODEL_NAME}`,
      generationConfig: {
        responseModalities: ['TEXT'],
      },
      inputAudioTranscription: {
        languageCodes: []
      }
    }
  };
  websocket.send(JSON.stringify(setupMessage));
};

websocket.onmessage = (event) => {
  const response = JSON.parse(event.data);
  const content = response.serverContent;
  if (content?.inputTranscription) {
    console.log('Transcript:', content.inputTranscription.text);
  }
};

Transcripciones provisionales y finalizadas

A medida que el audio se transmite a la API de Live, el servidor emite dos campos de transcripción complementarios dentro de server_content:

  • interim_input_transcription: Son hipótesis parciales especulativas de baja latencia que se actualizan mientras el orador habla de forma activa. Estas actualizaciones parciales se producen rápidamente y con una demora mínima. Usa interim_input_transcription para renderizar subtítulos de IU en vivo responsivos o para obtener una vista previa de los subtítulos.
  • input_transcription: Es la transcripción finalizada que se emite cuando el orador hace una pausa, finaliza el turno o se finaliza el discurso. Una vez que se emite, este texto representa la transcripción autorizada del modelo de ese segmento de voz. En el modo de transcripción inteligente, se incluirá la respuesta limpia y con formato.

En el siguiente ejemplo, se muestra cómo mostrar resultados parciales interinos de la transmisión y confirmar transcripciones finales:

Python

async def receive_transcripts(session):
    async for response in session.receive():
        server_content = response.server_content
        if not server_content:
            continue

        # Real-time interim hypothesis (updates dynamically as user speaks)
        if server_content.interim_input_transcription:
            interim_text = server_content.interim_input_transcription.text
            print(f"\r[Interim] {interim_text}", end="", flush=True)

        # Finalized transcript (emitted on speech completion)
        if server_content.input_transcription:
            final_text = server_content.input_transcription.text
            print(f"\n[Final] {final_text}")

JavaScript

onmessage: (message) => {
  const content = message.serverContent;
  if (!content) return;

  if (content.interimInputTranscription) {
    // Update live subtitle preview on screen
    renderInterimPreview(content.interimInputTranscription.text);
  }

  if (content.inputTranscription) {
    // Append final committed transcript to chat history
    commitFinalTranscript(content.inputTranscription.text);
  }
};

WebSockets

websocket.onmessage = (event) => {
  const response = JSON.parse(event.data);
  const content = response.serverContent;
  if (content?.interimInputTranscription) {
    console.log('[Interim]:', content.interimInputTranscription.text);
  }
  if (content?.inputTranscription) {
    console.log('[Final]:', content.inputTranscription.text);
  }
};

Cómo enviar audio

Transmite fragmentos de audio a través de la conexión activa como audio PCM sin procesar de 16 bits.

  • Formato de audio: PCM sin procesar de 16 bits a 16 kHz (mono, little-endian).
  • Tamaño del fragmento: Envía audio en fragmentos de 100 ms (de 1,024 a 2,048 fotogramas).
  • Tipo de MIME: audio/pcm;rate=16000 (o la frecuencia de muestreo coincidente)

Python

# Stream a raw PCM audio chunk
await session.send_realtime_input(
    audio=types.Blob(
        data=audio_chunk_bytes,
        mime_type="audio/pcm;rate=16000"
    )
)

# Signal the end of the audio stream when finished
await session.send_realtime_input(audio_stream_end=True)

JavaScript

// Send base64-encoded PCM audio chunk
session.sendRealtimeInput({
  audio: {
    data: audioChunkBase64,
    mimeType: 'audio/pcm;rate=16000'
  }
});

// Signal stream end
session.sendRealtimeInput({
  audioStreamEnd: true
});

WebSockets

// Send base64-encoded PCM audio chunk
websocket.send(JSON.stringify({
  realtimeInput: {
    audio: {
      data: audioChunkBase64,
      mimeType: 'audio/pcm;rate=16000'
    }
  }
}));

// Signal stream end
websocket.send(JSON.stringify({
  realtimeInput: {
    audioStreamEnd: true
  }
}));

Funciones de transcripción

Detección automática de idioma

De forma predeterminada, si se omite language_codes o se establece language_codes=[], se habilita la identificación automática del idioma. El modelo detecta de forma dinámica el idioma hablado en las expresiones, incluidas las conversaciones multilingües y el cambio de idioma.

Python

config = types.LiveConnectConfig(
    response_modalities=["TEXT"],
    input_audio_transcription=types.AudioTranscriptionConfig(
        language_codes=[],
    ),
)

JavaScript

const config = {
  responseModalities: [Modality.TEXT],
  inputAudioTranscription: {
    languageCodes: [],
  },
};

WebSockets

const setupMessage = {
  setup: {
    model: 'models/gemini-3.5-transcribe-live',
    generationConfig: {
      responseModalities: ['TEXT'],
    },
    inputAudioTranscription: {
      languageCodes: [],
    },
  },
};
websocket.send(JSON.stringify(setupMessage));

Sugerencia de idioma específica

Proporciona códigos de idioma BCP-47 explícitos (por ejemplo, ["es-ES"] para español o ["fr-FR"] para francés) para sesgar el reconocimiento hacia idiomas específicos (consulta Idiomas admitidos).

Python

config = types.LiveConnectConfig(
    response_modalities=["TEXT"],
    input_audio_transcription=types.AudioTranscriptionConfig(
        language_codes=["es-ES"],
    ),
)

JavaScript

const config = {
  responseModalities: [Modality.TEXT],
  inputAudioTranscription: {
    languageCodes: ['es-ES'],
  },
};

WebSockets

const setupMessage = {
  setup: {
    model: 'models/gemini-3.5-transcribe-live',
    generationConfig: {
      responseModalities: ['TEXT'],
    },
    inputAudioTranscription: {
      languageCodes: ['es-ES'],
    },
  },
};
websocket.send(JSON.stringify(setupMessage));

Sugerencias de vocabulario personalizadas

Proporciona una lista de hasta 1,000 frases, nombres propios, nombres de marcas o términos técnicos en custom_vocabulary para sesgar el reconocimiento de voz hacia una terminología específica (por lo general, se obtienen los mejores resultados con hasta 100 términos).

Python

config = types.LiveConnectConfig(
    response_modalities=["TEXT"],
    input_audio_transcription=types.AudioTranscriptionConfig(
        language_codes=[],
        custom_vocabulary=["Gemini", "Kubernetes", "BigQuery"],
    ),
)

JavaScript

const config = {
  responseModalities: [Modality.TEXT],
  inputAudioTranscription: {
    languageCodes: [],
    customVocabulary: ['Gemini', 'Kubernetes', 'BigQuery'],
  },
};

WebSockets

const setupMessage = {
  setup: {
    model: 'models/gemini-3.5-transcribe-live',
    generationConfig: {
      responseModalities: ['TEXT'],
    },
    inputAudioTranscription: {
      languageCodes: [],
      customVocabulary: ['Gemini', 'Kubernetes', 'BigQuery'],
    },
  },
};
websocket.send(JSON.stringify(setupMessage));

Transcripción inteligente

Configura el formato de salida de la transcripción con el parámetro mode en input_audio_transcription:

  • VERBATIM (predeterminado): Produce una transcripción literal exacta de todo lo que se dice, conservando las palabras de relleno sin procesar (“eh”, “um”, “como”), las repeticiones y los comienzos en falso.
  • SMART (Transcripción inteligente): Limpia y estructura la transcripción para facilitar la lectura:

    • Eliminación de disfluencias: Quita las muletillas, los tartamudeos y los inicios en falso.
    • Autocorrecciones intercaladas: Resuelve las correcciones habladas de forma natural.
    • Formato estructurado: Da formato automáticamente a listas, viñetas, números, fechas y saltos de párrafo.
    • Gramática y uso de mayúsculas: Aplica un pulido natural a la puntuación y el uso de mayúsculas.

Python

config = types.LiveConnectConfig(
    response_modalities=["TEXT"],
    input_audio_transcription=types.AudioTranscriptionConfig(
        mode="SMART",
    ),
)

JavaScript

const config = {
  responseModalities: [Modality.TEXT],
  inputAudioTranscription: {
    mode: 'SMART',
  },
};

WebSockets

const setupMessage = {
  setup: {
    model: 'models/gemini-3.5-transcribe-live',
    generationConfig: {
      responseModalities: ['TEXT'],
    },
    inputAudioTranscription: {
      mode: 'SMART',
    },
  },
};
websocket.send(JSON.stringify(setupMessage));

Estrategias de detección de actividad de voz (VAD)

VAD automático (predeterminado)

De forma predeterminada, la detección automática de actividad de voz del servidor detecta cuándo un orador comienza a hablar y cuándo deja de hacerlo.

VAD híbrido

El VAD híbrido combina la detección automática del inicio del habla del servidor con la detección del final del habla del cliente para la finalización del turno con latencia cero:

  1. La VAD automática del servidor sigue habilitada para detectar con precisión los inicios del habla con el padding de audio de prefijo, lo que evita el truncamiento de la primera palabra.
  2. El VAD del cliente detecta silencio: Cuando un VAD local en el dispositivo detecta que el orador dejó de hablar, el cliente envía un indicador audio_stream_end de inmediato.
  3. Finalización rápida: El servidor trata audio_stream_end como una instrucción de finalización de turno inmediata, omite el tiempo de espera de silencio predeterminado del servidor y devuelve la transcripción finalizada con una latencia mínima.
  4. Respaldo: Si el VAD del cliente no se activa, el VAD del servidor actúa como respaldo automático.

Python

config = types.LiveConnectConfig(
    response_modalities=["TEXT"],
    input_audio_transcription=types.AudioTranscriptionConfig(),
)

async with client.aio.live.connect(model=model, config=config) as session:
    # Stream audio chunks...
    await session.send_realtime_input(
        audio=types.Blob(data=chunk, mime_type="audio/pcm;rate=16000")
    )

    # When client-side VAD detects end of speech, send audio_stream_end:
    await session.send_realtime_input(audio_stream_end=True)

JavaScript

const config = {
  responseModalities: [Modality.TEXT],
  inputAudioTranscription: {},
};

// Stream audio...
session.sendRealtimeInput({
  audio: { data: chunkBase64, mimeType: 'audio/pcm;rate=16000' }
});

// When client VAD detects end of speech, send audioStreamEnd:
session.sendRealtimeInput({
  audioStreamEnd: true
});

WebSockets

const setupMessage = {
  setup: {
    model: 'models/gemini-3.5-transcribe-live',
    generationConfig: {
      responseModalities: ['TEXT'],
    },
    inputAudioTranscription: {},
  },
};
websocket.send(JSON.stringify(setupMessage));

// Stream audio...
websocket.send(JSON.stringify({
  realtimeInput: {
    audio: { data: chunkBase64, mimeType: 'audio/pcm;rate=16000' }
  }
}));

// When client VAD detects end of speech, send audioStreamEnd:
websocket.send(JSON.stringify({
  realtimeInput: {
    audioStreamEnd: true
  }
}));

VAD manual (presionar para hablar)

En el caso de las interfaces de walkie-talkie o los botones de pulsar para hablar, inhabilita por completo el VAD automático y controla los límites de turnos de forma explícita con activity_start y activity_end:

Python

config = types.LiveConnectConfig(
    response_modalities=["TEXT"],
    realtime_input_config=types.RealtimeInputConfig(
        automatic_activity_detection=types.AutomaticActivityDetection(
            disabled=True
        )
    ),
    input_audio_transcription=types.AudioTranscriptionConfig(),
)

async with client.aio.live.connect(model=model, config=config) as session:
    # Button pressed: signal speech start
    await session.send_realtime_input(activity_start=types.ActivityStart())

    # Stream audio chunks...
    await session.send_realtime_input(audio=types.Blob(data=chunk, mime_type="audio/pcm;rate=16000"))

    # Button released: signal speech end
    await session.send_realtime_input(activity_end=types.ActivityEnd())

JavaScript

const config = {
  responseModalities: [Modality.TEXT],
  realtimeInputConfig: {
    automaticActivityDetection: {
      disabled: true,
    },
  },
  inputAudioTranscription: {},
};

// Signal speech start
session.sendRealtimeInput({ activityStart: {} });

// Stream audio...

// Signal speech end
session.sendRealtimeInput({ activityEnd: {} });

WebSockets

const setupMessage = {
  setup: {
    model: 'models/gemini-3.5-transcribe-live',
    generationConfig: {
      responseModalities: ['TEXT'],
    },
    realtimeInputConfig: {
      automaticActivityDetection: {
        disabled: true,
      },
    },
    inputAudioTranscription: {},
  },
};
websocket.send(JSON.stringify(setupMessage));

// Button pressed: signal speech start
websocket.send(JSON.stringify({
  realtimeInput: {
    activityStart: {},
  },
}));

// Stream audio...
websocket.send(JSON.stringify({
  realtimeInput: {
    audio: { data: chunkBase64, mimeType: 'audio/pcm;rate=16000' },
  },
}));

// Button released: signal speech end
websocket.send(JSON.stringify({
  realtimeInput: {
    activityEnd: {},
  },
}));

Tokens efímeros en aplicaciones cliente

Para las aplicaciones cliente-servidor (como las apps web o para dispositivos móviles que transmiten contenido directamente desde un micrófono), usa tokens efímeros para evitar exponer tu clave de API en el código del cliente.

Crea un token efímero restringido en tu servidor antes de iniciar la conexión del cliente:

Python

import datetime
from google import genai

client = genai.Client()
expire_time = datetime.datetime.now(tz=datetime.timezone.utc) + datetime.timedelta(minutes=30)

token = client.auth_tokens.create(
    config={
        "uses": 1,
        "expire_time": expire_time,
        "live_connect_constraints": {
            "model": "gemini-3.5-transcribe-live",
            "config": {
                "response_modalities": ["TEXT"],
                "input_audio_transcription": {
                    "language_codes": [],
                },
            },
        },
    }
)

JavaScript

import { GoogleGenAI } from '@google/genai';

const client = new GoogleGenAI({});
const expireTime = new Date(Date.now() + 30 * 60 * 1000).toISOString();

const token = await client.authTokens.create({
  config: {
    uses: 1,
    expireTime: expireTime,
    liveConnectConstraints: {
      model: 'gemini-3.5-transcribe-live',
      config: {
        responseModalities: ['TEXT'],
        inputAudioTranscription: {
          languageCodes: [],
        },
      },
    },
  },
});

REST

curl -X POST "https://generativelanguage.googleapis.com/v1beta/auth_tokens" \
  -H "x-goog-api-key: ${GEMINI_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "uses": 1,
    "expireTime": "YYYY-MM-DDTHH:MM:SSZ",
    "liveConnectConstraints": {
      "model": "models/gemini-3.5-transcribe-live",
      "config": {
        "responseModalities": ["TEXT"],
        "inputAudioTranscription": {
          "languageCodes": []
        }
      }
    }
  }'

Idiomas admitidos

Los siguientes idiomas y códigos de idioma BCP-47 son compatibles con Gemini 3.5 Transcribe Live:

Idioma Código BCP-47 Idioma Código BCP-47
Afrikaans af-ZA Japonés ja-JP
Amárico am-ET Javanés jv-ID
Árabe (Egipto) ar-EG Kabuverdianu kea-CV
Armenio hy-AM Canarés kn-IN
Asamés as-IN Kazajo kk-KZ
Azerí az-AZ Coreano ko-KR
Bielorruso be-BY Kirguís ky-KG
Bengalí (Bangladés) bn-BD Letón lv-LV
Bengalí (India) bn-IN Lingala ln-CD
Bosnio bs-BA Lituano lt-LT
Búlgaro bg-BG Macedonio mk-MK
Búlgaro (arrumano) rup-BG Malayo ms-MY
Birmano my-MM Malayalam ml-IN
Cantonés (tradicional) yue-Hant-HK Maltés mt-MT
Catalán ca-ES Chino mandarín (simplificado) cmn-Hans-CN
Cebuano ceb Maratí mr-IN
Camboyano km-KH Mongol mn-MN
Croata hr-HR Nepalí ne-NP
Checo cs-CZ Noruego nb-NO
Danés da-DK Oriya or-IN
Holandés nl-NL Polaco pl-PL
Inglés (Gran Bretaña) en-GB Portugués (Brasil) pt-BR
Inglés (India) en-IN Portugués (Portugal) pt-PT
Inglés (Estados Unidos) en-US Punyabí pa-IN
Estonio et-EE Panyabí (alfabeto gurmukhi) pa-Guru-IN
Persa fa-IR Rumano ro-RO
Filipino fil-PH Ruso ru-RU
Finlandés fi-FI Serbio sr-RS
Francés fr-FR Sindhi (alfabeto árabe) sd-Arab-IN
Gallego gl-ES Eslovaco sk-SK
Georgiano ka-GE Esloveno sl-SI
Alemán de-DE Español (Latinoamérica) es-419
Griego el-GR Español (Estados Unidos) es-US
Gujarati gu-IN Suajili (Kenia) sw-KE
Hausa ha-NG Sueco sv-SE
Hebreo he-IL Tayiko tg-TJ
Hindi hi-IN Telugu te-IN
Húngaro hu-HU Tailandés th-TH
Islandés is-IS Turco tr-TR
tu idioma o tu país en-IN Ucraniano uk-UA
Indonesio id-ID Uzbeko uz-UZ
Italiano it-IT Vietnamita vi-VN

Referencia del parámetro

Configura la transcripción en vivo con los campos de input_audio_transcription y realtime_input_config:

Parámetro Tipo Descripción
language_codes Arreglo de strings Códigos de idioma BCP-47 (p.ej., ["en-US"]). Si se omite o está vacío ([]), el modelo detecta automáticamente el idioma y controla el habla multilingüe.
custom_vocabulary Arreglo de strings Hasta 1,000 términos, acrónimos, nombres de marcas o nombres propios personalizados para sesgar el reconocimiento de voz
mode String Modo de transcripción: "VERBATIM" (predeterminado) o "SMART" (Transcripción inteligente). Cuando se configura como "SMART", el modelo quita las muletillas, da formato a las listas y corrige las disfluencias.
automatic_activity_detection.disabled Booleano Se establece en true para inhabilitar la detección automática de actividad de voz y enviar manualmente los indicadores activityStart y activityEnd.

Campos de respuesta del servidor

Campo Descripción
server_content.interim_input_transcription Es una hipótesis de transcripción parcial provisional de baja latencia que se emite de forma continua mientras el usuario habla de forma activa.
server_content.input_transcription Es la transcripción de entrada autorizada y finalizada que se emite cuando finaliza un turno de voz.

Limitaciones

  • Duración de la sesión: Las sesiones de transcripción en vivo admiten la transmisión continua por hasta 10 minutos.
  • Identificación de oradores: La identificación de oradores no se admite en las sesiones de transmisión en vivo. Para la segmentación por orador, usa el extremo Transcripción de audio sin transmisión.
  • Marcas de tiempo a nivel de la palabra: Las marcas de tiempo a nivel de la palabra no se admiten a través de la API de Live. La API de Live emite marcas de tiempo a nivel de la expresión (interim_input_transcription y input_transcription).
  • Vocabulario personalizado: Puedes proporcionar hasta 1,000 términos en custom_vocabulary, pero, por lo general, se obtienen mejores resultados con hasta 100 términos.
  • Compatibilidad de modos: La transcripción inteligente ("mode": "SMART") quita las palabras de relleno y da formato al texto que tiene en cuenta la intención, pero no se puede combinar con las anotaciones de palabras.

¿Qué sigue?