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.
- 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. - Potem wdroż agenta. Uruchom
agent-infra deployz wymaganym identyfikatorem modelu Amazon Bedrock. AgentCore uruchamia agenta Strands, który wywołuje wdrożony już endpoint Kokoro. - 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ę.
- Utrzymuj połączenie dla kolejnych zdań. Po wysłaniu dźwięku i ramki
synthesis_completenasłuchuj następnej wiadomości w tej samej sesji. Zamknij ją dopiero po otrzymaniu od klienta komunikatu{"type": "close"}. - 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 audio | Bajty bez kodowania | Bajty w base64 | Przepływność |
| Raw PCM | 2 396 400 | 3 195 200 | 384 kb/s |
| MP3 48k | 308 304 | 411 072 | 49,5 kb/s |
| AAC 32k fMP4 | 258 039 | 344 084 | 41,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.