Files
home-voice-assistant/CLAUDE.md
Daniil Klimov 7ca8268b78 Initial commit: Cosmo Voice Satellite
Two-agent voice assistant (Cosmo + Люся) via OpenClaw Gateway.
Streaming STT (Groq) + LLM + TTS (ElevenLabs) pipeline with
keep-alive sessions, barge-in, and daily conversation sessions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 13:34:08 +03:00

9.9 KiB
Raw Blame History

Cosmo Voice Satellite

Голосовой ассистент дома — аналог Алисы через OpenClaw. Два агента: Cosmo (владельца) и Люся (жены). Каждый активируется своим wake word и идёт на свой OpenClaw gateway.

Архитектура

┌─────────────┐  wake word   ┌──────────────┐   STT (Groq)
│ Microphone  │ ───────────► │   Satellite  │ ──────────────►
└─────────────┘              └──────────────┘                  │
                                    │                           ▼
                                    │                    ┌──────────────┐
                                    │                    │  OpenClaw    │
                                    │                    │  Gateway     │
                                    │                    │  (N100 PC)   │
                                    │    stream response └──────────────┘
                                    ▼                           │
                             ┌──────────────┐                   │
                             │  ElevenLabs  │ ◄─────────────────┘
                             │     TTS      │
                             └──────────────┘
                                    │
                                    ▼  mp3 stream
                             ┌──────────────┐
                             │     mpv      │ → speakers (BT)
                             └──────────────┘

Инфраструктура

  • Сервер: N100 Mini-PC, 192.168.31.103, Proxmox
  • Cosmo Gateway: порт 18789, агент openclaw/main
  • Люся Gateway: порт 18790, агент openclaw/wife
  • Модель: openai/gpt-5.4-mini (через x-openclaw-model header)
  • STT: Groq API, whisper-large-v3-turbo, язык ru
  • TTS: ElevenLabs, eleven_flash_v2_5 (~75ms латентность)
  • Wake word: Porcupine (на Pi), Enter (при разработке)

Структура проекта

home-voice-assistant/
├── .env                 # секреты (не в git)
├── .env.example         # шаблон
├── requirements.txt
├── satellite.py         # обёртка для запуска
├── satellite/
│   ├── __init__.py
│   ├── __main__.py      # entry: python -m satellite [--wake]
│   ├── config.py        # env, AGENTS dict, keep-alive sessions
│   ├── text.py          # clean_for_speech, find_sentence_end
│   ├── stt.py           # transcribe (Groq, BytesIO, без temp файла)
│   ├── audio.py         # record, record_with_timeout (VAD)
│   ├── tts.py           # ElevenLabs streaming через mpv, barge-in
│   ├── llm.py           # ask_agent_stream, Conversation (history)
│   └── modes.py         # run_with_enter, run_with_porcupine
└── deploy/
    ├── setup.sh               # установка на Raspberry Pi
    └── cosmo-satellite.service  # systemd unit

Что важно знать

Сессии диалога

  • Одна сессия на день для каждого агента. Это осознанное решение: каждая новая сессия в OpenClaw тяжёлая (чтение памяти, большой контекст).
  • История хранится в Conversation.messages[] на клиенте и отправляется целиком с каждым запросом (stateless к серверу).
  • Сброс сессии: фраза "начни новую сессию" / "сбрось историю" / "очисти контекст" — паттерны в RESET_PATTERNS в llm.py.
  • Автосброс при смене даты (Conversation.is_expired()).
  • MAX_HISTORY=20 — лимит сообщений, чтобы не раздувать контекст.

Оптимизации скорости (все уже внедрены)

  1. Keep-alive HTTP сессии (requests.Session()) — в config.py._make_session(), переиспользуется TCP/TLS.
  2. Streaming TTS — ElevenLabs аудио пайпится в mpv через stdin, играет пока генерируется.
  3. STT без диска — PCM → WAV в BytesIO → Groq, без temp файлов.
  4. Barge-instop_speaking() вызывается при каждой активации, убивает текущий mpv процесс.

Роутинг по wake word

В modes.py::run_with_porcupine Porcupine грузит оба wake word:

  • index 0 = Cosmo → AGENTS["cosmo"] (:18789)
  • index 1 = Люся → AGENTS["lusya"] (:18790)

