Files
smart-home-tablet/RUNBOOK.md
d.klimov e6dab09c38
All checks were successful
Deploy / deploy (push) Successful in 3m43s
voice: локальный агент (fast-path), tool control_light, смелая тема, RUNBOOK
- lib/agent-local.ts + route.ts: команды-действия исполняет локальная intent-служба
  (под AGENT_URL, с fallback на LLM); инфо-запросы остаются у LLM
- lib/tools/lights.ts: голосовое управление светом через HA (control_light)
- app/globals.css + page.tsx: космическая indigo/aurora тема (токены, kiosk-safe)
- agent/: sidecar-служба (Dockerfile, predict.py), веса монтируются томом
- RUNBOOK.md: вывод в продакшен на мини-ПК
2026-07-22 21:48:24 +03:00

10 KiB
Raw Blame History

RUNBOOK — вывод голосового помощника в продакшен

Пошаговая инструкция, чтобы всё завелось на мини-ПК (где развёрнут https://tablet.digital-home.site). Рассчитана на то, что её выполняет Claude-сессия на мини-ПК (cosmo@192.168.31.60), с пометками, что делается на рабочей машине (Windows), а что — на мини-ПК.

TL;DR — что это добавляет

  1. Локальный агент (intent+slots) — sidecar-служба, распознаёт команды офлайн за ~2 мс и исполняет действия (таймеры, свет, музыка, очиститель), минуя облачный LLM. Инфо-запросы (погода/календарь) по-прежнему у LLM. Модель обучена: интент 40/40, слоты 40/40.
  2. Голосовое управление светом — новый tool control_light (2 лампы).
  3. Смелый визуал — обновлённая тема (космический индиго/aurora), деплоится с обычным билдом.
  4. Wake «Эй, космо» — пайплайн переобучения готов и проверен end-to-end; боевая модель требует досбора данных (см. раздел D).

Всё под фиче-флагами: без AGENT_URL планшет работает ровно как раньше → безопасно для kiosk.


Изменения в репозитории (уже в коде)

Файл Что
lib/tools/lights.ts новый tool control_light (свет через HA)
lib/tools/_registry.ts регистрация control_light
lib/agent-local.ts быстрый путь: вызов агент-службы + исполнение действий
app/api/voice/chat/route.ts вставка fast-path (под AGENT_URL, с fallback)
app/globals.css, app/page.tsx смелый визуал (токены/aurora)
agent/ sidecar агента: Dockerfile, predict.py, joint_model_def.py, tablet_map.py, requirements.txt

⚠️ Эти изменения надо закоммитить и запушить (с рабочей машины), чтобы мини-ПК их подтянул git pull. Веса моделей в git НЕ идут (переносятся отдельно, ниже).


Архитектура продакшена

Планшет (Android, kiosk-браузер)
  ├─ wake «Эй, космо» (public/wake/cosmo.onnx, WASM) ─┐
  └─ UI (Next.js)                                      │
                                                       ▼
tablet-контейнер (мини-ПК, network coolify) ── /api/voice/chat
   │  fast-path: AGENT_URL задан?
   │    ├─ да → POST http://tablet-agent:8091/predict
   │    │        интент-действие + score≥0.7 → executeTool → короткий ответ
   │    └─ нет/инфо/none → облачный LLM (Anthropic/Groq) как раньше
   ▼
tablet-agent-контейнер (sidecar, network coolify)
   └─ predict.py --serve  (joint_model смонтирован томом)

A. Агент (sidecar) — главный шаг

A1. Перенести модель на мини-ПК (с рабочей машины)

Веса joint_model/ (~115 МБ) в git нет. Скопировать на мини-ПК:

# на рабочей машине (Windows, Git Bash):
scp -i ~/.ssh/id_ed25519 -r "/d/Digital home/tablet-agent/joint_model" \
    cosmo@192.168.31.60:/opt/digital-home/agent-model

Должно получиться /opt/digital-home/agent-model/joint.pt (+ tokenizer, config).

Переобучить модель заново (если правил tablet-agent/templates.txt): cd tablet-agent && python train_joint.py && python test_joint.py, затем scp.

A2. Собрать и запустить sidecar (на мини-ПК)

cd /opt/digital-home/smart-home-tablet
git pull origin main                       # подтянуть код (после пуша с рабочей машины)

docker build -t tablet-agent:latest ./agent
docker rm -f tablet-agent || true
docker run -d --name tablet-agent \
  --network coolify \
  --restart unless-stopped \
  -v /opt/digital-home/agent-model:/app/joint_model \
  tablet-agent:latest

# проверка службы:
docker exec tablet-agent python -c "import urllib.request,json; \
  r=urllib.request.urlopen(urllib.request.Request('http://127.0.0.1:8091/predict', \
  data=json.dumps({'text':'поставь таймер на 5 минут'}).encode(), \
  headers={'Content-Type':'application/json'})); print(r.read().decode())"
# ждём: {"intent":"timer_set","slots":{"duration":"5 минут"},"score":...,"tool":{...}}

A3. Включить fast-path в планшете

Добавить в /opt/digital-home/tablet.env:

AGENT_URL=http://tablet-agent:8091
AGENT_THRESHOLD=0.7

Пере-задеплоить планшет (push любой коммит ИЛИ вручную пере-docker run по deploy.yml). После рестарта route.ts увидит AGENT_URL и начнёт использовать агента.

Откат агента: убрать AGENT_URL из tablet.env, пере-деплой. Мгновенно вернёт облачный LLM на все команды. Контейнер tablet-agent можно оставить.


B. Свет — реальные entity Home Assistant

control_light по умолчанию шлёт на light.bedside / light.reading (заглушки). Указать реальные entity_id твоих ламп в /opt/digital-home/tablet.env:

LIGHT_ENTITIES_JSON={"bedside":"light.ВАШ_СВЕТ_У_КРОВАТИ","reading":"light.ВАШААМПА_ДЛЯ_ЧТЕНИЯ","default":"light.ВАШ_СВЕТ_У_КРОВАТИ"}

Найти id ламп: HA → Developer Tools → States, отфильтровать light.. Ключи bedside/reading соответствуют фразам «свет у кровати» / «лампа для чтения» (маппинг в lib/tools/lights.ts, DEVICE_ALIASES). Пере-деплой планшета.

Без этой переменной команды света пройдут как mock (HA вернёт success, лампа не переключится) — то есть безопасно, но не управляет реальным светом.


C. Дизайн (смелый визуал)

Ничего дополнительно — тема живёт в app/globals.css (токены) и деплоится обычным билдом. Раскладка/логика не менялись, kiosk не затронут. Если визуал не зайдёт — откат = git revert коммита с globals.css.


D. Wake «Эй, космо» — статус и деплой

Проект tablet-wake (на рабочей машине). Пайплайн переобучения готов и проверен end-to-end: smoke-модель разделяет фразу (pos 0.96 / neg 0.01) через тот же WASM-рантайм, что у планшета. Но это не боевая модель — нужен досбор данных:

  1. Реальная речь в негативах (корпус Golos) — главный убийца ложняков.
  2. Фон Android-мика (запись, где планшет стоит).
  3. Held-out тест своим голосом + подбор порога.

Полный прогон (на рабочей машине) — см. tablet-wake/README.md. Итог — tablet-wake/out/cosmo.onnx.

Деплой wake-модели (когда готова): заменить файл в репозитории и запушить — инференс планшета НЕ меняется (окно 25 сохранено), только веса:

# на рабочей машине:
cp "/d/Digital home/tablet-wake/out/cosmo.onnx" \
   "/d/Digital home/smart-home-tablet/public/wake/cosmo.onnx"
# затем commit + push → Gitea пересоберёт планшет

Порог срабатывания подстраивается в клиенте (WAKE_THRESHOLD в components/VoiceController.tsx). Также обнови фразу-подпись в UI: сейчас «Космо» → «Эй, космо».


Сводка новых переменных tablet.env

AGENT_URL=http://tablet-agent:8091          # включает локального агента
AGENT_THRESHOLD=0.7                          # порог уверенности (опц.)
LIGHT_ENTITIES_JSON={"bedside":"...","reading":"...","default":"..."}

Проверка (после деплоя)

# планшет жив:
curl -sk -o /dev/null -w "%{http_code}\n" https://tablet.digital-home.site/

# агент отвечает изнутри сети:
docker exec tablet-yfh53kixpwkjlo4zibglx4n2 \
  wget -qO- --post-data='{"text":"выключи свет у кровати"}' \
  --header='Content-Type: application/json' http://tablet-agent:8091/predict

# логи планшета: fast-path пишет "[voice/chat] local-agent intent=..."
docker logs --tail 50 tablet-yfh53kixpwkjlo4zibglx4n2 | grep local-agent

Живой тест голосом: сказать «поставь таймер на 5 минут» (действие → агент, мгновенно), затем «какая погода» (инфо → LLM). В логах у первой команды — local-agent, у второй — обычный LLM-раунд.


Откат всего

Что Откат
Агент убрать AGENT_URL из tablet.env, пере-деплой
Свет убрать control_light из _registry.ts ИЛИ просто не задавать LIGHT_ENTITIES_JSON
Дизайн git revert коммита globals.css
Wake вернуть прежний public/wake/cosmo.onnx из git-истории

Каждая часть независима — можно катить и откатывать по отдельности.