Zdarzenia klienta

Poznaj i obsługuj zdarzenia w czasie rzeczywistym odbierane przez klienta w aplikacjach konwersacyjnych.

Zdarzenia klienta to zdarzenia na poziomie systemu wysyłane z serwera do klienta, które ułatwiają komunikację w czasie rzeczywistym. Dostarczają do aplikacji klienckiej audio, transkrypcje, odpowiedzi agenta i inne kluczowe informacje.

Informacje o zdarzeniach, które możesz wysyłać z klienta do serwera, znajdziesz w dokumentacji zdarzeń klient-serwer.

Omówienie

Zdarzenia klienta są niezbędne do zachowania rozmów w czasie rzeczywistym. Zapewniają wszystko — od metadanych inicjalizacji po przetworzone audio i odpowiedzi agenta.

Te zdarzenia są częścią protokołu komunikacji WebSocket i są automatycznie obsługiwane przez nasze SDK. Ich zrozumienie jest kluczowe przy zaawansowanych wdrożeniach i debugowaniu.

Typy zdarzeń klienta

  • Wysyłane automatycznie przy rozpoczęciu rozmowy
  • Inicjuje ustawienia i parametry rozmowy
// Example initialization metadata
{
"type": "conversation_initiation_metadata",
"conversation_initiation_metadata_event": {
"conversation_id": "conv_123",
"agent_output_audio_format": "pcm_44100", // TTS output format
"user_input_audio_format": "pcm_16000" // ASR input format
}
}
  • Wysyłane tylko do osób oczekujących w kolejce połączeń, gdy agent osiągnął limit współbieżności
  • waiting jest wysyłane raz, po conversation_initiation_metadata i przed dźwiękiem oczekiwania
  • admitted lub timed_out jest wysyłane raz po zakończeniu oczekiwania. Po timed_out WebSocket zostaje zamknięty z kodem 4300
  • Zawsze wysyłane do osób w kolejce. Nie trzeba włączać go w konfiguracji client_events agenta

Gdy osoba dzwoniąca czeka w kolejce, dźwięk oczekiwania przychodzi jako zwykłe zdarzenia audio. Użyj tego zdarzenia, aby wyświetlić stan oczekiwania, zamiast traktować dźwięk oczekiwania jak mowę agenta.

// Example queue status event structure
{
"type": "queue_status",
"queue_status_event": {
"status": "waiting" // "waiting" | "admitted" | "timed_out"
}
}
// Example queue status handler
websocket.on('queue_status', (event) => {
const { status } = event.queue_status_event;
if (status === 'waiting') {
showWaitingState();
} else if (status === 'admitted') {
hideWaitingState();
} else if (status === 'timed_out') {
showAllAgentsBusyMessage();
}
});
  • Zdarzenie kontroli stanu, które wymaga natychmiastowej odpowiedzi
  • Obsługiwane automatycznie przez SDK
  • Służy do utrzymania połączenia WebSocket
// Example ping event structure
{
"ping_event": {
"event_id": 123456,
"ping_ms": 50 // Optional, estimated latency in milliseconds
},
"type": "ping"
}
// Example ping handler
websocket.on('ping', () => {
websocket.send('pong');
});
  • Zawiera dźwięk zakodowany w base64 do odtwarzania
  • Zawiera numeryczny identyfikator zdarzenia do śledzenia i ustalania kolejności
  • Obsługuje streaming głosu
  • Zawiera dane wyrównania z informacjami o czasie na poziomie znaków

W połączeniach WebRTC zdarzenie audio nie jest wysyłane, ponieważ dźwięk obsługuje bezpośrednio LiveKit.

// Example audio event structure
{
"audio_event": {
"audio_base_64": "base64_encoded_audio_string",
"event_id": 12345,
"alignment": { // Character-level timing data
"chars": ["H", "e", "l", "l", "o"],
"char_durations_ms": [50, 30, 40, 40, 60],
"char_start_times_ms": [0, 50, 80, 120, 160]
}
},
"type": "audio"
}
// Example audio event handler
websocket.on('audio', (event) => {
const { audio_event } = event;
const { audio_base_64, event_id, alignment } = audio_event;
audioPlayer.play(audio_base_64);
// Use alignment data for synchronized text display
const { chars, char_start_times_ms } = alignment;
chars.forEach((char, i) => {
setTimeout(() => highlightCharacter(char, i), char_start_times_ms[i]);
});
});
  • Zawiera gotowe wyniki zamiany mowy na tekst
  • Reprezentuje pełne wypowiedzi użytkownika
  • Służy do historii rozmowy