Каждый агент имеет свой tts_voice в ElevenLabs.

Ошибки не должны ронять сервис

Каждый слой (stt, tts, llm, audio, modes) ловит Exception и пишет в errors.log через config.log. Верхний уровень в modes.py ловит всё непредвиденное и продолжает цикл.

Запуск

macOS / Windows (разработка)

python -m venv .venv
# macOS/Linux: source .venv/bin/activate
# Windows:     .venv\Scripts\activate
pip install -r requirements.txt
cp .env.example .env  # заполнить ключи

python satellite.py           # режим Enter (без wake word)
python satellite.py --wake    # режим Porcupine (нужны .ppn + PORCUPINE_KEY)

Raspberry Pi (продакшн)

sudo bash deploy/setup.sh
# далее:
sudo systemctl start cosmo-satellite
sudo journalctl -u cosmo-satellite -f

Зависимости системы

  • Python 3.12+
  • portaudio — для pyaudio (brew install portaudio / apt install portaudio19-dev)
  • mpv — для воспроизведения TTS (brew install mpv / apt install mpv)
  • ffmpeg — опционально, для совместимости форматов

Windows

  • Python 3.12+ с pip
  • pip install pyaudio — обычно работает через колеса pipwin или pre-built wheels. Если нет — pip install pipwin && pipwin install pyaudio
  • mpv: скачать с mpv.io, положить mpv.exe в PATH
  • Porcupine работает и на Windows — wake word модель нужна под платформу windows (качать отдельную .ppn)

Переменные окружения

Все в .env. Ключевые:

Переменная Что
GATEWAY_URL, LUSYA_GATEWAY_URL URL OpenClaw gateway на N100
GATEWAY_TOKEN, LUSYA_GATEWAY_TOKEN Токены авторизации
AGENT, LUSYA_AGENT Имя агента в OpenClaw (openclaw/main, openclaw/wife)
VOICE_MODEL, LUSYA_VOICE_MODEL Модель LLM для голоса
GROQ_API_KEY Groq для STT
ELEVENLABS_API_KEY ElevenLabs TTS
COSMO_TTS_VOICE, LUSYA_TTS_VOICE Voice ID в ElevenLabs
ELEVENLABS_MODEL eleven_flash_v2_5 (быстрый)
AUDIO_SINK На Pi: bluez_sink.XX_XX_XX.a2dp_sink. На Mac/Win: пусто.
PORCUPINE_KEY, WAKE_WORD_COSMO, WAKE_WORD_LUSYA Только для --wake режима
SILENCE_THRESHOLD=500 VAD: чувствительность (ниже = ловит тихую речь)
SILENCE_DURATION=1.5 Сек тишины = конец фразы
FOLLOWUP_TIMEOUT=8 Сек ожидания продолжения диалога
MAX_HISTORY=20 Макс. сообщений в сессии

Частые задачи

Сменить голос у агента: меняй COSMO_TTS_VOICE / LUSYA_TTS_VOICE в .env. Voice ID берётся на elevenlabs.io/app/voice-library.

Отладить VAD (ассистент не слышит / слушает слишком долго): SILENCE_THRESHOLD (громкость) и SILENCE_DURATION (сек).

Добавить третьего агента: в config.py::AGENTS новый ключ, в modes.py::run_with_porcupine добавить WAKE_WORD_* и wake_word_map.append(...).

Сменить модель LLM: VOICE_MODEL в .env — передаётся в header x-openclaw-model. Модель openclaw/main остаётся как agent (это маршрут в OpenClaw).

Что НЕ делать

  • Не комитить .env (есть в .gitignore)
  • Не возвращать fallback на macOS say — проект специально унифицирован на ElevenLabs + mpv
  • Не создавать новую сессию Conversation на каждую активацию — это было в старой версии, сейчас одна сессия на день
  • Не добавлять temp файлы для WAV/mp3 — всё идёт через BytesIO / stdin pipe

Roadmap

  • Speaker identification (определять кто говорит без разных wake words)
  • Проактивные уведомления (WebSocket от сервера → satellite сам начинает говорить)
  • Контекст окружения в system prompt (время, погода, состояние устройств)
  • Real-time barge-in (прерывание по голосу во время озвучки, не только по новой активации)