Aby agent głosowy zaczął mówić, zanim skończy generować odpowiedź, klient wysyła do Kokoro-82M każde gotowe zdanie, gdy tylko się pojawi. W przykładzie AWS endpoint w Amazon SageMaker AI przetwarza te zdania po kolei. Opóźnienie maleje, bo synteza pierwszego zdania rusza wcześniej, a nie dlatego, że model jednocześnie syntezuje kilka zdań.

Jak działa przykład AWS

Kokoro-82M działa w niestandardowym kontenerze na endpointcie czasu rzeczywistego Amazon SageMaker AI typu ml.g5.xlarge. Agent oparty na Strands działa w środowisku Amazon Bedrock AgentCore, korzysta ze strumieniowego generowania tekstu w Amazon Bedrock i wysyła zdania do endpointu Kokoro.

Kontener udostępnia trzy ścieżki: GET /ping do sprawdzania stanu, POST /invocations do strumieniowej odpowiedzi na pojedyncze wejście oraz WS /invocations-bidirectional-stream do dwukierunkowej komunikacji przez trwałe połączenie WebSocket. W tym układzie agent odbiera tekst z Amazon Bedrock, przekazuje go do Kokoro i wysyła dźwięk do klienta.

Przygotuj środowisko i kolejność wdrożenia

Przykład wymaga konta AWS z uprawnieniami do tworzenia endpointów SageMaker AI, środowiska AgentCore, ról IAM i repozytorium Amazon ECR. Wśród wymaganych uprawnień są między innymi sagemaker:CreateEndpoint, sagemaker:CreateEndpointConfig, sagemaker:CreateModel, sagemaker:InvokeEndpointWithBidirectionalStream oraz bedrock:InvokeModelWithResponseStream.

Potrzebujesz także AWS CDK v2 zainstalowanego i zainicjalizowanego w docelowym koncie i regionie, Pythona 3.12 lub nowszego, uv, Dockera oraz Boto3 obsługującego invoke_endpoint_with_bidirectional_stream. Kontener musi mieć etykietę Dockera com.amazonaws.sagemaker.capabilities.bidirectional-streaming=true. Przydadzą się również znajomość WebSocketów i Starlette lub innego frameworka ASGI.

  1. Najpierw wdroż endpoint Kokoro. Uruchom uv run speech-infra deploy kokoro-82m. Ten etap tworzy między innymi role IAM, synchronizuje wagi modelu do Amazon S3 i konfiguruje model oraz endpoint SageMaker AI.
  2. Potem wdroż agenta. Uruchom agent-infra deploy z wymaganym identyfikatorem modelu Amazon Bedrock. AgentCore uruchamia agenta Strands, który wywołuje wdrożony już endpoint Kokoro.
  3. Sprawdź obsługę binarnych ramek WebSocket. Nie zakładaj, że wiadomość przyjdzie jako tekst. W przykładzie AWS transport SageMaker AI przekazuje treść klienta jako binarną ramkę.
  4. Utrzymuj połączenie dla kolejnych zdań. Po wysłaniu dźwięku i ramki synthesis_complete nasłuchuj następnej wiadomości w tej samej sesji. Zamknij ją dopiero po otrzymaniu od klienta komunikatu {"type": "close"}.
  5. Przekazuj zdania w miarę generowania tekstu. Wysyłaj zdanie po otrzymaniu jego końcowej interpunkcji, a po zakończeniu generowania opróżnij bufor, aby przekazać także ostatni fragment bez kropki.

Obsłuż binarne ramki, zanim zaczniesz szukać błędu wdrożenia

Handler Starlette używający receive_text() może zakończyć obsługę żądania błędem KeyError('text'), ponieważ ramka zawiera pole bytes, a nie text. Endpoint może przy tym nadal zgłaszać stan InService. To, że status jest prawidłowy, nie wystarcza więc do sprawdzenia, czy handler poprawnie przetwarza wiadomości.

Odbierz wiadomość ogólną metodą receive() i odczytaj dostępny wariant zawartości. Jeśli wiadomość nie zawiera tekstu, zdekoduj bajty jako UTF-8:

message = await websocket.receive()
payload = message.get("text")
if payload is None:
    payload = message.get("bytes", b"").decode("utf-8")

Przetestuj handler, wysyłając ramkę binarną. Samo sprawdzenie stanu endpointu nie wykryje błędu, który pojawia się dopiero przy obsłudze takiej wiadomości.

Wysyłaj pełne zdania i opróżnij bufor na końcu

Chunker, czyli komponent dzielący strumień tekstu na części, powinien obsługiwać zarówno tekst przekazany w całości, jak i fragmenty napływające token po tokenie. Za każdym razem, gdy pojawi się nowy tekst, zwraca gotowe zdania i zachowuje niedokończoną resztę w buforze. Gdy generowanie się kończy, flush() przekazuje tę resztę do syntezy.

