Cosmo 5a2d34d268 feat(claude): tool use — weather, transport, events, notes, timer
Claude Haiku 4.5 теперь умеет дёргать tools. Все tools — proxy к endpoints
планшета (/api/voice/tools/* и /api/voice/timer) с bearer auth
VOICE_API_KEY. Никакой дополнительной auth в скрипте не требуется.

- satellite/tools.py — 5 tools:
  * get_weather(city?)            → Open-Meteo через tablet
  * get_transport(direction, routes?) → трамваи Антонова-Овсеенко
  * get_today_events(range?)      → Google Calendar (today/week)
  * get_notes()                   → текстовые + shopping lists
  * set_timer(seconds, label)     → создаёт таймер на дашборде
  Каждый tool возвращает dict/list; ошибки упаковываются как {error: ...}
  и отдаются Claude как результат — он сам обрабатывает.

- satellite/llm_claude.py:
  * Подключил TOOL_SCHEMAS в вызов messages.create
  * Цикл tool-use: до MAX_TOOL_ROUNDS=4 раундов tool_use → exec → tool_result
  * System prompt дополнен инструкцией «используй tools без спроса»
  * Финальный текст (после всех tool rounds) сохраняется в историю как один
    assistant-turn — tool rounds в history не пишутся чтобы не раздувать кеш
  * Usage логируется суммарно за все раунды

Работает с уже поднятым tinyproxy на .103 (HTTPS_PROXY в .env).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 13:33:51 +00:00
2026-04-13 18:33:53 +03:00
2026-04-12 21:58:40 +03:00
2026-04-13 18:33:53 +03:00

Cosmo Voice Satellite

Домашний голосовой ассистент поверх LLM — аналог Алисы/Siri, но умнее, потому что за ним стоит полноценный агент OpenClaw с памятью, tools и инструментами.

Два агента — Cosmo (владельца) и Люся (жены). Каждый со своим wake word, своим голосом ElevenLabs и своим OpenClaw gateway.

Архитектура

mic ─► wake word (openwakeword)
        └► STT (Groq whisper) ─► OpenClaw Gateway (session_key, N100) ─► LLM
                                      │
                                      ▼ streamed text
                                 ElevenLabs TTS ─► mpv stdin ─► speakers
  • Wake word: openwakeword (.onnx, обученная на своих записях)
  • STT: Groq API, whisper-large-v3-turbo, ru
  • Агент: OpenClaw на N100, модели через x-ocplatform-model header (openai/gpt-5.4-mini и т.п.)
  • История диалога: на сервере OpenClaw (per session_key), клиент stateless
  • TTS: ElevenLabs streaming через mpv

Структура

home-voice-assistant/
├── satellite.py               # entry-обёртка
├── satellite/                 # рантайм
│   ├── __main__.py            # python -m satellite [--wake]
│   ├── config.py              # AGENTS, keep-alive sessions
│   ├── audio.py               # запись + RMS VAD
│   ├── stt.py                 # Groq whisper
│   ├── llm.py                 # ask_agent_stream, strip_fillers, RESET_PATTERNS
│   ├── tts.py                 # ElevenLabs → mpv stdin
│   ├── text.py                # clean_for_speech (+ pymorphy3 для времени)
│   └── modes.py               # run_with_enter / run_with_porcupine
├── record_wav.py              # запись датасета wake word
├── remove_silent.py           # чистка тихих + перенумерация
├── training/                  # openwakeword пайплайн (в .gitignore)
├── data/models/               # готовые .onnx wake word моделей
└── deploy/                    # setup.sh + systemd unit для Pi 5

Быстрый старт

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

python satellite.py            # режим Enter (отладка)
python satellite.py --wake     # режим wake word (нужна модель в data/models/)

Системные зависимости

macOS: brew install portaudio mpv Linux: apt install portaudio19-dev mpv Windows: мpv с mpv.io, pip install pipwin && pipwin install pyaudio

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

История и память живут на стороне OpenClaw. Satellite отправляет только текущее сообщение + x-openclaw-session-key. Сервер сам подклеивает контекст.

Сброс сессии:

  • Голосом: «начни новую сессию» / «сбрось историю» / «очисти контекст» → satellite шлёт slash-команду /new в OpenClaw
  • Программно: меняй COSMO_SESSION_KEY в .env

Обучение своего 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": ["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
# }

rm -rf training/my_custom_model/cosmo data/models/cosmo.onnx
python training/step_4.py
# → .env: WAKE_WORD_COSMO=data/models/cosmo.onnx

Сколько данных нужно

Positive Negative Ожидаемый recall
100200 200+ 0.10.3 (плохо)
300500 500+ 0.40.6 (минимум для работы)
8001500 1000+ 0.70.85
2000+ 2000+ 0.9+

Главное — разнообразие: разные дистанции, интонации, время дня, фоны. Негативы должны включать фонетически близкие слова («космос», «косо», «просто»), обычную речь, имена других ассистентов («Алиса», «Сири»).

.env (ключевые переменные)

Переменная Что
GATEWAY_URL, LUSYA_GATEWAY_URL URL OpenClaw gateway
GATEWAY_TOKEN, LUSYA_GATEWAY_TOKEN Bearer токены
AGENT, LUSYA_AGENT Имя агента в OpenClaw
VOICE_MODEL LLM (передаётся в x-ocplatform-model)
COSMO_SESSION_KEY, LUSYA_SESSION_KEY Идентификатор серверной сессии
GROQ_API_KEY STT
ELEVENLABS_API_KEY, ELEVENLABS_MODEL TTS
COSMO_TTS_VOICE, LUSYA_TTS_VOICE Voice ID
WAKE_WORD_COSMO, WAKE_WORD_LUSYA Пути к .onnx
WAKE_THRESHOLD Порог активации (дефолт 0.5)
TTS_MODE full (цельная интонация) / stream (быстрый старт)
AUDIO_SINK На Pi: BT sink. На Mac/Win: пусто
SILENCE_THRESHOLD, SILENCE_DURATION, MAX_DURATION, FOLLOWUP_TIMEOUT VAD

Деплой на Raspberry Pi 5

sudo bash deploy/setup.sh
# подключи BT колонку, пропиши AUDIO_SINK в .env
# положи обученную .onnx в data/models/cosmo.onnx
sudo systemctl start cosmo-satellite
sudo journalctl -u cosmo-satellite -f

Roadmap

Сделано

  • Модульная структура satellite
  • ElevenLabs streaming TTS через mpv pipe
  • Keep-alive HTTP + STT без диска
  • Серверные сессии OpenClaw (x-openclaw-session-key)
  • Slash-команда /new для сброса голосом
  • Нормализация речи (числа, время, единицы) через pymorphy3 + num2words
  • Пайплайн тренировки wake word на своих записях
  • systemd unit для Pi

🚧 В работе

  • Дообучить wake-модель до recall ≥ 0.7 (нужно 500+ позитивов + разнообразие)
  • Подключить Люсю в run_with_porcupine (код готов, закомментирован)
  • Чистка мёртвого кода (start_barge_in_listener, conv и т.п.)
  • Починить FOLLOWUP_TIMEOUT (сейчас после ответа ассистент ждёт полный 15 с)

📋 Этап 2 — качество и надёжность

  • Автосброс OpenClaw сессии по таймауту (>1 ч → /new)
  • Retry с backoff для gateway
  • TTS-cache дежурных реплик
  • Persistent PyAudio stream (быстрее запись на Pi)
  • Заменить RMS-VAD на webrtcvad / silero-vad — RMS ломается с фоновой музыкой
  • Whisper prompt с именами собственными
  • Size-cap / logrotate для errors.log

📋 Этап 3 — «умнее Алисы»

  • Home Assistant tool в OpenClaw: свет/климат/медиа голосом
  • Контекст окружения в каждом запросе: время, комната, погода, кто говорит
  • Proactive notifications: OpenClaw → WebSocket/SSE → satellite сам начинает говорить (таймеры, напоминания, входящие)
  • Realtime barge-in голосом — прерывать TTS, когда пользователь начал говорить (требует echo cancellation)
  • No-wake mode в доверенной комнате — VAD + STT + intent filter без обязательного wake word
  • Streaming TTS пер-токен — выдавать в речь куски раньше полного предложения

📋 Этап 4 — амбициозное

  • Speaker identification (pyannote.audio / resemblyzer) — разные персонализации по голосу
  • Multi-room координация — MQTT между сателлитами, отвечает тот, кто слышит громче
  • Локальный fallback LLM на Pi когда gateway оффлайн (phi/llama для простых команд)
  • Камера + vision — агент видит кто в комнате, что происходит
  • Voice-memory hooks UX — голосовое «запомни/забудь»

Известные ограничения

  • Нет echo cancellation — если колонки близко к мику, TTS может триггерить wake-модель (поднимай WAKE_THRESHOLD).
  • RMS-VAD не отличает голос от музыки/ТВ — ассистент может «залипнуть» в шумной среде.
  • OpenClaw-сессия живёт вечно, пока не сказать «сбрось» — контекст раздувается.
  • При потере связи с gateway — ассистент молчит до явной ошибки.

Troubleshooting

Ассистент «иностранец», монотонно читает → смени ELEVENLABS_MODEL на eleven_multilingual_v2, возьми native-русский голос из Voice Library (не default English-speakers), в tts.py поставь style=0.0, speed=1.0 (style/speed ломают просодию).

Числа читаются цифрами вместо слов → проверь text.py::clean_for_speech применяется. Для времени нужны num2words и pymorphy3 в requirements.

Wake срабатывает сам по себе, когда TTS говоритWAKE_THRESHOLD=0.7, разнеси колонку и мик. В будущем — AEC.

Ассистент не реагирует на тихий голос → понижай SILENCE_THRESHOLD (дефолт 500).

pyaudio не ставится на Windowspip install pipwin && pipwin install pyaudio, или бери pre-built wheel.

Wake модель плохо работает → дело в данных. Смотри таблицу выше — нужно минимум 500 позитивов с разнообразием.

Description
No description provided
Readme 774 KiB
Languages
Python 94.9%
Shell 5.1%