Локальный STT на Linux без облаков, подписок и компромиссов

Я печатаю быстро. Где-то 90 слов в минуту, если верить тестам. Но есть ситуации, когда клавиатура просто мешает. Длинное описание бага с контекстом на три абзаца. Диктовка мыслей, пока идёшь за кофе. Объяснение задачи Claude Code, когда проще рассказать голосом, чем формулировать текстом. На macOS и Windows голосовой ввод давно встроен в систему. На Linux всё сложнее — готовых решений уровня «нажал кнопку и диктуешь» здесь нет. Точнее, не было.

В этой статье разберём проект voice-setup — готовую конфигурацию для локального распознавания речи на GPU. Whisper для STT, Kokoro для озвучки ответов, VoiceMode MCP для интеграции с Claude Code. Всё крутится в Docker, ничего не уходит в облако.

Зачем вообще локальный STT

Облачные сервисы распознавания речи работают отлично. Google Speech-to-Text, OpenAI Whisper API, Deepgram — выбирай на вкус. Проблема в другом.

Во-первых, приватность. Каждый фрагмент аудио улетает на чужой сервер. Если вы диктуете описание внутренней архитектуры или обсуждаете рабочие задачи, это может быть неприемлемо. Во-вторых, задержка. Даже при хорошем интернете сетевой round-trip добавляет 200–500 мс поверх самого распознавания. Для коротких команд это заметно. И в-третьих, деньги. Whisper API стоит $0.006 за минуту — немного, но при активном использовании набегает. Локальный Whisper на вашей видеокарте — это ноль рублей навсегда.

Проект voice-setup решает все три проблемы разом: ставит Whisper и Kokoro TTS в Docker-контейнеры с GPU-ускорением, подключает их к Claude Code через MCP-протокол, а бонусом даёт универсальный скрипт для голосового ввода в любое приложение на Linux.

Что внутри

Архитектура простая. Два Docker-контейнера с GPU-доступом, один MCP-сервер на Python, клиент в виде Claude Code.

Whisper STT слушает на порту 2022 и предоставляет OpenAI-совместимый API. Kokoro TTS — на порту 8880, тоже OpenAI-совместимый. VoiceMode MCP связывает микрофон с Claude Code: вы говорите «послушай», дожидаетесь сигнала, произносите задачу — Claude получает распознанный текст и работает с ним как с обычным промптом.

Kokoro — open-weight модель с 82 миллионами параметров. Занимает примерно 1 ГБ VRAM. Лицензия Apache 2.0, качество озвучки на уровне коммерческих решений (модель заняла первое место в HuggingFace TTS Arena для single-speaker). Если озвучка ответов не нужна — контейнер с Kokoro можно просто не запускать.

Требования к железу

Для работы нужна видеокарта NVIDIA с поддержкой CUDA. Модель Whisper large-v3 занимает около 3 ГБ VRAM, Kokoro — около 1 ГБ. На карте с 12 ГБ (RTX 4070 SUPER, например) остаётся ещё 8 ГБ для всего остального — Ollama, inference других моделей, что угодно.

Если VRAM мало, можно выбрать модель полегче:

Модель VRAM Латентность Качество распознавания русского
faster-whisper-tiny ~1 ГБ мгновенно слабое
faster-whisper-small ~2 ГБ очень быстро среднее
faster-whisper-medium ~5 ГБ быстро хорошее
faster-whisper-large-v3 ~10 ГБ 300–500 мс отличное

Для русского языка я бы не рекомендовал опускаться ниже medium. Tiny и small сносно работают с английским, но на русском начинают путать окончания, глотать предлоги и выдавать кашу на длинных предложениях. Large-v3 стоит по умолчанию — и это правильный выбор, если карта тянет.

Немного цифр для контекста: оригинальный Whisper large-v3 от OpenAI показывает WER (Word Error Rate) около 9.84% на русском по датасету Common Voice 17.0. Существует дообученная версия antony66/whisper-large-v3-russian, которая снижает WER до 6.39%. Проект voice-setup использует стандартную large-v3, но при желании модель можно заменить одной строкой в docker-compose.yml.

Установка

Минимальный путь — клонировать репозиторий и запустить скрипт:

git clone https://gitlab.com/avtobys/voice-setup.git
cd voice-setup
chmod +x setup.sh
./setup.sh

Скрипт поставит системные зависимости (ffmpeg, portaudio, pulseaudio), установит uv (менеджер пакетов для Python от Astral), поднимет Docker-контейнеры и настроит VoiceMode MCP. Первый запуск скачает модель large-v3 — это примерно 3 ГБ, так что на медленном интернете придётся подождать.

После установки нужно прописать переменные окружения. В репозитории есть файл bashrc-snippet.sh — его содержимое добавляется в ~/.bashrc:

export STT_BASE_URL="http://127.0.0.1:2022/v1"
export VOICEMODE_STT_BASE_URLS="http://127.0.0.1:2022/v1"
export TTS_BASE_URL="http://127.0.0.1:8880/v1"
export VOICEMODE_TTS_BASE_URLS="http://127.0.0.1:8880/v1"
export VOICEMODE_VOICES="af_sky"

Проверить, что всё работает:

curl http://localhost:2022/v1/models   # Whisper
curl http://localhost:8880/v1/models   # Kokoro

Если оба отвечают — готово. Дальше в Claude Code пишете «послушай» или «listen», говорите задачу, и Claude работает с ней.

Голосовой ввод без Claude Code: скрипт на хоткей

Самая практичная часть проекта — это даже не интеграция с Claude Code, а простой bash-скрипт для голосового ввода в любое приложение. Первое нажатие хоткея начинает запись, второе — останавливает, распознаёт и вставляет текст в активное окно через xdotool.

