Полностью локальный голосовой ассистент на Python. Стек: - Wake word: openWakeWord (onnxruntime) - STT: RealtimeSTT + faster-whisper + Silero VAD (CUDA) - LLM-агент: smolagents ToolCallingAgent + Ollama qwen2.5:7b - TTS: Silero V4 (torch.hub) + sounddevice - Shell: Git Bash (Windows) / bash (macOS) Поддерживает Windows и macOS. Агент с памятью и tool calling — находит программы самостоятельно, запоминает пути, выполняет произвольные shell-команды. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
120 lines
4.4 KiB
Bash
120 lines
4.4 KiB
Bash
#!/usr/bin/env bash
|
||
set -e
|
||
|
||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||
MODELS_DIR="$PROJECT_DIR/models"
|
||
SAMPLES_DIR="$SCRIPT_DIR/samples"
|
||
DATA_DIR="$SCRIPT_DIR/docker_data"
|
||
|
||
echo "============================================"
|
||
echo " Cosmo Wake Word — обучение модели"
|
||
echo "============================================"
|
||
echo ""
|
||
|
||
# Проверяем Docker
|
||
if ! command -v docker &>/dev/null; then
|
||
echo "ОШИБКА: Docker не найден."
|
||
echo "Установи Docker Desktop: https://www.docker.com/products/docker-desktop/"
|
||
exit 1
|
||
fi
|
||
|
||
if ! docker info &>/dev/null; then
|
||
echo "ОШИБКА: Docker не запущен. Запусти Docker Desktop и попробуй снова."
|
||
exit 1
|
||
fi
|
||
|
||
# Проверяем что есть записанные примеры
|
||
POSITIVE_DIR="$SAMPLES_DIR/positive"
|
||
if [ ! -d "$POSITIVE_DIR" ] || [ -z "$(ls "$POSITIVE_DIR"/*.wav 2>/dev/null)" ]; then
|
||
echo "Записанные примеры не найдены."
|
||
echo "Сначала запусти запись голоса:"
|
||
echo " python train_wakeword/record_samples.py"
|
||
echo ""
|
||
read -p "Продолжить без записанных примеров? (используются только TTS) [y/N]: " yn
|
||
case "$yn" in
|
||
[Yy]*) echo "Продолжаем с только TTS примерами..." ;;
|
||
*) exit 0 ;;
|
||
esac
|
||
fi
|
||
|
||
mkdir -p "$MODELS_DIR" "$DATA_DIR"
|
||
|
||
# Собираем Docker образ
|
||
echo "[1/4] Собираю Docker образ (первый раз ~5-10 мин)..."
|
||
docker build -t cosmo-wakeword-trainer "$SCRIPT_DIR" --quiet
|
||
echo " Образ готов."
|
||
echo ""
|
||
|
||
# Скачиваем датасет негативных примеров если нет
|
||
NEGATIVE_FEATURES="$DATA_DIR/openwakeword_features_ACAV100M_2000_hrs_16bit.npy"
|
||
VALIDATION_FEATURES="$DATA_DIR/validation_set_features.npy"
|
||
|
||
if [ ! -f "$NEGATIVE_FEATURES" ]; then
|
||
echo "[2/4] Скачиваю негативный датасет (~20 GB, один раз)..."
|
||
echo " Это займёт время в зависимости от скорости интернета."
|
||
docker run --rm \
|
||
-v "$DATA_DIR:/data" \
|
||
cosmo-wakeword-trainer \
|
||
python -c "
|
||
from datasets import load_dataset
|
||
import numpy as np, os
|
||
print('Скачиваю ACAV100M features...')
|
||
ds = load_dataset('davidscripka/openwakeword_features', 'ACAV100M_2000_hrs_16bit', split='train')
|
||
arr = np.array(ds['features'])
|
||
np.save('/data/openwakeword_features_ACAV100M_2000_hrs_16bit.npy', arr)
|
||
print('Скачиваю validation features...')
|
||
ds_val = load_dataset('davidscripka/openwakeword_features', 'validation_set', split='train')
|
||
arr_val = np.array(ds_val['features'])
|
||
np.save('/data/validation_set_features.npy', arr_val)
|
||
print('Датасет скачан.')
|
||
"
|
||
echo " Датасет готов."
|
||
else
|
||
echo "[2/4] Негативный датасет уже скачан. Пропускаю."
|
||
fi
|
||
echo ""
|
||
|
||
# Запускаем обучение
|
||
echo "[3/4] Запускаю обучение в Docker..."
|
||
echo " Это займёт ~30-60 минут."
|
||
echo ""
|
||
|
||
SAMPLES_MOUNT=""
|
||
if [ -d "$POSITIVE_DIR" ] && [ -n "$(ls "$POSITIVE_DIR"/*.wav 2>/dev/null)" ]; then
|
||
SAMPLES_MOUNT="-v $POSITIVE_DIR:/samples/positive"
|
||
fi
|
||
|
||
docker run --rm \
|
||
-v "$SCRIPT_DIR/cosmo_config.yaml:/app/cosmo_config.yaml" \
|
||
-v "$DATA_DIR:/data" \
|
||
-v "$MODELS_DIR:/output" \
|
||
$SAMPLES_MOUNT \
|
||
cosmo-wakeword-trainer
|
||
|
||
echo ""
|
||
echo "[4/4] Копирую модель в проект..."
|
||
|
||
# Ищем готовую модель
|
||
ONNX_FILE=$(ls "$MODELS_DIR"/*.onnx 2>/dev/null | head -1)
|
||
|
||
if [ -n "$ONNX_FILE" ]; then
|
||
echo ""
|
||
echo "============================================"
|
||
echo " Готово! Модель сохранена:"
|
||
echo " $ONNX_FILE"
|
||
echo ""
|
||
echo " Обновляю wake_word детектор..."
|
||
# Обновляем путь в конфиге
|
||
MODEL_FILENAME=$(basename "$ONNX_FILE")
|
||
sed -i "s|wakeword_models=\[\"hey_jarvis\"\]|wakeword_models=[\"models/$MODEL_FILENAME\"]|g" \
|
||
"$PROJECT_DIR/cosmo/wake_word.py" 2>/dev/null || true
|
||
echo " Теперь запускай: bash run.sh"
|
||
echo " и говори 'Hey Cosmo' для активации!"
|
||
echo "============================================"
|
||
else
|
||
echo "ОШИБКА: .onnx файл не найден в $MODELS_DIR"
|
||
echo "Проверь логи Docker выше."
|
||
exit 1
|
||
fi
|