Почему нетривиально: voice здесь упирается не в один движок STT/TTS, а в бюджет латентности всей цепочки PSTN->VAD->LLM->TTS->playback; этот скилл фиксирует Блокер №0, держит transport split Node/Python/FreeSWITCH и не даёт упростить дизайн до batch-пайплайна, который в телефонии не работает.
Реализация зафиксированного дизайна (docs/VOICE_MICROSERVICE.md). Real-time телефонный разговор с AI-агентом Smarty. Цель латентности — <500 ms mouth-to-ear (turn-based; <200 ms — миф для PSTN).
WARN: Блокер №0: сквозной стриминг в core (Vercel AI SDK, см.
smarty-ai-sdk-migration). Без него бюджет латентности не закрыть — делать раньше telephony-PoC.
PSTN/SIP -> FreeSWITCH (mod_audio_stream, L16 8k, buffer 100ms) -> WS -> Node Voice Gateway
Node Gateway (владелец сессии): ESL (answer/uuid_broadcast/uuid_break) + WS к ML + текст к core
ML Service (Python/FastAPI): resample 8->16k -> Silero VAD -> faster-whisper -> TTS
Core (Node): agent loop + MCP + streaming LLM
esl npm).mod_audio_stream (только исходящий форк); cloud — Twilio Media Streams. Один WS вниз.uuid_broadcast (return-path модуля НЕ используем; Node пишет файл и играет сам). Closed-source commercial в air-gapped периметр не тащим.faster-whisper large-v3-turbo, beam_size=1, VAD-gated. На GPU ~60–100 ms/чанк.uuid_break; full-duplex/AEC позже.mod_audio_stream)STREAM_BUFFER_SIZE=100 (мс, кратно 20) — гранулярность доставки, НЕ окно инференса.streamAudio пишет temp-файл + событие ::play, в канал не инжектит — играем через ESL.uuid_broadcast/uuid_break.uuid_break.docs/VOICE_MICROSERVICE.md (полный дизайн) · smarty-ai-sdk-migration (стриминг — блокер №0) · voicebox/, mod_audio_stream/ (reference-клоны).