#!/bin/bash
PIDFILE=/tmp/rec.pid
WAVFILE=/tmp/rec.wav

if [ -f "$PIDFILE" ]; then
    kill $(cat $PIDFILE)
    rm $PIDFILE
    notify-send "🎤 STT" "Распознаю..." -t 1500
    sleep 0.2
    TEXT=$(curl -s http://localhost:2022/v1/audio/transcriptions \
        -F file=@$WAVFILE \
        -F "model=Systran/faster-whisper-large-v3" \
        -F language=ru \
        | jq -r .text | tr -d '\n')
    xdotool type --clearmodifiers --delay 20 "$TEXT "
    notify-send "✅ STT" "$TEXT" -t 3000
else
    arecord -f cd -t wav $WAVFILE &
    echo $! > $PIDFILE
    notify-send "🔴 STT" "Запись..." -t 6000
fi

Сохраняете как ~/.local/bin/stt, делаете chmod +x, назначаете на хоткей (Super+V, Ctrl+Alt+S — что удобнее). Дальше это работает везде: в браузере, в IDE, в мессенджере, в терминале. Зависимости минимальные: arecord, curl, jq, xdotool, notify-send.

Я повесил это на Super+V и за неделю привык настолько, что теперь без этого хоткея чувствую себя как без буфера обмена. Особенно удобно для длинных сообщений в Telegram и описаний тикетов в Jira — ситуации, где печатать лень, а надиктовать за 30 секунд нормально.

Нормализация текста через Ollama

Whisper распознаёт хорошо, но не идеально. Бывают лишние запятые, неправильные окончания, слипшиеся слова. Если на машине уже крутится Ollama (а у кого из нас она не крутится), можно прогонять распознанный текст через локальную LLM для исправления.

В проекте есть расширенная версия скрипта, которая делает именно это. Штука в том, что скрипт определяет активное окно и передаёт его название в промпт. LLM получает контекст: если диктуешь в терминал — она не будет «исправлять» команды, если в редактор кода — учтёт техническую лексику.

# Контекст активного окна
WID=$(xdotool getactivewindow)
WINDOW_NAME=$(xdotool getwindowname $WID 2>/dev/null || echo "unknown")
PROCESS=$(ps -p $(xdotool getwindowpid $WID) -o comm= 2>/dev/null || echo "unknown")

# Нормализация через Ollama
PAYLOAD=$(jq -n \
    --arg text "$TEXT" \
    --arg window "$WINDOW_NAME" \
    --arg process "$PROCESS" \
    '{model: "qwen3.5:4b", stream: false, think: false,
      prompt: "Исправь опечатки и ошибки распознавания речи в тексте.
      Пользователь вводит текст в программе \($process) (окно: \($window)).
      Верни ТОЛЬКО исправленный текст.\nТекст: \($text)"}')

Qwen 3.5 4B справляется за доли секунды. Суммарная задержка от окончания записи до появления текста — около секунды на RTX 4070 SUPER (Whisper ~400 мс + Ollama ~300 мс + накладные расходы). Для диктовки в чат или описание задачи это вполне комфортно.

А что с нативным голосовым вводом в Claude Code?

Справедливый вопрос. В марте 2026 Anthropic добавила в Claude Code встроенный голосовой режим: зажимаете пробел, говорите, отпускаете — текст попадает в промпт. Казалось бы, зачем городить огород с Docker и MCP?

Разница в scope. Нативный /voice в Claude Code — это голосовой ввод только внутри Claude Code. Проект voice-setup даёт голосовой ввод везде в системе. Плюс полная приватность — аудио не покидает вашу машину. Плюс возможность использовать дообученные модели Whisper (например, ту самую whisper-large-v3-russian с WER 6.39%). Плюс нормализация через Ollama с учётом контекста окна.

Если вам нужен голосовой ввод только для Claude Code и приватность не критична — встроенный /voice проще в установке и не требует GPU. Если нужен универсальный голосовой ввод по всей системе с полным контролем — voice-setup.

Грабли и нюансы

Несколько вещей, на которые стоит обратить внимание.

PulseAudio vs PipeWire. Если у вас PipeWire (а в 2026 году он стоит почти везде), убедитесь, что установлен pipewire-pulse — прослойка совместимости с PulseAudio. Docker-контейнер Whisper ожидает PulseAudio для доступа к микрофону.

Холодный старт. Первый запрос к Whisper после старта контейнера обрабатывается дольше — модель загружается в GPU. Последующие запросы идут за 300–500 мс.

xdotool и Wayland. Скрипт с xdotool type работает только в X11. Если у вас Wayland, нужен wtype или ydotool. В проекте пока только X11-версия.

Кодировка при xdotool type. Если вставляемый текст содержит кириллицу, а раскладка клавиатуры в момент вставки английская — текст может превратиться в мусор. Простое решение: переключить раскладку перед вставкой или использовать xclip/xsel для вставки через буфер обмена вместо эмуляции набора.

Проект живёт на GitLab. Клонируете, запускаете setup.sh, вешаете скрипт на хоткей — и получаете голосовой ввод по всей системе за один вечер. Whisper large-v3 на GPU распознаёт русскую речь с WER около 10% из коробки, с дообученной моделью — около 6%. Kokoro TTS озвучивает ответы Claude Code, если это нужно. Ollama подчищает ошибки распознавания с учётом контекста.

Из зависимостей — NVIDIA GPU от 6 ГБ VRAM, Docker с nvidia-container-toolkit и пара минут на настройку. По соотношению «усилия / результат» это одна из лучших оптимизаций рабочего процесса, которые я делал за последний год.

Оставить ответ

Ваш адрес email не будет опубликован. Обязательные поля помечены *