Аудио в Gemma-4: исправляем пайплайн VAD-LLM-TTS | Гайд 2026 | AiManual
AiManual Logo Ai / Manual.
08 Апр 2026 Гайд

Как заставить работать аудио в Gemma-4: решение проблем пайплайна VAD-LLM-TTS

Пошаговое решение проблем аудиопаплайна в Gemma-4 с llama.cpp, Unsloth Studio и LiteRT LM. Запускаем VAD-LLM-TTS на GPU без задержек.

Ты собрал аудиопаплайн для Gemma-4. И он не работает

Картина знакомая. Скачал свежую мультимодальную Gemma-4, которая в теории должна и слушать, и говорить. Поставил VAD для детекта речи. Подключил модель через llama.cpp. Нацепил TTS на выход. Запускаешь – и получаешь либо тишину, либо дикие лаги, либо падение со словами "CUDA out of memory". Знакомо? Это не ты криворукий. Это стандартный ад, который ждет всех, кто пытается заставить аудио работать в локальных LLM без правильного стека.

Проблема в том, что большинство гайдов до сих пор предлагают CPU-only пайплайны. Аудио обрабатывается в одном потоке, модель тупит на CPU, а TTS ждет своей очереди. Результат – задержка в 5-10 секунд, что для голосового ассистента смертельно. Нам нужен другой подход.

Актуальность на 08.04.2026: Речь идет о Gemma-4 14B, последней на этой дате мультимодальной версии от Google. llama.cpp поддерживает GPU inference через CUDA 12.4 и Vulkan 1.3. Unsloth Studio обновлен до версии 2026.1, а LiteRT LM – это новая легковесная библиотека, специально заточенная под реальное время.

Почему стандартный пайплайн VAD-LLM-TTS ломается?

Давай разберем по косточкам. Классическая цепочка: Voice Activity Detection (VAD) → Транскрипция/понимание через LLM → Синтез речи (TTS). На бумаге все просто. В реальности:

  • VAD работает в отдельном процессе и кидает куски аудио в очередь. Если LLM медленная, очередь переполняется.
  • Gemma-4 на CPU – это 2-3 токена в секунду для 14B модели. Для ответа из 50 слов ждать полминуты.
  • TTS, особенно нейронный, тоже требует GPU. Без него синтез тормозит весь поток.
  • Самое мерзкое – бутылочное горло в передаче данных между процессами. Аудиобуферы, текстовые строки, управляющие сигналы – все это создает лаг.

Решение – перевести всю цепочку на GPU, максимально распараллелить и использовать инструменты, которые не просто работают, а созданы для реального времени. Как в нашем гайде про убийство задержки в голосовом AI.

1Собираем правильный инструментарий

Забудь про чистый Ollama и стандартные биндинги. Нам нужны специализированные инструменты, которые умеют жать каждую миллисекунду.

ИнструментЗачем нуженВерсия на 08.04.2026
llama.cpp с GPUИнференс Gemma-4 на GPU с квантованием. Поддержка непрерывного батчинга.Commit с CUDA 12.4
Unsloth StudioУскорение инференса в 2-5 раз за счет кастомных ядер и оптимизаций памяти.2026.1
LiteRT LMЛегковесная обертка для запуска моделей с приоритетом на низкую задержку.v0.4.2
Silero VADДетект речи с минимальной задержкой. Может работать на GPU.v4.1
KittenTTS или AnyTTSСинтез речи с поддержкой потокового вывода.KittenTTS 2.3, AnyTTS 1.8

Первое, что делаем – собираем llama.cpp с поддержкой CUDA. Не ту версию, что в репозиториях (там часто старая), а свежую с GitHub.

git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp
make LLAMA_CUDA=1 -j$(nproc)
💡
Если у тебя не NVIDIA, а AMD или Intel, используй LLAMA_VULKAN=1. Но учти, что оптимизации под CUDA в Unsloth Studio могут не сработать. Для интеграции с Apple Silicon есть отдельный гайд про запуск Gemma 4 через MLX.

2Квантуем Gemma-4 для скорости, а не только для экономии памяти

Берем оригинальную Gemma-4 14B (не инструктивную, а базовую мультимодальную) и квантуем в формат Q4_K_M. Почему не Q8 или тем более FP16? Потому что нам нужен баланс между скоростью и качеством. Q4_K_M на GPU дает ускорение в 3-4 раза с минимальной потерей точности для аудиозадач.

# Конвертируем Hugging Face модель в GGUF
python3 convert-hf-to-gguf.py /path/to/gemma-4-14b --outtype q4_k_m