// Example transcript event structure
{
"type": "user_transcript",
"user_transcription_event": {
"user_transcript": "Hello, how can you help me today?"
}
}
// Example transcript handler
websocket.on('user_transcript', (event) => {
const { user_transcription_event } = event;
const { user_transcript } = user_transcription_event;
updateConversationHistory(user_transcript);
});
  • Zawiera pełną wiadomość agenta
  • Jest wysyłane po zakończeniu wiadomości, więc w rozmowach głosowych zwykle dociera po rozpoczęciu streamingu dźwięku wiadomości.
  • Służy do wyświetlania i historii

Aby wyświetlać tekst agenta w trakcie jego generowania, użyj opisanego niżej zdarzenia agent_chat_response_part, zamiast czekać na to zdarzenie.

// Example response event structure
{
"type": "agent_response",
"agent_response_event": {
"agent_response": "Hello, how can I assist you today?"
}
}
// Example response handler
websocket.on('agent_response', (event) => {
const { agent_response_event } = event;
const { agent_response } = agent_response_event;
displayAgentMessage(agent_response);
});
  • Zawiera skróconą odpowiedź po przerwaniu
  • Aktualizuje wyświetlaną wiadomość
  • Zachowuje poprawność rozmowy
// Example response correction event structure
{
"type": "agent_response_correction",
"agent_response_correction_event": {
"original_agent_response": "Let me tell you about the complete history...",
"corrected_agent_response": "Let me tell you about..." // Truncated after interruption
}
}
// Example response correction handler
websocket.on('agent_response_correction', (event) => {
const { agent_response_correction_event } = event;
const { corrected_agent_response } = agent_response_correction_event;
displayAgentMessage(corrected_agent_response);
});
  • Zawiera dowolne metadane z odpowiedzi niestandardowego LLM
  • Wysyłane tylko przy użyciu niestandardowego LLM
  • Musi być wyraźnie włączone w konfiguracji client_events agenta

To zdarzenie dotyczy integracji z niestandardowym LLM. Pozwala serwerowi niestandardowego LLM przekazać dodatkowe metadane wraz z odpowiedzią, które aplikacja kliencka może wykorzystać.

// Example agent response metadata event structure
{
"type": "agent_response_metadata",
"agent_response_metadata_event": {
"metadata": {
// Any key-value pairs returned by your custom LLM
"key": "value"
},
"event_id": 12345
}
}
// Example metadata handler
websocket.on('agent_response_metadata', (event) => {
const { agent_response_metadata_event } = event;
const { metadata, event_id } = agent_response_metadata_event;
// Use metadata for UI updates, logging, or analytics
console.log(`Response ${event_id} metadata:`, metadata);
updateResponseDetails(metadata);
});
  • Reprezentuje wywołanie funkcji, którą agent chce wykonać po stronie klienta
  • Zawiera nazwę narzędzia, identyfikator wywołania narzędzia i parametry
  • Wymaga wykonania funkcji po stronie klienta i odesłania wyniku na serwer

Jeśli używasz SDK, dostępne są callbacki do obsługi odsyłania wyniku na serwer.

// Example tool call event structure
{
"type": "client_tool_call",
"client_tool_call": {
"tool_name": "search_database",
"tool_call_id": "call_123456",
"parameters": {
"query": "user information",
"filters": {
"date": "2024-01-01"
}
}
}
}
// Example tool call handler
websocket.on('client_tool_call', async (event) => {
const { client_tool_call } = event;
const { tool_name, tool_call_id, parameters } = client_tool_call;
try {
const result = await executeClientTool(tool_name, parameters);
// Send success response back to continue conversation
websocket.send({
type: "client_tool_result",
tool_call_id: tool_call_id,
result: result,
is_error: false
});
} catch (error) {
// Send error response if tool execution fails
websocket.send({
type: "client_tool_result",
tool_call_id: tool_call_id,
result: error.message,
is_error: true
});
}
});
  • Wskazuje, że agent wykonał funkcję narzędzia
  • Zawiera metadane narzędzia i status wykonania
  • Umożliwia wgląd w użycie narzędzi przez agenta podczas rozmów
// Example agent tool response event structure
{
"type": "agent_tool_response",
"agent_tool_response": {
"tool_name": "skip_turn",
"tool_call_id": "skip_turn_c82ca55355c840bab193effb9a7e8101",
"tool_type": "system",
"is_error": false
}
}
// Example agent tool response handler
websocket.on('agent_tool_response', (event) => {
const { agent_tool_response } = event;
const { tool_name, tool_call_id, tool_type, is_error } = agent_tool_response;
if (is_error) {
console.error(`Agent tool ${tool_name} failed:`, tool_call_id);
} else {
console.log(`Agent executed ${tool_type} tool: ${tool_name}`);
}
});
  • Odzwierciedla agent_tool_response i dodatkowo przesyła pełny wynik narzędzia jako ciąg znaków w full_tool_result.
  • Udostępnia wynik narzędzia klientowi do wyświetlenia lub dalszego przetwarzania.
  • Musi być wyraźnie włączone w konfiguracji client_events agenta.

