Vai alla navigazione

WebSocket

Crea conversazioni vocali interattive in tempo reale con agenti IA

Questa documentazione è rivolta agli sviluppatori che integrano direttamente l’API WebSocket di ElevenLabs. Per maggiore comodità, valuta l’utilizzo degli SDK ufficiali forniti da ElevenLabs.

L’API WebSocket di ElevenAgents consente conversazioni vocali interattive in tempo reale con agenti IA. Stabilendo una connessione WebSocket, puoi inviare input audio e ricevere risposte audio in tempo reale, creando esperienze conversazionali realistiche.

Endpoint: wss://api.elevenlabs.io/v1/convai/conversation?agent_id={agent_id}

Autenticazione

Utilizzo dell’ID dell’agente

Per gli agenti pubblici, puoi usare direttamente agent_id nell’URL WebSocket senza autenticazione aggiuntiva:

wss://api.elevenlabs.io/v1/convai/conversation?agent_id=<your-agent-id>

Utilizzo di un URL firmato

Per gli agenti privati o le conversazioni che richiedono autorizzazione, ottieni un URL firmato dal tuo server, che comunica in modo sicuro con l’API di ElevenLabs usando la tua chiave API.

Esempio con cURL

Richiesta:

curl -X GET "https://api.elevenlabs.io/v1/convai/conversation/get-signed-url?agent_id=<your-agent-id>" \
-H "xi-api-key: <your-api-key>"

Risposta:

{
"signed_url": "wss://api.elevenlabs.io/v1/convai/conversation?agent_id=<your-agent-id>&token=<token>"
}
Non esporre mai la tua chiave API ElevenLabs lato client.

Eventi WebSocket

Eventi dal client al server

Il client può inviare al server i seguenti eventi:

Invia informazioni contestuali non interrompenti per aggiornare lo stato della conversazione. Questo ti consente di fornire contesto aggiuntivo senza interrompere il flusso della conversazione in corso.

{
"type": "contextual_update",
"text": "User clicked on pricing page"
}

Casi d’uso:

  • Aggiornamento dello stato o delle preferenze dell’utente
  • Fornitura del contesto ambientale
  • Aggiunta di informazioni di background
  • Monitoraggio delle interazioni con l’interfaccia utente

Punti chiave:

  • Non interrompe il flusso della conversazione corrente
  • Gli aggiornamenti vengono inclusi come chiamate di strumenti nella cronologia della conversazione
  • Aiuta a mantenere il contesto senza interrompere il dialogo naturale

Gli aggiornamenti contestuali vengono elaborati in modo asincrono e non richiedono una risposta diretta dal server.

Esempio di implementazione in Next.js

Questo esempio mostra come implementare un client di agente conversazionale basato su WebSocket in Next.js usando l’API WebSocket di ElevenLabs.

Sebbene questo esempio usi il pacchetto voice-stream per gestire l’input del microfono, puoi implementare la tua soluzione per acquisire e codificare l’audio. L’obiettivo qui è mostrare la connessione WebSocket e la gestione degli eventi con l’API di ElevenLabs.

1

Installa le dipendenze necessarie

Per prima cosa, installa i pacchetti necessari:

npm install voice-stream

Il pacchetto voice-stream gestisce l’accesso al microfono e lo streaming audio, codificando automaticamente l’audio in formato base64 come richiesto dall’API di ElevenLabs.

Questo esempio usa Tailwind CSS per lo stile. Per aggiungere Tailwind al tuo progetto Next.js:

npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

Segui quindi la guida ufficiale alla configurazione di Tailwind CSS per Next.js.

In alternativa, puoi sostituire gli attributi className con i tuoi stili CSS.

2

Crea i tipi WebSocket

Definisci i tipi per gli eventi WebSocket:

app/types/websocket.ts
type BaseEvent = {
type: string;
};
type UserTranscriptEvent = BaseEvent & {
type: "user_transcript";
user_transcription_event: {
user_transcript: string;
};
};
type AgentResponseEvent = BaseEvent & {
type: "agent_response";
agent_response_event: {
agent_response: string;
};
};
type AgentResponseCorrectionEvent = BaseEvent & {
type: "agent_response_correction";
agent_response_correction_event: {
original_agent_response: string;
corrected_agent_response: string;
};
};
type AudioResponseEvent = BaseEvent & {
type: "audio";
audio_event: {
audio_base_64: string;
event_id: number;
alignment: {
chars: string[];
char_durations_ms: number[];
char_start_times_ms: number[];
};
};
};
type InterruptionEvent = BaseEvent & {
type: "interruption";
interruption_event: {
reason: string;
};
};
type PingEvent = BaseEvent & {
type: "ping";
ping_event: {
event_id: number;
ping_ms?: number;
};
};
type AgentChatResponsePartEvent = BaseEvent & {
type: "agent_chat_response_part";
text_response_part: {
type: "start" | "delta" | "stop";
text: string;
event_id: number;
response_id: string;
};
};
export type ElevenLabsWebSocketEvent =
| UserTranscriptEvent
| AgentResponseEvent
| AgentResponseCorrectionEvent
| AudioResponseEvent
| InterruptionEvent
| PingEvent
| AgentChatResponsePartEvent;
3

Crea l'hook WebSocket

Crea un hook personalizzato per gestire la connessione WebSocket:

