English translation is not available yet. Showing Russian content.

Как вы строите real-time voice agent с latency <500ms?

Краткий тезис

Построение agent|voice agent|voice real-time agent|voice agent|voice agent с latency менее 500 миллисекунд требует сквозного streaming-пайплайна, где каждый компонент — ASR (распознавание речи), LLM (генерация ответа) и TTS (синтез речи) — работает в параллельных потоках без блокировок. Ключевые решения: выбор лёгких моделей (faster-whisper, Phi-3-mini, Piper TTS), использование WebRTC для транспорта и оптимизация каждого этапа под latency. Цель — обеспечить плавный диалог, при котором задержка не ощущается пользователем.


1. Терминология

  • Real-time voice agent — голосовой ассистент, который обрабатывает речь пользователя и отвечает голосом с минимальной задержкой, имитируя естественный разговор.
  • Latency — время от окончания речи пользователя до начала воспроизведения ответа. Целевое значение <500 мс (обычно <300 мс для «real-time»).
  • ASR (Automatic Speech Recognition) — преобразование аудиопотока в текст. Streaming ASR выдаёт слова по мере поступления звука.
  • LLM (Large Language Model) — модель, генерирующая текстовый ответ на основе распознанного запроса.
  • TTS (Text-to-Speech) — синтез речи из текста. Streaming TTS выдаёт аудиочанки без ожидания полного текста.
  • WebRTC — протокол для peer-to-peer аудио/видео с низкой задержкой (UDP), поддерживает DTLS и SRTP.
  • Streaming — обработка данных порциями (чанками) без ожидания полного входа/выхода.

2. Общая архитектура real-time voice agent

Система состоит из трёх последовательных этапов, соединённых через буферы и очереди:

Пользователь (микрофон) → WebRTC → ASR (streaming) → LLM (streaming) → TTS (streaming) → WebRTC → Пользователь (динамики)

Каждый этап работает в отдельном процессе/потоке и передаёт данные по мере готовности. Это позволяет перекрывать задержки: ASR уже отправляет части текста в LLM, пока TTS ещё воспроизводит предыдущий чанк.

Целевой бюджет latency (в мс):

КомпонентТипичная задержкаКомментарий
ASR (first token)100–200Зависит от модели и размера чанка
LLM (first token)50–150Маленькая модель, streaming
TTS (first audio)50–100Chunked синтез
Транспорт (RTT)20–50WebRTC, UDP
Буферизация/джиттер30–50Для плавности
Итого250–550Цель <500 мс

3. ASR: выбор модели и streaming

Основные требования низкая задержка первого токена (first token latency), поддержка streaming (online decoding), работа на CPU/GPU.

Варианты

  • Whisper streaming (faster-whisper с online decoding) — использует модель Whisper (small или base) с чанкованием аудио по 200–400 мс. Выдаёт частичные гипотезы.
  • Fast-Conformer (NVIDIA NeMo) — специально разработан для real-time ASR, latency ~100 мс, поддерживает CTC и Transducer.
  • Kaldi / Vosk — лёгкие, но менее точные.

Рекомендация faster-whisper (small) с размером чанка 320 мс и перекрытием 50 мс. На CPU даёт latency ~150 мс, на GPU ~80 мс.

Ключевые настройки

  • Размер чанка (chunk size) — компромисс между latency и качеством.
  • VAD (Voice Activity Detection) — отсекает тишину, снижая нагрузку.
  • Endpoint detection — определяет конец фразы для отправки в LLM.

4. LLM: маленькая модель и streaming

Требования генерация первого токена за <100 мс, поддержка streaming (token-by-token), возможность работы на edge.

Варианты

Инференс-движки

Streaming LLM генерирует токены по одному и сразу отправляет в TTS. Не нужно ждать полного ответа.

Промпт: должен быть коротким (системный промпт + история диалога), чтобы минимизировать время префилла (prefill latency).


5. TTS: низкая задержка и chunked синтез

Требования синтез первого аудиочанка за <50 мс, поддержка streaming (incremental synthesis).

Варианты

  • Piper TTS — лёгкая модель (VITS-based), latency ~15 мс на CPU, поддерживает chunked вывод.
  • ElevenLabs Turbo — облачный сервис, latency ~50 мс, но требует интернета.
  • Coqui TTS (XTTS) — качественный, но тяжелее.
  • Microsoft Speech T5 — экспериментальный, low latency.

Рекомендация Piper TTS (quantized) на edge-устройстве. Для облачного варианта — ElevenLabs Turbo с WebSocket.

Chunked синтез TTS начинает воспроизведение, как только сгенерирован первый аудиочанк (например, 200 мс), не дожидаясь полного текста.


6. Транспорт: WebRTC

Почему WebRTC

  • Использует UDP — нет retransmission, низкая задержка.
  • Встроенная поддержка аудиокодеков (Opus, G.711).
  • DTLS/SRTP — шифрование без блокировок.
  • P2P архитектура — снижает нагрузку на сервер.

Альтернативы

  • WebSocket (TCP) — может быть медленнее из-за TCP backpressure.
  • HTTP/2 Server-Sent Events — только для текста.

Реализация на сервере используем библиотеку aiortc (Python) или mediasoup (Node.js). Клиент — браузер или нативное приложение.

Оптимизация

  • Минимальный размер аудиочанка (20 мс для Opus).
  • Настройка jitter buffer (30–50 мс).
  • Использование Simulcast для адаптации к сети.

7. Архитектура пайплайна: параллельные потоки

