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. Usainterim_input_transcriptionpara 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:
- 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.
- 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_endde inmediato. - Finalización rápida: El servidor trata
audio_stream_endcomo 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. - 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_transcriptionyinput_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?
- Consulta la documentación de Gemini Transcribe para archivos de audio que no son de transmisión.
- Lee la descripción general de la API de Live para agentes de voz conversacionales.
- Lee la guía de traducción en vivo para obtener información sobre la traducción de voz a voz en tiempo real.
- Consulta la página de precios para conocer los precios de la transmisión de la API de Live.
- Explora la guía de funciones de la API de Live.