Как мы выжали 180 миллисекунд задержки из локального голосового ИИ на обычном железе
Девяносто процентов открытых туториалов по голосовым ассистентам собирают одну и ту же нежизнеспособную схему.
Сначала микрофон две секунды копит звук в массив, чтобы VAD убедился в конце фразы. Потом тяжелый Whisper полсекунды пережевывает пятисекундный WAV-файл. Затем модель неспешно генерирует три абзаца текста, синтезатор переводит этот кирпич в аудио, и только через три секунды в динамиках раздается первый звук. Ощущение такое, будто звонишь по межгороду через спутниковый модем в 1998 году.
Человеческий мозг устроен жестко. Если собеседник молчит дольше 300 миллисекунд, диалог разваливается, а пауза длиннее секунды воспринимается как зависание программы.
Платить по тридцать центов за минуту в облачный Realtime API от OpenAI — сомнительное удовольствие, особенно если бот должен постоянно слушать рабочий кабинет в фоне.
Мы поставили задачу собрать полностью локальный конвейер на обычном ноутбуке с задержкой первого ответа не выше 200 миллисекунд. Без облаков, без платных API и без компромиссов по естественности голоса.
В чем математика задержки
Чтобы получить мгновенный отклик, нужно полностью отказаться от последовательного выполнения шагов.
Кстати, если вы хотите протестировать все самые свежие модели уровня Claude Sonnet 5, GPT-5.6 или Gemini 3 в одном удобном месте и сравнить их ответы без костылей с иностранными картами — на платформе SYNTX.AI это можно сделать за пару кликов.
По промокоду NEIROSKUF дадут 15% скидку на все тарифы.
В классическом пайплайне каждый компонент ждет, пока предыдущий закончит свою работу целиком. В стриминговом конвейере данные непрерывно текут сквозь три параллельных асинхронных процесса.
Вот реальный лог обработки одного короткого вопроса пользователя («Какая сейчас загрузка на сервере авторизации?»):
Человек слышит начало ответа меньше чем через треть секунды. Иллюзия живого собеседника сохраняется на сто процентов.
Аудиоинженерия: кольцевой буфер и обход GIL в Python
Самое узкое место в потоковом звуке на Python — это блокировка интерпретатора (GIL). Если читать аудиопоток из микрофона напрямую в асинхронном цикле asyncio, тяжелые вычисления нейросетей вызывают микрозадержки в 50-100 миллисекунд, и звуковой драйвер вылетает с ошибкой опустошения буфера (buffer underflow).
Решение — изоляция звукового ввода-вывода в отдельный нативный C-поток через библиотеку sounddevice или PyAudio.
Мы создаем lock-free кольцевой буфер (circular ring buffer) в памяти. Нативный callback драйвера PortAudio непрерывно складывает туда сырые PCM-байты с частотой 16 кГц в формате 16-битных целых чисел (Int16). Асинхронный цикл Python забирает данные из буфера порциями по 512 сэмплов через метод loop.call_soon_threadsafe(). Никаких блокировок и потерь сэмплов.
Детекция пауз и гистерезис в Silero VAD
Стандартный VAD по уровню энергии звука не годится: он реагирует на щелчки клавиатуры и шум вентилятора.
Мы используем модель Silero VAD v5, запущенную через легковесный рантайм ONNX Runtime на процессоре. Модель сохраняет скрытое состояние между фреймами (stateful recurrent architecture) и анализирует спектрограмму каждые 32 миллисекунды.
Чтобы отсекать тишину мгновенно и не обрезать окончания слов, настраивается гистерезис по вероятности речи:
- Порог входа (speech threshold): 0.5. Как только модель видит вероятность речи выше 50%, начинается накопление аудио.
- Порог выхода (exit threshold): 0.35. Голос считается затихшим, только когда вероятность падает ниже 35%.
- Окно подтверждения тишины: ровно 120 миллисекунд. Этого достаточно, чтобы отличить микропаузу между словами от завершения реплики.
Распознавание речи: CTranslate2 против стандартного PyTorch
Оригинальный Whisper от OpenAI на чистом PyTorch слишком тяжелый для реального времени: даже модель small на пятисекундном отрезке думает около 350 миллисекунд.
Мы берем движок Faster-Whisper, собранный на библиотеке CTranslate2. Он использует кастомные ядра FP16/INT8 с тензорными оптимизациями под архитектуры Nvidia и Apple Silicon. Модель medium на том же отрезке отрабатывает за 75-90 миллисекунд.
При инициализации обязательно выставляем параметры: beam_size=1 (жадный поиск без тяжелого перебора лучей), vad_filter=False (звук уже нарезан нашим Silero VAD) и condition_on_previous_text=False (исключает галлюцинации и зацикливание на предыдущих репликах).
Потоковая нарезка токенов: рабочий код буфера
Языковая модель не должна генерировать весь ответ целиком до включения синтезатора. Слушателю для непрерывного восприятия достаточно услышать первые четыре слова.
Мы подключаем локальную модель (например, Qwen 2.5 7B на движке vLLM) в режиме токен-стриминга. Время получения первого токена (TTFT) составляет около 35 миллисекунд.
Специальный асинхронный генератор перехватывает поток выходящих токенов и накапливает их в строковом буфере ровно до первого знака препинания.
Вот минимальный и надежный код такого буфера на Python, который использует регулярное выражение с просмотром назад (lookbehind), не ломая дробные числа вроде 3.14 и сокращения:

telegram.metelegram.meИсточник: vc.ru
Похожие записи
Оцените материал:
Похожие записи
Oracle предупреждает об уязвимости в системе безопасности, которую хакеры использовали для взлома более чем 100 компаний.
13.06.2026
История (и код на github) про то, как ChatGPT подружил проектный телеграм-чатик и таски в Jira
14.10.2025
Считалось, что искусственный интеллект уничтожит инженерные профессии, но новые данные показывают, что они оказались наиболее устойчивыми.
25.06.2026Присоединяйтесь и подпишитесь на рассылку самых свежих новостей по Email
Получайте свежие новости и идеи на почту. Без спама — только самое интересное.
Нажимая «Подписаться», вы соглашаетесь с политикой конфиденциальности.