Ключевой принцип все компоненты работают асинхронно и передают данные через очереди (asyncio.Queue, Kafka, Redis Streams).

Схема потоков

  1. Audio capture → VAD → ASR (streaming) → partial text.
  2. Partial text накапливается в буфере; при обнаружении endpoint (пауза >300 мс) отправляется в LLM.
  3. LLM генерирует токены и отправляет их в TTS по мере появления.
  4. TTS синтезирует аудиочанки и отправляет в WebRTC output.

Без блокировок каждый этап не ждёт завершения предыдущего. Например, ASR может отправлять новый текст, пока LLM ещё генерирует ответ на предыдущий.

Управление очередями

  • Приоритет: ответ на текущий запрос имеет приоритет перед новым.
  • Drop policy: если очередь переполнена, старые чанки отбрасываются.

8. Метрики и мониторинг

End-to-end latency: измеряется от момента, когда пользователь закончил говорить, до начала воспроизведения ответа. Инструменты: Wireshark, собственные логи с метками времени.

Per-component latency

КомпонентМетрикаЦель
ASRFirst token latency<150 мс
LLMTime to first token<100 мс
TTSFirst audio chunk<50 мс
TransportRTT<30 мс

Джиттер (jitter): вариация задержки между пакетами. Должен быть <20 мс, иначе слышны «заикания».

Качество речи MOS (Mean Opinion Score) >3.5 для TTS, WER (Word Error Rate) <5% для ASR.


9. Оптимизации для достижения <500 мс

  • Quantization LLM и TTS в INT4/INT8 — снижает latency на 30–50%.
  • Batching если несколько пользователей, используем continuous batching в vLLM.
  • Hardware acceleration GPU (T4, A10) для LLM, GPU/CPU для ASR и TTS.
  • Edge deployment размещение моделей на устройстве пользователя (например, в браузере через WebAssembly) — исключает сетевую задержку.
  • Prefill caching кэширование KV-cache для частых промптов.
  • Speculative decoding для LLM — генерация нескольких токенов за шаг.

10. Trade-offs

АспектВысокое качествоНизкая latency
Модель ASRWhisper largefaster-whisper small
Модель LLMLlama-3-8BPhi-3-mini / Llama-3-1B
Модель TTSElevenLabs (cloud)Piper (edge)
ТранспортWebSocket (TCP)WebRTC (UDP)
Размер чанкаБольшой (1 с)Маленький (200 мс)

Компромисс для <500 мс приходится жертвовать качеством (WER, perplexity, MOS). В production обычно выбирают средний вариант: faster-whisper medium + Phi-3-mini + Piper TTS.


11. Пример реализации (псевдокод)

import asyncio
from aiortc import RTCPeerConnection, MediaStreamTrack
from faster_whisper import WhisperModel
from transformers import pipeline
import piper_tts

# Инициализация моделей
asr_model = WhisperModel("base", device="cpu", compute_type="int8")
llm_pipeline = pipeline("text-generation", model="microsoft/phi-3-mini", device=0)
tts_engine = piper_tts.PiperVoice("voice_model.onnx")

class AudioTrack(MediaStreamTrack):
    kind = "audio"
    async def recv(self):
        # Получение аудиочанка от WebRTC
        frame = await super().recv()
        # Отправка в ASR
        asr_queue.put(frame.to_ndarray())

async def asr_worker():
    buffer = []
    while True:
        chunk = await asr_queue.get()
        buffer.append(chunk)
        if len(buffer) >= 10:  # ~200 мс
            audio = np.concatenate(buffer)
            segments, _ = asr_model.transcribe(audio, beam_size=1, vad_filter=True)
            text = " ".join(seg.text for seg in segments)
            if text.endswith(".") or text.endswith("?"):
                llm_queue.put(text)
                buffer.clear()

async def llm_worker():
    while True:
        prompt = await llm_queue.get()
        # Streaming generation
        for token in llm_pipeline(prompt, max_new_tokens=128, return_full_text=False):
            tts_queue.put(token["generated_text"])

async def tts_worker():
    while True:
        text = await tts_queue.get()
        audio_chunk = tts_engine.synthesize(text, chunk_size=200)  # 200 ms
        # Отправка через WebRTC
        await pc.send(audio_chunk)

# Запуск
pc = RTCPeerConnection()
pc.addTrack(AudioTrack())
asyncio.run(asyncio.gather(asr_worker(), llm_worker(), tts_worker()))

Пет-проект для закрепления

Задача построить голосового ассистента, который отвечает на вопросы о погоде с latency <500 мс.

Инструменты

Шаги:

  1. Установить зависимости: faster-whisper, llama-cpp-python, piper-tts, aiortc.
  2. Настроить ASR: streaming с чанками 320 мс, VAD.
  3. Настроить LLM: загрузить Phi-3-mini Q4_K_M, создать промпт с контекстом погоды.
  4. Настроить TTS: загрузить модель Piper для русского языка.
  5. Интегрировать через WebRTC: клиент (браузер) отправляет аудио, сервер обрабатывает и возвращает ответ.
  6. Измерить latency: добавить логи с метками времени.

Ожидаемый результат работающий voice agent, который отвечает на вопросы «Какая погода в Москве?» с задержкой 300–450 мс.


Связь с другими вопросами

ВопросТема
7Как уменьшить latency RAG-системы?
12Как реализовать streaming в LLM?
15Какие методы оптимизации моделей вы знаете?
20Как работает WebRTC и когда его использовать?
30Как выбрать ASR для real-time приложения?
45Как оценить качество TTS?

Навигация