WebSocket

Crie conversas de voz interativas em tempo real com agentes de IA

Esta documentação é destinada a desenvolvedores que integram diretamente com a API WebSocket da ElevenLabs. Para maior conveniência, considere usar os SDKs oficiais fornecidos pela ElevenLabs.

A API WebSocket do ElevenAgents permite conversas de voz interativas em tempo real com agentes de IA. Ao estabelecer uma conexão WebSocket, você pode enviar áudio de entrada e receber respostas em áudio em tempo real, criando experiências de conversa naturais.

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

Autenticação

Usando o ID do agente

Para agentes públicos, você pode usar diretamente o agent_id na URL do WebSocket sem autenticação adicional:

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

Usando uma URL assinada

Para agentes privados ou conversas que exigem autorização, obtenha uma URL assinada do seu servidor, que se comunica com segurança com a API da ElevenLabs usando sua chave de API.

Exemplo com cURL

Solicitação:

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

Resposta:

{
"signed_url": "wss://api.elevenlabs.io/v1/convai/conversation?agent_id=<your-agent-id>&token=<token>"
}
Nunca exponha sua chave de API da ElevenLabs no lado do cliente.

Eventos do WebSocket

Eventos do cliente para o servidor

Os eventos a seguir podem ser enviados do cliente para o servidor:

Envie informações contextuais sem interrupções para atualizar o estado da conversa. Isso permite fornecer contexto adicional sem interromper o fluxo da conversa em andamento.

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

Casos de uso:

  • Atualizar o status ou as preferências do usuário
  • Fornecer contexto do ambiente
  • Adicionar informações de apoio
  • Acompanhar interações na interface do usuário

Pontos principais:

  • Não interrompe o fluxo atual da conversa
  • As atualizações são incorporadas como chamadas de ferramenta no histórico da conversa
  • Ajuda a manter o contexto sem interromper o diálogo natural

As atualizações contextuais são processadas de forma assíncrona e não exigem uma resposta direta do servidor.

Exemplo de implementação no Next.js

Este exemplo demonstra como implementar um cliente de agente conversacional baseado em WebSocket no Next.js usando a API WebSocket da ElevenLabs.

Embora este exemplo use o pacote voice-stream para gerenciar a entrada do microfone, você pode implementar sua própria solução para capturar e codificar áudio. O foco aqui é demonstrar a conexão WebSocket e o tratamento de eventos com a API da ElevenLabs.

1

Instale as dependências necessárias

Primeiro, instale os pacotes necessários:

npm install voice-stream

O pacote voice-stream gerencia o acesso ao microfone e a transmissão de áudio, codificando automaticamente o áudio no formato base64, conforme exigido pela API da ElevenLabs.

Este exemplo usa Tailwind CSS para estilização. Para adicionar o Tailwind ao seu projeto Next.js:

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

Em seguida, siga o guia oficial de configuração do Tailwind CSS para Next.js.

Como alternativa, você pode substituir os atributos className pelos seus próprios estilos CSS.

2

Crie os tipos do WebSocket

Defina os tipos para os eventos do 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

Crie o hook do WebSocket

Crie um hook personalizado para gerenciar a conexão 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

Crie o componente de conversa

Crie um componente para usar o hook do 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>
);
}

Próximas etapas

  1. Reprodução de áudio: Implemente seu próprio sistema de reprodução de áudio usando a Web Audio API ou uma biblioteca. Lembre-se de gerenciar a fila de áudio para evitar sobreposições, pois o WebSocket envia eventos de áudio em blocos.
  2. Tratamento de erros: Adicione lógica de novas tentativas e mecanismos de recuperação de erros
  3. Feedback da interface: Adicione indicadores visuais de atividade de voz e status da conexão

Gerenciamento de latência

Para garantir conversas fluidas, implemente estas estratégias:

  • Buffer adaptativo: Ajuste o buffer de áudio com base nas condições da rede.
  • Buffer de jitter: Implemente um buffer de jitter para suavizar variações nos tempos de chegada dos pacotes.
  • Monitoramento de ping-pong: Use eventos de ping e pong para medir o tempo de ida e volta e fazer os ajustes necessários.

Práticas recomendadas de segurança

  • Altere as chaves de API regularmente e use variáveis de ambiente para armazená-las.
  • Implemente limitação de taxa para evitar abusos.
  • Explique claramente a finalidade ao solicitar acesso ao microfone dos usuários.
  • Segmentação otimizada: ajuste a duração dos blocos de áudio para equilibrar latência e eficiência.

Recursos adicionais