To zdarzenie udostępnia klientowi pełny wynik narzędzia i może zawierać poufne dane. Włącz je tylko wtedy, gdy klient jest zaufany i może obsłużyć ten ładunek. Wyniki większe niż 64 KB są automatycznie skracane.

// Example agent tool response full payload event structure
{
"type": "agent_tool_response_full_payload",
"agent_tool_response_full_payload": {
"tool_name": "lookup_order",
"tool_call_id": "lookup_order_c82ca55355c840bab193effb9a7e8101",
"tool_type": "webhook",
"is_error": false,
"full_tool_result": "{\"order_id\": \"ORD-789\", \"status\": \"shipped\"}",
"truncated": false
}
}
// Example agent tool response full payload handler (using @elevenlabs/react)
import { ConversationProvider } from '@elevenlabs/react';
function App() {
return (
<ConversationProvider
onAgentToolResponse={(response) => {
if (!('full_tool_result' in response)) return;
const { tool_name, tool_call_id, is_error, full_tool_result, truncated } = response;
if (is_error) {
console.error(`Agent tool ${tool_name} failed:`, tool_call_id);
} else {
console.log(`Tool ${tool_name} returned:`, full_tool_result);
}
if (truncated) {
console.warn(`Tool ${tool_name} result was truncated (exceeded 64 KB).`);
}
}}
>
<Agent />
</ConversationProvider>
);
}
  • Zdarzenie wyniku Voice Activity Detection
  • Wskazuje prawdopodobieństwo, że użytkownik mówi
  • Wartości mieszczą się w zakresie od 0 do 1, gdzie wyższe wartości oznaczają większą pewność wykrycia mowy
// Example VAD score event
{
"type": "vad_score",
"vad_score_event": {
"vad_score": 0.95
}
}
  • Wskazuje, że agent wykonał funkcję narzędzia MCP
  • Zawiera nazwę narzędzia, identyfikator wywołania narzędzia i parametry
  • Jest wywoływane z jednym z czterech stanów: loading, awaiting_approval, success i failure.
{
"type": "mcp_tool_call",
"mcp_tool_call": {
"service_id": "xJ8kP2nQ7sL9mW4vR6tY",
"tool_call_id": "call_123456",
"tool_name": "search_database",
"tool_description": "Search the database for user information",
"parameters": {
"query": "user information",
},
"timestamp": "2024-09-30T14:23:45.123456+00:00",
"state": "loading",
"approval_timeout_secs": 10
}
}
  • Streamuje tekst odpowiedzi agenta podczas generowania jako wiadomości start, delta i stop
  • Zawsze wysyłane w trybie tylko tekstowym; w rozmowach głosowych musi być wyraźnie włączone w konfiguracji client_events agenta
  • Nie jest wysyłane, gdy agent lub aktywna procedura używa blokującego guardraila, który musi ocenić całą odpowiedź, zanim zostanie ona udostępniona
  • response_id identyfikuje streamowaną wiadomość i jest zgodne z response_id elementu agent_response, który później ją zatwierdza
// Example start event
{
"type": "agent_chat_response_part",
"text_response_part": {
"type": "start",
"text": "",
"event_id": 12345,
"response_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
}
}
// Example delta event with text chunk
{
"type": "agent_chat_response_part",
"text_response_part": {
"type": "delta",
"text": "Hello, how can I",
"event_id": 12345,
"response_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
}
}
// Example stop event
{
"type": "agent_chat_response_part",
"text_response_part": {
"type": "stop",
"text": "",
"event_id": 12345,
"response_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
}
}
// Example handler
websocket.on('agent_chat_response_part', (event) => {
const { text_response_part } = event;
const { type: partType, text, response_id } = text_response_part;
if (partType === 'start') {
initializeResponseBuffer(response_id);
} else if (partType === 'delta') {
appendToResponseBuffer(response_id, text);
} else if (partType === 'stop') {
finalizeResponse(response_id);
}
});

agent_reasoning_response_part streamuje dostarczane przez model rozumowanie podczas rozmów tylko tekstowych. Włącz to zdarzenie w client_events oraz podsumowanie rozumowania dla agenta. Serwer wysyła wiadomości start, delta i stop. Nie wysyła tego zdarzenia podczas rozmów głosowych ani gdy agent lub aktywna procedura używa blokujących guardraili.

