> 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.

# WebSocket met meerdere contexten

> **Geavanceerd**
>
> Spraakagenten orkestreren met deze WebSocket API met meerdere contexten is een complexe taak die wordt aanbevolen
> voor gevorderde ontwikkelaars. Bekijk voor een meer beheerde oplossing ons product [Agents Platform product](/docs/nl/eleven-agents/overview), dat veel van deze uitdagingen vereenvoudigt.

## Overzicht

Voor het bouwen van responsieve spraakagenten moet je audiostreams dynamisch kunnen beheren, onderbrekingen soepel kunnen afhandelen en natuurlijk klinkende spraak gedurende gespreksbeurten kunnen behouden. Onze WebSocket API met meerdere contexten voor Text to Speech (TTS) is specifiek ontworpen voor deze scenario's.

Deze API breidt onze [standaard TTS WebSocket-functionaliteit](/docs/nl/eleven-api/guides/how-to/websockets/realtime-tts) uit met het concept van "contexten". Elke context werkt als een onafhankelijke audiogeneratiestream binnen één WebSocket-verbinding. Hiermee kun je:

* Meerdere spraakregels tegelijkertijd beheren (bijvoorbeeld een agent die spreekt terwijl die een reactie voorbereidt op een onderbreking door een gebruiker).
* Onderbrekingen door gebruikers soepel afhandelen door een bestaande spraakcontext te sluiten en een nieuwe te starten.
* Prosodische consistentie behouden voor uitingen binnen dezelfde logische context.
* Het gebruik van resources optimaliseren door contexten die niet meer nodig zijn selectief te sluiten.

> **Warning**
>
> De WebSocket API met meerdere contexten is geoptimaliseerd voor spraaktoepassingen en is niet bedoeld voor het
> gelijktijdig genereren van meerdere niet-gerelateerde audiostreams. Daarom is elke verbinding beperkt tot 5
> gelijktijdige contexten.

Deze handleiding helpt je bij het verbinden met de WebSocket met meerdere contexten, het beheren van contexten en het toepassen van best practices voor het bouwen van aantrekkelijke spraakagenten.

### Best practices

> **Note**
>
> Deze best practices zijn essentieel voor het bouwen van responsieve, efficiënte spraakagenten met onze
> WebSocket API met meerdere contexten.

#### Gebruik één WebSocket-verbinding

Maak één WebSocket-verbinding voor elke sessie van een eindgebruiker. Dit vermindert overhead en latentie
vergeleken met het maken van meerdere verbindingen. Binnen deze ene verbinding kun je
meerdere contexten voor verschillende delen van het gesprek beheren.

#### Stream reacties in chunks, genereer zinnen

Stream bij het genereren van lange reacties de tekst in kleinere chunks en gebruik de vlag `flush: true`
aan het einde van volledige zinnen. Dit verbetert de kwaliteit van de gegenereerde audio en de
responsiviteit.

#### Ga soepel om met onderbrekingen

Stream tekst naar één context totdat er een onderbreking plaatsvindt, maak vervolgens een nieuwe context en sluit
de bestaande. Deze aanpak zorgt voor vloeiende overgangen wanneer het gespreksverloop verandert.

#### Beheer de levenscyclus van contexten

Sluit ongebruikte contexten snel. De server kan per
verbinding maximaal 5 gelijktijdige contexten behouden, maar sluit contexten wanneer ze niet meer nodig zijn.

#### Voorkom context-time-outs

Contexten verlopen standaard na 20 seconden en worden automatisch gesloten. De inactiviteits-
time-out is een parameter op WebSocket-niveau die op alle contexten van toepassing is en indien nodig maximaal 180 seconden kan zijn.
Stuur een leeg tekstbericht naar een context om de time-outklok opnieuw in te stellen.

### Onderbrekingen afhandelen

