Как вы строите 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–100 | Chunked синтез |
| Транспорт (RTT) | 20–50 | WebRTC, 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.
Варианты
- Llama-3-1B (1.3B параметров) — quantized (INT4/INT8) даёт latency ~50 мс на GPU.
- Phi-3-mini (3.8B) — хорошее качество, но требует GPU.
- Gemma-2B — альтернатива.
Инференс-движки
- vLLM — поддерживает continuous batching и streaming, latency ~30 мс на A10.
- Groq — аппаратное ускорение (LPU), latency <10 мс для маленьких моделей.
- llama.cpp — для CPU, quantized 4-bit, latency ~100 мс.
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).
Схема потоков
- Audio capture → VAD → ASR (streaming) → partial text.
- Partial text накапливается в буфере; при обнаружении endpoint (пауза >300 мс) отправляется в LLM.
- LLM генерирует токены и отправляет их в TTS по мере появления.
- TTS синтезирует аудиочанки и отправляет в WebRTC output.
Без блокировок каждый этап не ждёт завершения предыдущего. Например, ASR может отправлять новый текст, пока LLM ещё генерирует ответ на предыдущий.
Управление очередями
- Приоритет: ответ на текущий запрос имеет приоритет перед новым.
- Drop policy: если очередь переполнена, старые чанки отбрасываются.
8. Метрики и мониторинг
End-to-end latency: измеряется от момента, когда пользователь закончил говорить, до начала воспроизведения ответа. Инструменты: Wireshark, собственные логи с метками времени.
Per-component latency
| Компонент | Метрика | Цель |
|---|---|---|
| ASR | First token latency | <150 мс |
| LLM | Time to first token | <100 мс |
| TTS | First audio chunk | <50 мс |
| Transport | RTT | <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 |
|---|---|---|
| Модель ASR | Whisper large | faster-whisper small |
| Модель LLM | Llama-3-8B | Phi-3-mini / Llama-3-1B |
| Модель TTS | ElevenLabs (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 мс.
Инструменты
- ASR faster-whisper (small) с online decoding.
- LLM Phi-3-mini (quantized 4-bit) через llama.cpp.
- TTS Piper TTS (русский голос).
- Транспорт WebRTC (aiortc).
- Данные API погоды (OpenWeatherMap) для ответов.
Шаги:
- Установить зависимости: faster-whisper, llama-cpp-python, piper-tts, aiortc.
- Настроить ASR: streaming с чанками 320 мс, VAD.
- Настроить LLM: загрузить Phi-3-mini Q4_K_M, создать промпт с контекстом погоды.
- Настроить TTS: загрузить модель Piper для русского языка.
- Интегрировать через WebRTC: клиент (браузер) отправляет аудио, сервер обрабатывает и возвращает ответ.
- Измерить latency: добавить логи с метками времени.
Ожидаемый результат работающий voice agent, который отвечает на вопросы «Какая погода в Москве?» с задержкой 300–450 мс.
Связь с другими вопросами
| Вопрос | Тема |
|---|---|
| 7 | Как уменьшить latency RAG-системы? |
| 12 | Как реализовать streaming в LLM? |
| 15 | Какие методы оптимизации моделей вы знаете? |
| 20 | Как работает WebRTC и когда его использовать? |
| 30 | Как выбрать ASR для real-time приложения? |
| 45 | Как оценить качество TTS? |
Навигация
- Предыдущий: 543
- Следующий: 545
- Индекс: 00. Индекс разборов