To zdarzenie i odpowiadający mu callback SDK są eksperymentalne. Ich działanie i struktura mogą zmienić się w dowolnej wersji.

Ładunek zdarzenia
{
"type": "agent_reasoning_response_part",
"reasoning_response_part": {
"type": "delta",
"text": "The user asked to cancel, so I should verify the account before continuing.",
"event_id": 123456
}
}

Zdarzenia rozpoczęcia i zakończenia używają pustej wartości text.

Obsługa zdarzeń rozumowania
import { Conversation } from '@elevenlabs/client';
const conversation = await Conversation.startSession({
agentId: 'agent_7101k5zvyjhmfg983brhmhkd98n6',
textOnly: true,
onAgentReasoningResponsePart: ({ type, text, event_id }) => {
if (type === 'start') {
initializeReasoningBuffer(event_id);
} else if (type === 'delta') {
appendToReasoningBuffer(text);
} else if (type === 'stop') {
finalizeReasoning();
}
},
});
  • Uruchamia się, gdy agent zakończy odpowiedź, w tym wszystkie oczekujące wywołania narzędzi. Po tym zdarzeniu agent wygeneruje kolejny wynik tylko wtedy, gdy użytkownik poda nowe dane wejściowe lub limit czasu tury uruchomi nową turę.
  • Musi być wyraźnie włączone w konfiguracji client_events agenta
// Example agent response complete event structure
{
"type": "agent_response_complete",
"agent_response_complete_event": {
"event_id": 12345
}
}
// Example handler
websocket.on('agent_response_complete', (event) => {
const { agent_response_complete_event } = event;
const { event_id } = agent_response_complete_event;
console.log(`Agent response ${event_id} complete`);
});
  • Uruchamia się, gdy naruszenie guardraila kończy rozmowę. Nie jest wysyłane, gdy guardrail uruchamia ponowną próbę, która kończy się powodzeniem.
  • Samo zdarzenie jest sygnałem — nie zawiera żadnego ładunku poza polem type.
  • Musi być wyraźnie włączone w konfiguracji client_events agenta.
// Example guardrail triggered event structure
{
"type": "guardrail_triggered"
}
// Example guardrail triggered handler (using @elevenlabs/client)
import { Conversation } from '@elevenlabs/client';
const conversation = await Conversation.startSession({
agentId: 'agent_7101k5zvyjhmfg983brhmhkd98n6',
onGuardrailTriggered: () => {
console.warn('Guardrail triggered — conversation will end.');
},
});

Przepływ zdarzeń

Oto typowa sekwencja zdarzeń podczas rozmowy:

conversation_initiation_metadata ping pong audio user_transcript audio agent_response client_tool_call client_tool_result audio agent_response agent_response_correction Connection established Playing audio User responds Client tool runs Playing audio Interruption detected Client Server

Gdy agent osiągnie limit równoczesnych rozmów, a kolejkowanie połączeń jest włączone, serwer wysyła zdarzenia queue_status między conversation_initiation_metadata a pierwszym zdarzeniem audio. Dźwięk oczekiwania jest przesyłany jako zdarzenia audio, dopóki rozmówca nie zostanie dopuszczony.

Dobre praktyki

  1. Obsługa błędów

    • Zaimplementuj właściwą obsługę błędów dla każdego typu zdarzenia
    • Zapisuj ważne zdarzenia w logach na potrzeby debugowania
    • Sprawnie obsługuj przerwy w połączeniu
  2. Zarządzanie dźwiękiem

    • Odpowiednio buforuj fragmenty audio
    • Zadbaj o właściwe czyszczenie po przerwaniu
    • Zarządzaj zasobami audio
  3. Zarządzanie połączeniem

    • Szybko odpowiadaj na zdarzenia PING
    • Zaimplementuj logikę ponownego łączenia
    • Monitoruj stan połączenia

Rozwiązywanie problemów

  • Upewnij się, że połączenie WebSocket jest poprawne
  • Sprawdź odpowiedzi PING/PONG
  • Zweryfikuj dane uwierzytelniające API
  • Sprawdź obsługę fragmentów audio
  • Zweryfikuj zgodność formatu audio
  • Monitoruj użycie pamięci
  • Zapisuj wszystkie zdarzenia w logach na potrzeby debugowania
  • Zaimplementuj granice błędów
  • Sprawdź rejestrację procedur obsługi zdarzeń

Szczegółowe przykłady implementacji znajdziesz w naszej dokumentacji SDK.