Wanneer een gebruiker je agent onderbreekt, moet je [de huidige context sluiten](/docs/nl/api-reference/text-to-speech/v-1-text-to-speech-voice-id-multi-stream-input#send.Close-Context) en [een nieuwe maken](/docs/nl/api-reference/text-to-speech/v-1-text-to-speech-voice-id-multi-stream-input#send.Initialise-Context):

```python
async def handle_interruption(websocket, old_context_id, new_context_id, new_response):
    # Close the existing context that was interrupted
    await websocket.send(json.dumps({
        "context_id": old_context_id,
        "close_context": True
    }))
    print(f"Closed interrupted context '{old_context_id}'")

    # Create a new context for the new response
    await send_text_in_context(websocket, new_response, new_context_id)
```

```javascript
function handleInterruption(websocket: WebSocket, oldContextId: string, newContextId: string, newResponse: string) {
  // Close the existing context that was interrupted
  websocket.send(JSON.stringify({
    context_id: oldContextId,
    close_context: true
  }));
  console.log(`Closed interrupted context '${oldContextId}'`);

  // Create a new context for the new response
  sendTextInContext(websocket, newResponse, newContextId);
}
```

### Een context actief houden

Contexten verlopen automatisch na [standaard 20 seconden inactiviteit](/docs/nl/api-reference/text-to-speech/v-1-text-to-speech-voice-id-multi-stream-input#request.query.inactivity_timeout). Als je een context actief moet houden zonder tekst te genereren (bijvoorbeeld tijdens een verwerkingsvertraging), kun je een leeg tekstbericht sturen om de time-outklok opnieuw in te stellen.

```python
async def keep_context_alive(websocket, context_id):
    await websocket.send(json.dumps({
        "context_id": context_id,
        "text": ""
    }))
```

```javascript
function handleInterruption(websocket: WebSocket, contextId: string) {
  // Close the existing context that was interrupted
  websocket.send(JSON.stringify({
    context_id: oldContextId,
    text: ""
  }));
}
```

### De WebSocket-verbinding sluiten

Wanneer je gesprek eindigt, kun je alle contexten opruimen door [de socket te sluiten](/docs/nl/api-reference/text-to-speech/v-1-text-to-speech-voice-id-multi-stream-input#send.Close-Socket):

```python
async def end_conversation(websocket):
    # This will close all contexts and close the connection
    await websocket.send(json.dumps({
        "close_socket": True
    }))
    print("Ending conversation and closing WebSocket")`
```

```javascript
function endConversation(websocket: WebSocket) {
  // This will close all contexts and close the connection
  websocket.send(JSON.stringify({
    close_socket: true
  }));
  console.log("Ending conversation and closing WebSocket");
}
```

## Volledig voorbeeld van een conversatieagent

### Vereisten

* Een ElevenLabs-account met een API-sleutel (lees hoe je [je API-sleutel vindt](/docs/nl/api-reference/authentication)).
* Python of Node.js (of een andere JavaScript-runtime) geïnstalleerd op je computer.
* Kennis van WebSocket-communicatie. We raden aan onze [handleiding voor standaard WebSocket-streaming](/docs/nl/eleven-api/guides/how-to/websockets/realtime-tts) te lezen voor de basisconcepten.

### Installatie

Installeer de benodigde dependencies voor de taal van je keuze:

```python
pip install python-dotenv websockets
```

```javascript
npm install dotenv ws
for TypeScript, you might also want types:
npm install @types/dotenv @types/ws --save-dev
```

Maak een .env-bestand in de directory van je project om je API-sleutel op te slaan:

**`.env`**

```python .env
ELEVENLABS_API_KEY=your_elevenlabs_api_key_here
```

### Voorbeeld van een spraakagent

> **Note**
>
> Deze code wordt als voorbeeld aangeboden en is niet bedoeld voor productiegebruik

```python maxLines=100
import os
import json
import asyncio
import websockets
from dotenv import load_dotenv

load_dotenv()
ELEVENLABS_API_KEY = os.getenv("ELEVENLABS_API_KEY")
VOICE_ID = "your_voice_id"
MODEL_ID = "eleven_flash_v2_5"

WEBSOCKET_URI = f"wss://api.elevenlabs.io/v1/text-to-speech/{VOICE_ID}/multi-stream-input?model_id={MODEL_ID}"

async def send_text_in_context(websocket, text, context_id, voice_settings=None):
    """Send text to be synthesized in the specified context."""
    message = {
        "text": text,
        "context_id": context_id,
    }

    # Only include voice_settings for the first message in a context
    if voice_settings:
        message["voice_settings"] = voice_settings

    await websocket.send(json.dumps(message))

async def continue_context(websocket, text, context_id):
    """Add more text to an existing context."""
    await websocket.send(json.dumps({
        "text": text,
        "context_id": context_id
    }))

async def flush_context(websocket, context_id):
    """Force generation of any buffered audio in the context."""
    await websocket.send(json.dumps({
        "context_id": context_id,
        "flush": True
    }))

async def handle_interruption(websocket, old_context_id, new_context_id, new_response):
    """Handle user interruption by closing current context and starting a new one."""
    # Close the existing context that was interrupted
    await websocket.send(json.dumps({
        "context_id": old_context_id,
        "close_context": True
    }))

    # Create a new context for the new response
    await send_text_in_context(websocket, new_response, new_context_id)

async def end_conversation(websocket):
    """End the conversation and close the WebSocket connection."""
    await websocket.send(json.dumps({
        "close_socket": True
    }))

async def receive_messages(websocket):
    """Process incoming WebSocket messages."""
    context_audio = {}
    try:
        async for message in websocket:
            data = json.loads(message)
            context_id = data.get("contextId", "default")

            if data.get("audio"):
                print(f"Received audio for context '{context_id}'")

            if data.get("is_final"):
                print(f"Context '{context_id}' completed")
    except (websockets.exceptions.ConnectionClosed, asyncio.CancelledError):
        print("Message receiving stopped")

async def conversation_agent_demo():
    """Run a complete conversational agent demo."""
    # Connect with API key in headers
    async with websockets.connect(
        WEBSOCKET_URI,
        max_size=16 * 1024 * 1024,
        additional_headers={"xi-api-key": ELEVENLABS_API_KEY}
    ) as websocket:
        # Start receiving messages in background
        receive_task = asyncio.create_task(receive_messages(websocket))

        # Initial agent response
        await send_text_in_context(
            websocket,
            "Hello! I'm your virtual assistant. I can help you with a wide range of topics. What would you like to know about today?",
            "greeting"
        )

        # Wait a bit (simulating user listening)
        await asyncio.sleep(2)

        # Simulate user interruption
        print("USER INTERRUPTS: 'Can you tell me about the weather?'")

        # Handle the interruption by closing current context and starting new one
        await handle_interruption(
            websocket,
            "greeting",
            "weather_response",
            "I'd be happy to tell you about the weather. Currently in your area, it's 72 degrees and sunny with a slight chance of rain later this afternoon."
        )

        # Add more to the weather context
        await continue_context(
            websocket,
            " If you're planning to go outside, you might want to bring a light jacket just in case.",
            "weather_response"
        )

        # Flush at the end of this turn to ensure all audio is generated
        await flush_context(websocket, "weather_response")

        # Wait a bit (simulating user listening)
        await asyncio.sleep(3)

        # Simulate user asking another question
        print("USER: 'What about tomorrow?'")

        # Create a new context for this response
        await send_text_in_context(
            websocket,
            "Tomorrow's forecast shows temperatures around 75 degrees with partly cloudy skies. It should be a beautiful day overall!",
            "tomorrow_weather"
        )

        # Flush and close this context
        await flush_context(websocket, "tomorrow_weather")
        await websocket.send(json.dumps({
            "context_id": "tomorrow_weather",
            "close_context": True
        }))

        # End the conversation
        await asyncio.sleep(2)
        await end_conversation(websocket)

        # Cancel the receive task
        receive_task.cancel()
        try:
            await receive_task
        except asyncio.CancelledError:
            pass

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

```

```javascript maxLines=100
// Import required modules
import dotenv from "dotenv";
import fs from "fs";
import WebSocket from "ws";

// Load environment variables
dotenv.config();
const ELEVENLABS_API_KEY = process.env.ELEVENLABS_API_KEY;
const VOICE_ID = "your_voice_id";
const MODEL_ID = "eleven_flash_v2_5";

const WEBSOCKET_URI = `wss://api.elevenlabs.io/v1/text-to-speech/${VOICE_ID}/multi-stream-input?model_id=${MODEL_ID}`;

// Function to send text in a specific context
function sendTextInContext(websocket, text, contextId, voiceSettings = null) {
  const message = {
    text: text,
    context_id: contextId,
  };

  // Only include voice_settings for the first message in a context
  if (voiceSettings) {
    message.voice_settings = voiceSettings;
  }

  websocket.send(JSON.stringify(message));
}

// Function to continue an existing context with more text
function continueContext(websocket, text, contextId) {
  websocket.send(
    JSON.stringify({
      text: text,
      context_id: contextId,
    })
  );
}

// Function to flush a context, forcing generation of buffered audio
function flushContext(websocket, contextId) {
  websocket.send(
    JSON.stringify({
      context_id: contextId,
      flush: true,
    })
  );
}

// Function to handle user interruption
function handleInterruption(websocket, oldContextId, newContextId, newResponse) {
  // Close the existing context that was interrupted
  websocket.send(
    JSON.stringify({
      context_id: oldContextId,
      close_context: true,
    })
  );

  // Create a new context for the new response
  sendTextInContext(websocket, newResponse, newContextId);
}

// Function to end the conversation and close the connection
function endConversation(websocket) {
  websocket.send(
    JSON.stringify({
      close_socket: true,
    })
  );
}

// Function to run the conversation agent demo
async function conversationAgentDemo() {
  // Connect to WebSocket with API key in headers
  const websocket = new WebSocket(WEBSOCKET_URI, {
    headers: {
      "xi-api-key": ELEVENLABS_API_KEY,
    },
    maxPayload: 16 * 1024 * 1024,
  });

  // Set up event handlers
  websocket.on("open", () => {
    // Initial agent response
    sendTextInContext(
      websocket,
      "Hello! I'm your virtual assistant. I can help you with a wide range of topics. What would you like to know about today?",
      "greeting"
    );

    // Simulate wait time (user listening)
    setTimeout(() => {
      // Simulate user interruption
      console.log("USER INTERRUPTS: 'Can you tell me about the weather?'");

      // Handle the interruption
      handleInterruption(
        websocket,
        "greeting",
        "weather_response",
        "I'd be happy to tell you about the weather. Currently in your area, it's 72 degrees and sunny with a slight chance of rain later this afternoon."
      );

      // Add more to the weather context
      setTimeout(() => {
        continueContext(
          websocket,
          " If you're planning to go outside, you might want to bring a light jacket just in case.",
          "weather_response"
        );

        // Flush at the end of this turn
        flushContext(websocket, "weather_response");

        // Simulate wait time (user listening)
        setTimeout(() => {
          // Simulate user asking another question
          console.log("USER: 'What about tomorrow?'");

          // Create a new context for this response
          sendTextInContext(
            websocket,
            "Tomorrow's forecast shows temperatures around 75 degrees with partly cloudy skies. It should be a beautiful day overall!",
            "tomorrow_weather"
          );

          // Flush and close this context
          flushContext(websocket, "tomorrow_weather");
          websocket.send(
            JSON.stringify({
              context_id: "tomorrow_weather",
              close_context: true,
            })
          );

          // End the conversation
          setTimeout(() => {
            endConversation(websocket);
          }, 2000);
        }, 3000);
      }, 500);
    }, 2000);
  });

  // Handle incoming messages
  websocket.on("message", (message) => {
    try {
      const data = JSON.parse(message);
      const contextId = data.contextId || "default";

      if (data.audio) {
        //do stuff
      }

      if (data.is_final) {
        console.log(`Context '${contextId}' completed`);
      }
    } catch (error) {
      console.error("Error parsing message:", error);
    }
  });

  // Handle WebSocket closure
  websocket.on("close", () => {
    console.log("WebSocket connection closed");
  });

  // Handle WebSocket errors
  websocket.on("error", (error) => {
    console.error("WebSocket error:", error);
  });
}

// Run the demo
conversationAgentDemo();
```

## Volgende stappen

#### [ElevenAgents](/docs/nl/eleven-agents/quickstart)

Bouw productieklare spraakagenten met het volledige ElevenAgents-platform.

#### [Audiostreaming begrijpen](/docs/nl/eleven-api/concepts/audio-streaming)

Lees hoe WebSocket-streaming onder de motorkap werkt en wat de latentie beïnvloedt.