Cosmo Voice Satellite
Домашний голосовой ассистент. Слушает wake word, распознаёт речь, ходит в OpenClaw gateway, проигрывает ответ через ElevenLabs.
Два агента: Cosmo (владельца) и Люся (жены) — каждый со своим wake word и своим gateway.
Архитектура
mic ─► wake word (openwakeword) ─► STT (Groq) ─► OpenClaw gateway ─► TTS (ElevenLabs) ─► mpv ─► speakers
- Wake word: openwakeword (обучается на своих записях, см. ниже). Раньше планировался Porcupine — отказались.
- STT: Groq API,
whisper-large-v3-turbo, ru. - LLM: OpenClaw gateway на N100 (
192.168.31.103:18789для cosmo,:18790для lusya),openai/gpt-5.4-mini. - TTS: ElevenLabs
eleven_flash_v2_5стримом через mpv stdin.
Структура
home-voice-assistant/
├── satellite.py # entry-обёртка
├── satellite/ # рантайм
│ ├── __main__.py # python -m satellite [--wake]
│ ├── config.py, text.py
│ ├── stt.py, audio.py, tts.py, llm.py
│ └── modes.py # run_with_enter / run_with_porcupine (wake word)
├── record_wav.py # запись датасета для wake word
├── remove_silent.py # чистка тихих + перенумерация
├── training/ # пайплайн обучения wake word
│ ├── step_1.py … step_5.py
│ ├── training_config.json
│ ├── own_samples/<word>/{positive,negative}/*.wav
│ ├── openwakeword/ # форк
│ └── my_custom_model/<word>/ # фичи + .onnx
├── data/models/ # готовые .onnx wake word моделей
└── deploy/ # setup.sh + systemd unit для Pi
Запуск
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env # заполнить ключи
python satellite.py # режим Enter (без wake word, для отладки)
python satellite.py --wake # режим wake word (нужна обученная модель в data/models/)
Системные зависимости:
- Python 3.12+
portaudio—brew install portaudiompv—brew install mpv
Обучение своего wake word
OpenWakeWord обучает DNN-модель на твоих записях слова. Пайплайн в training/:
| Шаг | Что делает |
|---|---|
step_1.py |
Установка зависимостей (piper, openwakeword) |
step_2.py |
Создаёт training_config.json (параметры обучения) |
step_3.py |
Скачивает датасеты (audioset, fma, RIRs, ACAV features) — ~17 GB |
step_4.py |
Аугментация → тренировка → экспорт .onnx в data/models/ |
step_5.py |
Проверка моделей и подсказки для .env |
Запись датасета
# по одной записи (Enter → 2 секунды → сохраняем)
python record_wav.py cosmo positive
python record_wav.py cosmo negative
# непрерывно N секунд → нарезаем по 2с, тишину выкидываем
python record_wav.py cosmo negative long 300
INPUT_DEVICE=1 python record_wav.py cosmo negative long 600
record_wav.py отбраковывает тихие записи по MIN_RMS=300 (изменить можно константой в начале файла).
Чистка
python remove_silent.py
Удаляет файлы с RMS ниже порога и переименовывает оставшиеся в 001.wav … NNN.wav.
Тренировка
- В
training/training_config.jsonукажиwake_word_list,use_own_samples: true, параметры:{ "wake_word_list": ["cosmo"], "use_own_samples": true, "false_activation_penalty": 100, "target_false_positives_per_hour": 3.0, "target_recall": 0.5, "number_of_training_steps": 3000, "layer_size": 64 } - В
training/openwakeword/examples/custom_model.ymlподнимиaugmentation_rounds: 10(или больше). - Снеси кэш если был старый запуск:
rm -rf training/my_custom_model/<word> data/models/<word>.onnx - Запусти:
python training/step_4.py - Пропиши в
.env:WAKE_WORD_COSMO=data/models/cosmo.onnx.
Сколько данных нужно
| Positive | Negative | Recall |
|---|---|---|
| 100–200 | 200+ | 0.1–0.3 (плохо) |
| 300–500 | 500+ | 0.4–0.6 (минимум) |
| 800–1500 | 1000+ | 0.7–0.85 |
| 2000+ | 2000+ | 0.9+ |
Главное — разнообразие: разные дистанции до микрофона, интонации, время дня, фоны. Аугментация (augmentation_rounds) умножит твой датасет в N раз во время обучения.
Негативы должны включать фонетически близкие слова ("космос", "косо", "просто"), обычную речь, имена других ассистентов ("алиса", "сири"), бытовые звуки.
Архитектурные решения
- Одна сессия диалога на день на агента (
Conversationвllm.py). История хранится клиентом, отправляется целиком. Сброс — фразой "сбрось историю" или сменой даты. - Keep-alive HTTP (
requests.Session) — переиспользует TCP/TLS. - Streaming TTS — ElevenLabs пайпится в
mpvчерез stdin, играет пока генерируется. - STT без диска — PCM → WAV в
BytesIO→ Groq. - Barge-in —
stop_speaking()убивает mpv при новой активации. - Ошибки не роняют сервис — каждый слой ловит
Exception, пишет вerrors.log.
.env (ключевые переменные)
| Переменная | Что |
|---|---|
GATEWAY_URL, LUSYA_GATEWAY_URL |
OpenClaw gateways |
GATEWAY_TOKEN, LUSYA_GATEWAY_TOKEN |
Авторизация |
AGENT, LUSYA_AGENT |
openclaw/main, openclaw/wife |
VOICE_MODEL |
LLM для голоса (передаётся в x-openclaw-model) |
GROQ_API_KEY |
STT |
ELEVENLABS_API_KEY, COSMO_TTS_VOICE, LUSYA_TTS_VOICE |
TTS |
WAKE_WORD_COSMO, WAKE_WORD_LUSYA |
Пути к .onnx моделям |
SILENCE_THRESHOLD, SILENCE_DURATION |
VAD |
MAX_HISTORY |
Лимит сообщений в сессии |
AUDIO_SINK |
На Pi: bluez_sink.XX_XX_XX.a2dp_sink |
Деплой на Raspberry Pi
sudo bash deploy/setup.sh
sudo systemctl start cosmo-satellite
sudo journalctl -u cosmo-satellite -f
Roadmap
- Модулизация satellite
- ElevenLabs streaming + barge-in
- Сессии диалога с автосбросом
- Пайплайн тренировки wake word на своих записях
- Обучить рабочую модель cosmo (нужно ~500+ позитивов)
- Подключить Люсю в
run_with_porcupine(сейчас грузится только cosmo) - Проверить systemd autostart на Pi в проде
- Home Assistant tool в OpenClaw
- Real-time barge-in (прерывание голосом во время TTS)
- Контекст окружения в system prompt
- Speaker identification
- Проактивные уведомления через WebSocket