app/hooks/useAgentConversation.ts
'use client';
import { useCallback, useEffect, useRef, useState } from 'react';
import { useVoiceStream } from 'voice-stream';
import type { ElevenLabsWebSocketEvent } from '../types/websocket';
const sendMessage = (websocket: WebSocket, request: object) => {
if (websocket.readyState !== WebSocket.OPEN) {
return;
}
websocket.send(JSON.stringify(request));
};
export const useAgentConversation = () => {
const websocketRef = useRef<WebSocket>(null);
const [isConnected, setIsConnected] = useState<boolean>(false);
const { startStreaming, stopStreaming } = useVoiceStream({
onAudioChunked: (audioData) => {
if (!websocketRef.current) return;
sendMessage(websocketRef.current, {
user_audio_chunk: audioData,
});
},
});
const startConversation = useCallback(async () => {
if (isConnected) return;
const websocket = new WebSocket("wss://api.elevenlabs.io/v1/convai/conversation");
websocket.onopen = async () => {
setIsConnected(true);
sendMessage(websocket, {
type: "conversation_initiation_client_data",
});
await startStreaming();
};
websocket.onmessage = async (event) => {
const data = JSON.parse(event.data) as ElevenLabsWebSocketEvent;
// Handle ping events to keep connection alive
if (data.type === "ping") {
setTimeout(() => {
sendMessage(websocket, {
type: "pong",
event_id: data.ping_event.event_id,
});
}, data.ping_event.ping_ms);
}
if (data.type === "user_transcript") {
const { user_transcription_event } = data;
console.log("User transcript", user_transcription_event.user_transcript);
}
if (data.type === "agent_response") {
const { agent_response_event } = data;
console.log("Agent response", agent_response_event.agent_response);
}
if (data.type === "agent_response_correction") {
const { agent_response_correction_event } = data;
console.log("Agent response correction", agent_response_correction_event.corrected_agent_response);
}
if (data.type === "interruption") {
// Handle interruption
}
if (data.type === "audio") {
const { audio_event } = data;
// Implement your own audio playback system here
// Note: You'll need to handle audio queuing to prevent overlapping
// as the WebSocket sends audio events in chunks
}
if (data.type === "agent_chat_response_part") {
const { text_response_part } = data;
const { type: partType, text, response_id } = text_response_part;
// Handle the agent's response text as it is generated. Enable
// agent_chat_response_part in the agent's client_events to receive
// this during voice conversations.
console.log("Chat response part:", partType, text, response_id);
}
};
websocketRef.current = websocket;
websocket.onclose = async () => {
websocketRef.current = null;
setIsConnected(false);
stopStreaming();
};
}, [startStreaming, isConnected, stopStreaming]);
const stopConversation = useCallback(async () => {
if (!websocketRef.current) return;
websocketRef.current.close();
}, []);
useEffect(() => {
return () => {
if (websocketRef.current) {
websocketRef.current.close();
}
};
}, []);
return {
startConversation,
stopConversation,
isConnected,
};
};
4

Crea il componente della conversazione

Crea un componente che utilizzi l’hook WebSocket:

app/components/Conversation.tsx
'use client';
import { useCallback } from 'react';
import { useAgentConversation } from '../hooks/useAgentConversation';
export function Conversation() {
const { startConversation, stopConversation, isConnected } = useAgentConversation();
const handleStart = useCallback(async () => {
try {
await navigator.mediaDevices.getUserMedia({ audio: true });
await startConversation();
} catch (error) {
console.error('Failed to start conversation:', error);
}
}, [startConversation]);
return (
<div className="flex flex-col items-center gap-4">
<div className="flex gap-2">
<button
onClick={handleStart}
disabled={isConnected}
className="px-4 py-2 bg-blue-500 text-white rounded disabled:bg-gray-300"
>
Start Conversation
</button>
<button
onClick={stopConversation}
disabled={!isConnected}
className="px-4 py-2 bg-red-500 text-white rounded disabled:bg-gray-300"
>
Stop Conversation
</button>
</div>
<div className="flex flex-col items-center">
<p>Status: {isConnected ? 'Connected' : 'Disconnected'}</p>
</div>
</div>
);
}

Passaggi successivi

  1. Riproduzione audio: implementa il tuo sistema di riproduzione audio usando Web Audio API o una libreria. Ricorda di gestire l’accodamento dell’audio per evitare sovrapposizioni, poiché WebSocket invia gli eventi audio in blocchi.
  2. Gestione degli errori: aggiungi logica di ripetizione e meccanismi di ripristino dagli errori
  3. Feedback dell’interfaccia: aggiungi indicatori visivi per l’attività vocale e lo stato della connessione

Gestione della latenza

Per garantire conversazioni fluide, implementa queste strategie:

  • Buffering adattivo: adatta il buffering audio in base alle condizioni di rete.
  • Jitter buffer: implementa un jitter buffer per attenuare le variazioni nei tempi di arrivo dei pacchetti.
  • Monitoraggio ping-pong: usa gli eventi ping e pong per misurare il tempo di andata e ritorno e adattarti di conseguenza.

Best practice per la sicurezza

  • Ruota regolarmente le chiavi API e usa variabili d’ambiente per archiviarle.
  • Implementa il rate limiting per prevenire abusi.
  • Spiega chiaramente lo scopo quando chiedi agli utenti l’accesso al microfono.
  • Suddivisione ottimizzata in blocchi: regola la durata dei blocchi audio per bilanciare latenza ed efficienza.

Risorse aggiuntive