# BLIND — передаточный конспект (продолжение с другого компьютера) Обновлён: 2026-09-11 (вечер). Читай этот файл первым — он заменяет всю историю чата. ## Что это за проект Голосовой AI-ассистент для незрячего пожилого человека (отца юзера), живущий на ПК. Отец управляет ТОЛЬКО ГОЛОСОМ: говорит wake-word «Марта» → ассистент просыпается (куи «Ассистент готов к диалогу») → диалог → «до свидания» → куи «Спасибо, до новых встреч» и снова ждёт слова. Вне диалога микрофон слушает тихо и реагирует только на wake-word (фоновый ТВ/разговоры не будят — проверено). ## Архитектура (модули = законченные проверяемые единицы) ``` микрофон → Silero-VAD (тишина 2с = конец фразы) → faster-whisper (STT, small/int8) → OpenRouter (LLM: gemma-3-27b обычно / glm-5.3-flash:online для погоды-курсов-новостей) → edge-tts (голос Svetlana, темп +15%) → стриминговая озвучка чанками-предложениями ``` - `modules/stt/` — faster-whisper 1.2.1, small/int8/CPU, RTF ~0.15. Фолбэк: Vosk (не реализован). - `modules/tts/` — edge-tts 7.2.8 (Svetlana, TTS_RATE=+15) + офлайн-фолбэк piper 1.8.0 (голоса в models/piper/: dmitri, irina — irina проверена, 0.21 c синтез). Фабрика `make_tts()` из .env. Параллельные запросы по предложениям + кэш коротких фраз → типичный ответ синтезируется 0.5 c. - `modules/tts/provider_edge.py` → `stream(text)` — генератор чанков-предложений ( Assistant играет чанк сразу, не дожидаясь всего ответа). - `modules/audio_io/` — Recorder (push-to-talk резерв), HandsFreeRecorder (VAD-режим, pre-buffer 0.4 c, noise gate RMS>0.006, suppress во время собственной речи), Player (stop = «Замолчи» за 8 мс; lead_silence 0.1 c против съедания первого слова), codec (mp3 через PyAV). - `modules/brain/` — OpenRouter через openai-клиент (RF-нюансы: openai/* модели блокируются ToS OpenAI по аккаунту; прокси http://192.168.0.246:8887 → egress Италия 85.137.175.114). Маршрутизация online/offline: хинты (погод|курс|доллар|новост|спорт...) + контекст последних 4 реплик + маркеры уточнения. Промпт: короткие ответы, НО стихи/сказки целиком, многочастность [ЧАСТЬ i ИЗ n] с продолжением по «дальше», на уточнения — ответ по существу без извинений. - `modules/assistant/` — машина состояний idle/listening/thinking/speaking, перебивание (поколенческий счётчик _generation), память между сессиями (data/memory.md, конспект по «до свидания»), куи-файлы (start/end/error/unclear), wake_word.py, errors.py (классификация). - `modules/hotkeys/` — бэкенды: console (readchar, пробел работает без Enter; Linux без root) и keyboard_hook (Windows, глобальные клавиши из любого окна). Wake-word — основной способ. ## Ключевые решения и грабли (обязательно прочти) 1. **Веб-поиск**: суффикс `:online` у OpenRouter (веб-плагин), НЕ свойство модели. Маршрутизация в `_needs_online(question, history)` — смотрит текущую фразу + историю + маркеры уточнения. Уточнение «я поэтому и спрашиваю...» после вопроса про акции → online-модель (фикс «извиняется, а не отвечает»). 2. **GLM reasoning**: z-ai/glm-* тратят max_tokens на размышления → при малом бюджете content пустой. max_tokens=2000. 3. **Silero-VAD**: только CPU-torch (GPU-пакеты 2.5 ГБ не нужны); модель принимает ТОЛЬКО torch.Tensor; на белом шуме даёт prob 0.86! → обязательный noise gate по RMS. 4. **Колбэки рук**: Assistant присваивает recorder.on_phrase (публичный атрибут!) — не спрятать в _private, иначе фразы уходят в никуда (уже ловили). 5. **Wake-word matching**: whisper ломает имена («Ага-то», «А гата», «Агад») → normalize склеивает дефисы, проверяется первое слово И склейка первых двух, обрубки-префиксы ок, но «мартовские» — нет (граница слова). 6. **.env**: при добавлении строк через `>>` файл может не кончаться \n → строки склеиваются, ключ ломается. После любой записи в .env проверяй длину ключа (sk-or-v1 = 73 симв). 7. **Стриминг**: Player.play() с lead_silence; чанки по 1 предложению; «Отвечаю...» печатается до синтеза. 8. Сегфолты в смоук-тестах — из-за реального звука из нескольких потоков; в тестах глушить play/play_file. ## Конфиг (.env) — создаётся из .env.example OPENROUTER_API_KEY (ключ юзера), OPENROUTER_PROXY=http://192.168.0.246:8887, OPENROUTER_MODEL=google/gemma-3-27b-it, OPENROUTER_MODEL_ONLINE=z-ai/glm-5.3-flash:online, TTS_PROVIDER=edge, TTS_VOICE=ru-RU-SvetlanaNeural, TTS_RATE=+15, PIPER_MODEL=models/piper/ru_RU-dmitri-medium.onnx, WAKE_WORD=марта, HOTKEY_DIALOG=space, HOTKEY_TALK=num 0, HOTKEY_STOP=esc, HOTKEY_REPEAT=enter. ## Окружение (на этой машине) - .venv на python3.13 (Debian: venv без pip — ставить `python3 -m pip --python .venv install ...`). - Модели: ~/.cache/huggingface (whisper small), models/piper/ (121 МБ). - Первый старт whisper качает модель ~460 МБ (если HF недоступен: HF_ENDPOINT=https://hf-mirror.com). ## Установка на новом компьютере (полный набор, проверенный) ```bash # 1. Клонировать и окружение git clone <репозиторий> && cd Blind python3 -m venv .venv && source .venv/bin/activate cp .env.example .env # и вписать реальный OPENROUTER_API_KEY (и WAKE_WORD при желании) # 2. Основные зависимости pip install -r requirements.txt # 3. Linux: системный PortAudio (звук) sudo apt install -y libportaudio2 # 4. torch/клиент для Silero-VAD — СТРОГО CPU (GPU-пакеты 2.5 ГБ не нужны): pip install --index-url https://download.pytorch.org/whl/cpu torch pip install --index-url https://download.pytorch.org/whl/cpu torchaudio --force-reinstall --no-deps # 5. Whisper-модель скачается при первом запуске (~460 МБ, кэш ~/.cache/huggingface) # 6. Голос Piper (если TTS_PROVIDER=piper): python -m piper.download_voices ru_RU-dmitri-medium --download-dir models/piper # 7. Куи-файлы уже в git (assets/earcons/*.wav); перегенерация после смены голоса: python assets/earcons/generate_cues.py --force # 8. Проверка: python modules/assistant/test_free.py # «Марта» → диалог → «До свидания» ``` Прокси (OPENROUTER_PROXY=http://192.168.0.246:8887) работает только в LAN юзера — вне его сети понадобится VPN/другой egress, иначе openai/* и часть источников недоступны (gemma-3-27b и glm работают из РФ напрямую, проверено). ## Как запускать (проверенные команды) ```bash cd ~/cloud/AI/Blind source .venv/bin/activate # или .venv/bin/python напрямую # Живой ассистент (wake-word «Марта», всё голосом): .venv/bin/python modules/assistant/test_free.py # «Марта» → диалог → «До свидания». Консоль: s/r/пробел/q (readchar, без Enter) # Регресс модулей: .venv/bin/python modules/stt/test_stt.py --make-sample # STT .venv/bin/python modules/audio_io/test_audio_io.py auto # аудио .venv/bin/python modules/brain/test_brain.py clean # очистка MD (офлайн) .venv/bin/python modules/brain/test_brain.py ask # LLM .venv/bin/python modules/tts/test_tts.py --play # TTS # Перегенерация куи (после смены голоса/темпа): .venv/bin/python assets/earcons/generate_cues.py --force ``` ## Пользовательские решения (зафиксировано) - Голос: **edge-tts Svetlana, темп +15%** («женский приятный»); Piper dmitri/irina — фолбэк (быстрее, но не понравился темп/голос). - Wake-word: **«Марта»** (пробовали «Агата» — whisper искажал «Агад»). - Свободный режим принят: «значительно лучше, чем нажимать кнопки». - Тумблер диалога: пробел (дублируется wake-word), куи из готовых wav. ## ЧТО ДАЛЬШЕ (по порядку) 1. **Модуль 6: Telegram** — мост сын↔ассистент: сообщения юзера озвучиваются отцу («Пришло сообщение от Александра: …»), отец отвечает голосом → в чат юзеру. Нужен токен бота (@BotFather → /newbot) → в .env TG_BOT_TOKEN. Библиотека aiogram 3.x (крутить в отдельном потоке; в edge-TTS уже учтён чужой event loop — _run_async). 2. **Модуль 7: Earcons** — короткие музыкальные сигналы на статусы (думаю/готов/ошибка) вместо длинных фраз; ассистент уже вызывает on_state_change(state, note) — вешать на него. 3. **Модуль 8: Сборка exe для Windows** — PyInstaller; глобальные клавиши (HOTKEY_* уже в .env); Silero-VAD заменить на ONNX-версию (2 МБ, без torch); тест на ПК отца. 4. Хвосты: живой тест памяти «2 запуска» юзером; паузы между строфами стихов (по желанию). ## Известные открытые мелочи - Стриминговый путь: история/конспект пополняются до стрима — маркер [ЧАСТЬ i ИЗ n] в _last_speech для «Повтори» не сохраняется из чанков (повтор последней ЧАСТИ, а не всего текста). - Веб-поиск медленный (5–10 c) — если раздражает, ускорять стримингом или мини-моделью. - edge-tts нестабилен исторически (неофициальный API MS) — фолбэк Piper готов, переключение 1 строкой в .env. - Vosk-фолбэк для STT не реализован (решение отложить до замеров на слабом ПК отца). - Развертывание A (монолит Windows) vs B (гибрид Docker) — НЕ выбрано окончательно; код кроссплатформенный, интерфейсы провайдеров позволяют вариант B (provider_http + FastAPI на сервере юзера).