Kolejność ma znaczenie: bez końcowego opróżnienia bufora zdanie, które nie zdążyło otrzymać kropki, może nie trafić do Kokoro. Sam endpoint nadal syntezuje wiadomości sekwencyjnie. Wzorzec pozwala więc rozpocząć syntezę pierwszego zdania, gdy agent tworzy dalszy tekst, ale nie generuje dźwięku dla następnych zdań równolegle z pierwszym.

Dobierz transport dźwięku do klienta

Server-Sent Events (SSE) to jednokierunkowy strumień tekstowy obsługiwany przez przeglądarki. Jeśli istniejący interfejs już korzysta z SSE, przesyłanie dźwięku zakodowanego w base64 pozwala zachować ten tor komunikacji. W pomiarach AWS kodowanie base64 zwiększyło rozmiar danych o około 33%. Trzeba też buforować odebrane fragmenty i składać je ponownie: granice części transportowych nie zawsze pokrywają się z granicami ramek dźwięku.

W przykładzie AWS modelowy kanał dwukierunkowy przenosi surowy dźwięk oraz ramki JSON z komunikatami sterującymi. Kanał AgentCore do przeglądarki nadal przesyła dane w base64, ponieważ środowisko AgentCore przekazuje klientowi słowniki możliwe do serializacji jako JSON. To dwa różne odcinki komunikacji.

Poniższe rozmiary pochodzą z testu transportowego AWS obejmującego 24 klipy:

Format audioBajty bez kodowaniaBajty w base64Przepływność
Raw PCM2 396 4003 195 200384 kb/s
MP3 48k308 304411 07249,5 kb/s
AAC 32k fMP4258 039344 08441,6 kb/s

Jeżeli interfejs już korzysta z SSE, base64 może uprościć integrację, ale odbiorca musi buforować dane i składać je zgodnie z granicami ramek audio. Dwukierunkowy transport z surowym dźwiękiem i ramkami JSON omija kodowanie base64 na połączeniu z Kokoro.

Jak sprawdzić, czy streaming działa

Przykład agenta udostępnia tryb dwukierunkowy bidi i tryb wsadowy batch. Test porównuje czas od rozpoczęcia pracy agenta do pierwszego zdarzenia audio_chunk. W porównaniu end-to-end AWS użyło tego samego promptu, modelu Amazon Bedrock i endpointu; zmieniał się moment rozpoczęcia syntezy.

Przed oceną wyników sprawdź, czy endpoint obsługuje binarną ramkę, sesja przyjmuje kolejną wiadomość bez ponownego łączenia, a ostatni fragment tekstu trafia do syntezy po wywołaniu flush(). W przypadku SSE sprawdź również, czy klient potrafi buforować i składać fragmenty, które przecinają granice ramek dźwięku.

W teście transportowym opublikowanym 22 października 2026 r. AWS wysłało ten sam blok tekstu o długości 267 znaków i czterech zdaniach w trybie wsadowym oraz z początkową syntezą pierwszego zdania. Średni czas do pierwszego bajtu audio wyniósł 329,3 ms w trybie wsadowym i 156,7 ms w trybie przyrostowym. AWS podało spadek o 52,4% w pięciu próbach każdego trybu.

W osobnym porównaniu end-to-end AWS podało, że tryb dwukierunkowy osiągnął pierwszy dźwięk 2,4 razy szybciej w teście lokalnym i 3,5 razy szybciej w teście wdrożonego środowiska AgentCore niż tryb wsadowy. Są to wyniki testów AWS dla opisanego układu, a nie gwarancja podobnego opóźnienia w każdej aplikacji.

Inne sposoby uruchomienia Kokoro

Trelis Research pokazało lokalne generowanie mowy przez Kokoro-ONNX na CPU oraz osobny serwer Kokoro-FastAPI na instancji H100 w Runpod. W teście współbieżności Trelis Research podało przepustowość około 55–78 godzin audio na godzinę czasu rzeczywistego. To odrębne przykłady uruchomienia modelu, a ich przepustowości nie da się bezpośrednio porównać z pomiarem czasu do pierwszego dźwięku AWS.

Usuń wdrożone zasoby i sprawdź pozostałe magazyny

Usuwaj zasoby w odwrotnej kolejności do wdrażania: najpierw środowisko agenta poleceniem agent-infra destroy z identyfikatorem modelu Amazon Bedrock, a potem endpoint poleceniem speech-infra destroy kokoro-82m. Te operacje usuwają runtime AgentCore oraz model, konfigurację i endpoint SageMaker AI.

Polecenia nie usuwają pamięci podręcznej wag w Amazon S3 ani repozytorium obrazu kontenera w Amazon ECR. Jeśli nie są już potrzebne, trzeba je opróżnić lub usunąć osobno; pozostawione zasoby mogą nadal generować opłaty za przechowywanie danych.