> This is a page from the ElevenLabs documentation. For a complete page index, fetch https://elevenlabs.io/docs/llms.txt. For the full documentation in a single file, fetch https://elevenlabs.io/docs/llms-full.txt.

# Diarización de hablantes

> **Note**
>
> **Guía práctica** · Da por hecho que has completado la guía de
> [streaming del lado del cliente](/docs/es/eleven-api/guides/how-to/speech-to-text/realtime/client-side-streaming) o de
> [streaming del lado del servidor](/docs/es/eleven-api/guides/how-to/speech-to-text/realtime/server-side-streaming).

## Resumen

La diarización de hablantes asigna ID de hablante a las palabras para que puedas distinguir quién dijo qué. Con la diarización activada, las transcripciones parciales incluyen etiquetas de hablante provisionales, y cada transcripción confirmada va seguida de un evento `diarized_transcript` independiente. Las etiquetas de hablante pueden cambiar a medida que llega más audio.

## Activar la diarización

Establece el parámetro de consulta booleano `diarize=true` al abrir una conexión WebSocket con `model_id=scribe_v3_live`. La diarización está desactivada (`false`) de forma predeterminada.

Los siguientes ejemplos del lado del servidor abren una conexión y reciben eventos. Instala `websockets` para Python, o `ws` y la dependencia de desarrollo `@types/ws` para TypeScript. Configura `ELEVENLABS_API_KEY` en el entorno de tu servidor; los ejemplos la envían en la cabecera `xi-api-key`. Guarda esta clave en el servidor, no en el código del navegador.

Estos ejemplos no envían audio. Intégralos con tu productor de audio siguiendo la [guía de streaming del lado del servidor](/docs/es/eleven-api/guides/how-to/speech-to-text/realtime/server-side-streaming): envía mensajes `input_audio_chunk` en la misma conexión mientras recibes eventos. Los ejemplos seleccionan `pcm_16000`, así que utiliza audio compatible. En Python, ejecuta la tarea de envío de audio de forma simultánea con el bucle de recepción; en TypeScript, envía el audio después de que se abra la conexión.

```python
import asyncio
import json
import os

from websockets.asyncio.client import connect


async def main():
    url = (
        "wss://api.elevenlabs.io/v1/speech-to-text/realtime"
        "?model_id=scribe_v3_live&audio_format=pcm_16000&diarize=true"
    )
    headers = {"xi-api-key": os.environ["ELEVENLABS_API_KEY"]}

    async with connect(url, additional_headers=headers) as websocket:
        async for message in websocket:
            event = json.loads(message)
            print(event)


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

```typescript
import WebSocket from "ws";

const apiKey = process.env.ELEVENLABS_API_KEY;
if (!apiKey) {
  throw new Error("Set ELEVENLABS_API_KEY in your server environment.");
}

const websocket = new WebSocket(
  "wss://api.elevenlabs.io/v1/speech-to-text/realtime" +
    "?model_id=scribe_v3_live&audio_format=pcm_16000&diarize=true",
  { headers: { "xi-api-key": apiKey } }
);

websocket.on("message", (message) => {
  const event = JSON.parse(message.toString());
  console.log(event);
});

websocket.on("error", (error) => {
  console.error(error);
});
```

## Gestionar eventos

### Transcripciones parciales

Con la diarización activada, cada `partial_transcript` incluye `words` provisionales con el mismo formato de palabra que la transcripción por lotes. El `speaker_id` de cada palabra es `speaker_0`, `speaker_1`, etc. una vez identificado un hablante, o `null` mientras no se resuelva.

Considera estas palabras provisionales: los ID de hablante y las marcas de tiempo pueden cambiar con más audio. Las palabras que aún no se han alineado no tienen marcas de tiempo (`start` y `end` son `null` o no están presentes).

### Transcripciones confirmadas y diarizadas

La diarización no retrasa `committed_transcript`. Con la diarización activada, este evento añade un entero de base cero `segment_index`:

```json
{
  "message_type": "committed_transcript",
  "segment_index": 0,
  "text": "Hello."
}
```

Cada confirmación va seguida de un evento `diarized_transcript` independiente con los valores `segment_index`, `text` y `words` correspondientes:

```json
{
  "message_type": "diarized_transcript",
  "segment_index": 0,
  "text": "Hello.",
  "words": [
    {
      "text": "Hello.",
      "speaker_id": "speaker_0"
    }
  ]
}
```

La palabra anterior muestra solo el texto y la etiqueta de hablante. El formato completo de palabra utiliza los mismos campos `start`, `end`, `type`, `logprob` y `characters` que la transcripción por lotes, junto con `speaker_id`. Un hablante sin resolver tiene un ID de hablante `null`.

Las transcripciones diarizadas se envían en orden de confirmación. Relaciónalas con las transcripciones confirmadas mediante `segment_index`, en lugar de hacerlo por texto o por el siguiente evento recibido. Siempre se envía un `diarized_transcript` por cada confirmación, aunque falle la identificación del hablante; en ese caso, los ID de hablante sin resolver son `null`.

### Fusiones de hablantes

Un evento `speakers_merged` significa que dos ID de hablante se refieren a la misma persona:

```json
{
  "message_type": "speakers_merged",
  "from_speaker_id": "speaker_1",
  "to_speaker_id": "speaker_0"
}
```

Sustituye cada uso anterior de `from_speaker_id` por `to_speaker_id` tanto en las transcripciones parciales como en las diarizadas. Los ID de hablante nunca se reutilizan. Las fusiones pueden encadenarse, así que aplícalas en el orden en que se reciben: si `speaker_2` se fusiona con `speaker_1` y después `speaker_1` se fusiona con `speaker_0`, todas las palabras anteriores de ambos ID deben acabar etiquetadas como `speaker_0`.

Por ejemplo, llama a este controlador para cada evento recibido. Aquí, `transcript_history` almacena cada transcripción parcial y diarizada que conserva la aplicación, incluidas las instantáneas parciales anteriores. Cada fusión actualiza todas las palabras almacenadas in situ, por lo que las fusiones encadenadas también actualizan las palabras fusionadas previamente. Muestra las etiquetas de hablante desde este almacenamiento actualizado; las etiquetas `null` siguen siendo `null`.

```python
transcript_history = []


def handle_event(event):
    if event["message_type"] in ("partial_transcript", "diarized_transcript"):
        transcript_history.append(event)
    elif event["message_type"] == "speakers_merged":
        for transcript in transcript_history:
            for word in transcript.get("words") or []:
                if word.get("speaker_id") == event["from_speaker_id"]:
                    word["speaker_id"] = event["to_speaker_id"]
```

## Limitaciones

* La diarización solo es compatible con `scribe_v3_live`. Otros modelos devuelven un error `invalid_request` con el mensaje `"The 'diarize' parameter is only supported by scribe_v3_live."` y, después, cierran la conexión.
* La diarización no se puede combinar con `filter_background_audio`. Esta combinación devuelve un error `invalid_request` cuyo mensaje contiene `"cannot be combined with filter_background_audio"` y, después, cierra la conexión.
* Las etiquetas de hablante pueden permanecer sin resolver (`null`), y los eventos `speakers_merged` posteriores pueden revisar etiquetas anteriores. Estos eventos de fusión solo se envían cuando la diarización está activada.