# Квантуем через llama.cpp
./quantize ./models/gemma-4-14b.gguf ./models/gemma-4-14b-q4_k_m.gguf q4_k_m

Вот здесь многие ошибаются – квантуют в самый агрессивный формат Q2_K, а потом удивляются, что модель перестала понимать контекст аудио. Для мультимодальных моделей, которые работают с аудиоэмбеддингами, лучше не опускаться ниже Q4.

3Интегрируем Unsloth Studio и LiteRT LM

Теперь магия. Устанавливаем Unsloth Studio – он умеет патчить llama.cpp для использования более эффективных ядер матричного умножения.

pip install unsloth-studio==2026.1
pip install litert-lm==0.4.2

Создаем простой скрипт инициализации, который загружает модель один раз и держит ее в памяти GPU, готовую к непрерывному приему запросов. Это ключевое отличие от обычного llama.cpp, который для каждого запроса перезагружает контекст.

from litert_lm import LiteRTModel
import torch

model = LiteRTModel(
    model_path="./models/gemma-4-14b-q4_k_m.gguf",
    max_batch_size=4,  # Обрабатываем до 4 аудиосегментов параллельно
    continuous_batching=True,  # Непрерывный батчинг – жизнь без ожидания
    gpu_layers=99,  # Все слои на GPU
)

# Вместо стандартного generate используем потоковый вариант
def process_audio_transcript(transcript):
    stream = model.generate(
        transcript,
        max_tokens=150,
        stream=True,
        temperature=0.7,
    )
    for token in stream:
        yield token  # Отдаем токены по мере генерации, не дожидаясь конца

Этот подход сокращает задержку между окончанием речи пользователя и началом ответа модели с 10+ секунд до 1-2 секунд. Как в статье про SAM-Audio, где мы экономили VRAM, здесь мы экономим время.

4Собираем VAD в реальном времени, а не для галочки

Берем Silero VAD, но не стандартную версию, а ту, что умеет работать на GPU через ONNX Runtime с CUDA execution provider. Зачем? Чтобы детект речи не съедал CPU, который нам нужен для управления потоками.

import onnxruntime as ort
import numpy as np

# Создаем сессию ONNX с CUDA
providers = [
    ('CUDAExecutionProvider', {
        'device_id': 0,
        'arena_extend_strategy': 'kNextPowerOfTwo',
        'gpu_mem_limit': 2 * 1024 * 1024 * 1024,  # 2 GB лимит
        'cudnn_conv_algo_search': 'EXHAUSTIVE',
        'do_copy_in_default_stream': True,
    }),
    'CPUExecutionProvider',
]
session = ort.InferenceSession('silero_vad.onnx', providers=providers)

# Функция для обработки аудиобуфера
def detect_speech(audio_chunk):
    # audio_chunk: numpy array, 16 kHz mono
    input_data = {
        session.get_inputs()[0].name: audio_chunk.astype(np.float32)
    }
    out = session.run(None, input_data)
    is_speech = out[0].item() > 0.5  # Порог можно настраивать
    return is_speech

Этот VAD мы интегрируем в отдельный поток, который постоянно слушает микрофон или аудиопоток (например, из WebRTC). Как только детектируется конец речи, сегмент аудио сразу отправляется в ASR (если нужна транскрипция) или прямо в Gemma-4, если она умеет работать с аудио напрямую (через аудиоэмбеддинги).

5Подключаем TTS, который не тормозит

Здесь два варианта. Если хочешь максимальное качество и есть лишние 4-6 ГБ VRAM, ставим KittenTTS сервер, как в нашем гайде по развертыванию на Windows. Если ресурсы ограничены, берем AnyTTS – он легче и умеет работать с разными бэкендами.

# Пример с AnyTTS через его API
import requests
import json

def synthesize_speech(text, voice="ru_RU-ai_ru-ekaterina"):
    # AnyTTS работает как отдельный сервис
    response = requests.post(
        'http://localhost:8001/synthesize',
        json={
            'text': text,
            'voice': voice,
            'stream': True  # Ключевой параметр – начинаем отдавать аудио сразу
        },
        stream=True
    )
    for chunk in response.iter_content(chunk_size=1024):
        yield chunk  # Потоковое аудио, можно сразу в аудиовыход

Важно: TTS сервер должен быть на том же GPU или отдельной карте. Не пытайся запускать и LLM, и TTS на одном слабом GPU – будут троттлинг и артефакты. Лучше распределить: LLM на основную карту, TTS на вторую (если есть) или на CPU, но с легкой моделью.

