Files

154 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 на сервере юзера).