Собираем пазл: архитектура всего пайплайна

Теперь нужно связать все компоненты в одну систему. Рисуем архитектуру в голове:

  1. Входной аудиопоток (микрофон, WebRTC, файл) идет в VAD модуль на GPU.
  2. Обнаруженные сегменты речи буферизуются и отправляются в ASR (если используется) или прямо в Gemma-4. Для ASR можно взять SLAY-ASR – легковесную модель.
  3. Gemma-4 через LiteRT LM получает текст (или аудиоэмбеддинги) и генерирует ответ потоково.
  4. Каждый сгенерированный токен сразу отправляется в TTS сервер с флагом stream=True.
  5. TTS начинает синтез еще до завершения генерации полного ответа. Это создает эффект почти мгновенного ответа.

Реализуем это на Python с asyncio. Главное – правильно настроить очереди и семафоры, чтобы не перегрузить память.

import asyncio
from queue import Queue
from threading import Thread

# Очереди для межпоточного обмена
audio_queue = Queue(maxsize=10)  # Сегменты аудио
text_queue = Queue(maxsize=10)   # Транскрипты/запросы
tts_queue = Queue(maxsize=10)    # Токены для TTS

async def pipeline():
    # Запускаем все компоненты в отдельных потоках/процессах
    vad_thread = Thread(target=run_vad, args=(audio_queue,))
    llm_thread = Thread(target=run_llm, args=(text_queue, tts_queue,))
    tts_thread = Thread(target=run_tts, args=(tts_queue,))
    
    vad_thread.start()
    llm_thread.start()
    tts_thread.start()
    
    # Главный цикл управления
    while True:
        await asyncio.sleep(0.001)  # Не блокируем event loop
        # Контролируем заполненность очередей, при необходимости сбрасываем старые данные
        if audio_queue.qsize() > 8:
            audio_queue.get()  # Выкидываем самый старый сегмент, чтобы не копилось

Предупреждение: Не используй обычные списки или глобальные переменные для обмена данными между потоками. Только потокобезопасные очереди (Queue) или asyncio.Queue. Иначе будут race conditions и падения.

Типичные ошибки и как их избежать

Даже с правильными инструментами можно наступить на грабли. Вот список самых частых проблем:

  • "CUDA out of memory" после запуска TTS. Происходит, когда LLM и TTS борются за память. Решение: ограничь память для каждого процесса через CUDA_VISIBLE_DEVICES или torch.cuda.set_per_process_memory_fraction(). Либо используй техники экономии VRAM.
  • Задержка между предложениями в TTS. TTS ждет точки, чтобы начать синтез. Включи флаг stream=True и отправляй текст кусками по 2-3 слова. Да, синтез будет менее естественным, но зато без пауз.
  • VAD ловит шумы как речь. Настрой порог детекции (threshold) под твое окружение. В тихой комнате можно поставить 0.3, в шумном офисе – 0.7. Лучше сделать калибровку в начале сессии.
  • Gemma-4 теряет контекст в длинном диалоге. Убедись, что используешь непрерывный батчинг и не сбрасываешь контекст между запросами. В LiteRT LM есть параметр keep_alive.

Если нужен готовый пример такого пайплайна в действии, посмотри на проект из статьи про голосовых NPC в Unity. Там похожая архитектура, но для игр.

Что в итоге? Аудио работает, но требует тонкой настройки

Полноценный аудиопаплайн для Gemma-4 на GPU – это не пять строк кода. Это сборка из специфичных инструментов, каждый из которых решает свою задачу с минимальной задержкой. Основные выводы:

  • Не используй CPU для инференса LLM в реальном времени. Только GPU с квантованием Q4_K_M или выше.
  • Бери llama.cpp с поддержкой CUDA/Vulkan, патчи его через Unsloth Studio и управляй через LiteRT LM для низкой задержки.
  • VAD и TTS тоже должны работать на GPU, либо быть максимально легковесными.
  • Потоковая обработка от начала до конца – ключ к отсутствию лагов. Никакого ожидания полного предложения.

И последнее. Не гонись за самым большим контекстом. Для голосового диалога достаточно 4096 токенов. Увеличивая контекст до 32k, ты добавляешь задержку на attention слоях. Gemma-4 и так умная, дай ей только самое необходимое. Как в том гайде про настройку для перевода игр, где мы резали контекст для скорости.

Теперь у тебя есть план. Собирай, тестируй, настраивай под свое железо. А когда заработает – сделай так, чтобы TTS говорил с эмоциями. Но это уже другая история.

Подписаться на канал