15 KiB
Home AI Assistant
Домашний ИИ-ассистент с REST API, веб-интерфейсом и помидоро-таймером. LLM — OpenRouter (по умолчанию DeepSeek).
Возможности
- Чат с потоковыми ответами (SSE), скриншоты и vision-разбор изображений
- Помидоро-таймер с циклом работа/перерывы, управление из чата (tool calling)
- Долгосрочная память, профиль пользователя, опциональный RAG (Qdrant)
- Фитнес: дневник, TDEE/Navy, графики веса и состава тела
- Списки покупок, календарь напоминаний
- Персонаж и генерация картинок (ComfyUI / RP Chat)
- Интеграции: Taiga, Gitea, погода, утренний дайджест, Netdata
- Мультипользовательская авторизация по API-токену (
/login) - Веб-интерфейс: Чат, Помидоро, Персонаж, Память, Фитнес, Покупки, Календарь, Настройки
- REST API для внешних клиентов — см. Telegram-бот
Быстрый старт
1. Настройка окружения
cp .env.example .env
Заполните в .env:
OPENROUTER_API_KEY=sk-or-v1-...
BACKEND_PORT=8080
FRONTEND_PORT=3080
Если порт занят (например, 3000 уже используется Gitea), смените FRONTEND_PORT на свободный.
2. Запуск через Docker
docker compose up --build
- Backend API: http://localhost:${BACKEND_PORT:-8080}
- Web UI: http://localhost:${FRONTEND_PORT:-3080}
- Healthcheck: http://localhost:8080/api/v1/health
Prod за nginx: при загрузке скриншотов возможна ошибка 413 Request Entity Too Large — дефолтный лимит nginx 1 MB. На host nginx (Ubuntu перед docker) добавьте client_max_body_size 64m; в server { } и в location /api/. Пример: deploy/nginx-host-assistant.conf.example. После правки: sudo nginx -t && sudo systemctl reload nginx. Контейнер frontend тоже поднимает лимит в frontend/nginx.conf — пересоберите образ.
Порты в .env:
| Переменная | По умолчанию | Назначение |
|---|---|---|
BACKEND_PORT |
8080 | API с хоста |
FRONTEND_PORT |
3080 | Веб-морда с хоста |
VITE_DEV_PORT |
5173 | Frontend при npm run dev |
TAIGA_PORT |
9000 | Taiga (фаза 2) |
GITEA_PORT |
3000 | Gitea HTTP (фаза 2) |
GITEA_SSH_PORT |
222 | Gitea SSH (фаза 2) |
QDRANT_PORT |
6333 | Qdrant HTTP |
POSTGRES_USER / POSTGRES_PASSWORD / POSTGRES_DB |
assistant | PostgreSQL в docker compose |
3. Локальная разработка
Backend:
cd backend
python -m venv .venv
.venv\Scripts\activate # Windows
pip install -r requirements-dev.txt
uvicorn app.main:app --reload --port 8080
Для локального backend без Docker задайте в .env:
DATABASE_URL=sqlite:///./data/assistant.db
Frontend:
cd frontend
npm install
npm run dev
Vite dev-server: http://localhost:5173 (проксирует /api на backend).
REST API
Полная схема — Swagger UI: http://localhost:${BACKEND_PORT:-8080}/docs
Основные эндпоинты (префикс /api/v1, авторизация Authorization: Bearer <token> если AUTH_REQUIRED=true):
| Method | Path | Описание |
|---|---|---|
| POST | /login |
Получить сессию по API-токену |
| GET | /health |
Healthcheck |
| POST/GET | /chat/sessions |
Чат-сессии и история |
| POST | /chat/sessions/{id}/messages |
Сообщение (SSE) |
| GET/POST | /pomodoro/* |
Таймер |
| GET/PUT | /memory, /profile |
Память и профиль |
| GET/POST | /fitness/* |
Фитнес, графики /fitness/charts |
| GET/POST | /shopping/* |
Списки покупок |
| GET/POST | /reminders/* |
Напоминания и календарь |
| GET/POST | /documents/* |
Загрузка документов (RAG) |
| GET | /homelab/status, /homelab/weather |
Homelab |
| GET/PUT | /settings |
RAG toggle, пользователи |
| GET/POST | /projects, /work-items |
Taiga + Gitea |
| POST | /webhooks/gitea |
Webhook автозакрытия |
Taiga + Gitea (фаза 2)
Taiga и Gitea обычно работают на хосте (не в Docker compose). Контейнер backend достучится через host.docker.internal (настроено в docker-compose.yml). Публичные URL — в .env (TAIGA_PUBLIC_URL, GITEA_PUBLIC_URL).
Настройка .env
TAIGA_BASE_URL=http://host.docker.internal:9000
TAIGA_USERNAME=...
TAIGA_PASSWORD=...
TAIGA_PUBLIC_URL=https://taiga.example.com
GITEA_BASE_URL=http://host.docker.internal:3000
GITEA_TOKEN=... # Settings → Applications → Generate Token
GITEA_PUBLIC_URL=https://git.example.com
GITEA_WEBHOOK_SECRET=... # произвольная строка
Первый запуск
# 1. Синхронизировать проекты Taiga (ID подтянутся автоматически)
curl -X POST http://localhost:8080/api/v1/projects/sync-taiga
# 2. Привязать Gitea repo к проекту Taiga
curl -X PUT http://localhost:8080/api/v1/projects/home-assistant/gitea \
-H "Content-Type: application/json" \
-d '{"gitea_owner":"Grigo","gitea_repo":"Home_assistant","default_branch":"main"}'
Gitea webhook
В репозитории: Settings → Webhooks → Add Webhook:
- URL (выбери один вариант):
- Рекомендуется:
https://assistant.example.com/api/v1/webhooks/gitea— nginx →127.0.0.1:${BACKEND_PORT} - Если Gitea в Docker:
http://172.17.0.1:${BACKEND_PORT}/api/v1/webhooks/gitea— не127.0.0.1(это localhost контейнера Gitea)
- Рекомендуется:
- Content type:
application/json - Secret: значение
GITEA_WEBHOOK_SECRET - Events: Push
Проверка из контейнера Gitea: docker exec gitea wget -qO- http://172.17.0.1:8202/api/v1/health
Test delivery в Gitea должен вернуть 200, не 0.
Автозакрытие по коммиту
В сообщении коммита:
fix: кнопка сохранения
Closes gitea #12, taiga #45
Закроются Gitea issue #12 и Taiga story #45 (если только один ref — второй найдётся по связи в БД).
Чат
«Заведи баг: кнопка не сохраняет настройки» → create_work_item → Taiga story + Gitea issue + ветка feature/45-....
Структура проекта
backend/ FastAPI, OpenRouter, PostgreSQL (docker) / SQLite (local dev)
frontend/ React + Vite
telegram-bot/ Telegram-клиент (отдельный VPS)
data/ uploads, generated media, SQLite-бэкап при миграции
deploy/ примеры nginx
PostgreSQL
В docker compose по умолчанию поднимается PostgreSQL 16. Переменные — POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB в .env.
Миграция с SQLite
Если уже есть ./data/assistant.db:
# 1. Бэкап
cp -a data data.bak.$(date +%Y%m%d)
# 2. Поднять postgres и пересобрать backend (скрипт миграции в образе)
docker compose up -d postgres
docker compose build backend
# 3. Dry-run (подсчёт строк)
docker compose run --rm backend python scripts/migrate_sqlite_to_postgres.py --dry-run
# 4. Импорт (DATABASE_URL уже указывает на postgres в compose)
docker compose run --rm backend python scripts/migrate_sqlite_to_postgres.py
# 5. Перезапуск
docker compose up -d
Флаги: --force — очистить Postgres перед импортом; --sqlite-path — путь к файлу.
SQLite-файл не удаляется — остаётся бэкапом.
Память и контекст
Долгосрочная память в БД (PostgreSQL или SQLite). При включённом RAG — семантический поиск фактов через Qdrant.
| Слой | Что хранит |
|---|---|
| Профиль | имя, timezone, language, notes |
| Факты | устойчивые знания с категорией и важностью |
| Сводка чата | краткое содержание длинной сессии |
В system prompt на каждый ответ: персонаж → время → память → фитнес → погода → помидоро → проекты.
История чата обрезается до 40 последних сообщений; раннее — в session_summaries.
Автоизвлечение: после каждого ответа LLM анализирует ход диалога и сохраняет
устойчивые факты (source=auto). Отключить: MEMORY_AUTO_EXTRACT=false.
UI: вкладка /memory — профиль, факты, JSON-снимок для отладки.
Tools
remember_fact— «запомни, что…»recall_memories— поиск по памятиforget_memory— удалить факт по idupdate_profile— имя, часовой пояс и т.д.update_session_summary— сжать тему длинного чата
API
| Method | Path | Описание |
|---|---|---|
| GET | /api/v1/memory |
снимок памяти (+ ?session_id=) |
| GET/PUT | /api/v1/profile |
профиль |
| GET/POST | /api/v1/memory/facts |
список / создать факт |
| DELETE | /api/v1/memory/facts/{id} |
забыть |
| PUT | /api/v1/memory/sessions/{id}/summary |
сводка чата |
RAG (Qdrant)
Векторный поиск по фактам памяти и загруженным документам. Два условия одновременно:
RAG_ENABLED=trueв.env(Qdrant должен быть доступен)- Toggle «RAG включён» в Настройки (веб-UI)
Загрузка документов: Settings → документы, или POST /api/v1/documents/upload (текст: .txt, .md, .json, .csv).
Tool в чате: search_documents.
Backfill существующих фактов:
docker compose exec backend python -m app.rag.migrate_memory_to_qdrant
Ограничения: нет API удаления документов; session summaries индексируются, но в чате читаются из SQLite; при ошибке embedding — fallback на топ фактов из БД.
Фитнес-трекер
Профиль, дневник (еда/вода/вес/шаги/тренировки), калькуляторы TDEE и Navy (WHR/LBM/FFMI),
графики веса и состава тела (/fitness, API /fitness/charts), LLM-оценка ккал/БЖУ,
lookup wger + Open Food Facts, vision-импорт скриншотов Mi Fitness, напоминания в чат.
Чат: «обед: гречка 200г, курица 150г», «выпил 300 мл воды», «жим 80×5×3».
Списки покупок
Несколько списков, позиции с количеством, отметка «куплено». Вкладка /shopping, tools в чате (add_shopping_items, list_shopping_lists, …).
Чат: «добавь молоко и хлеб в продукты», «что в списке покупок», «отметь молоко купленным».
Homelab API (фаза 4)
Интеграции с домашней инфраструктурой:
| Сервис | URL по умолчанию | Назначение |
|---|---|---|
| Open-Meteo | $OPENMETEO_BASE_URL |
Погода в контексте и tool get_weather |
| ComfyUI | $COMFYUI_BASE_URL |
fallback / рофл-watcher |
| RP Chat (aiChatBot) | http://host.docker.internal:8201 |
generate_image: sd-prompt + Anima; appearance в /character |
| Netdata | http://host.docker.internal:19999 |
Алерты warning/critical → notice в чат |
Утренний дайджест (MORNING_DIGEST_HOUR=8): погода + RSS (Habr, r/programming по умолчанию).
По запросу: «что на улице», «будет ли дождь» → get_weather; полный брифинг → get_morning_briefing.
Переменные — в .env.example (секция Homelab).
Проверка доступности
В образе backend нет curl/wget. Удобнее всего — API-диагностика (из контейнера или с хоста):
curl -s http://localhost:${BACKEND_PORT:-8202}/api/v1/homelab/status | python3 -m json.tool
Или изнутри backend через Python:
docker compose exec backend python -c "
import os, httpx
for url in [
os.environ.get('OPENMETEO_BASE_URL', 'http://host.docker.internal:8085').rstrip('/') + '/v1/forecast?latitude=59.93&longitude=30.33¤t=temperature_2m',
os.environ.get('COMFYUI_BASE_URL', 'http://host.docker.internal:8188').rstrip('/') + '/system_stats',
'http://host.docker.internal:19999/api/v1/info',
]:
try:
r = httpx.get(url, timeout=10)
print(url, '->', r.status_code, r.text[:120])
except Exception as e:
print(url, '-> ERROR', e)
"
По умолчанию Anima (как в aiChatBot): COMFYUI_UNET + COMFYUI_CLIP + COMFYUI_VAE + style LoRA.
COMFYUI_CHECKPOINT оставь пустым. Для SD1.5/Pony — укажи checkpoint и очисти COMFYUI_UNET.
Telegram-бот
Отдельный сервис в telegram-bot/: диалог с ассистентом с VPS, привязка API-токена, дублирование notice из чата.
Создание пользователя: Settings → Пользователи или docker compose exec backend python -m scripts.create_user.
Разработка и тесты
cd backend
pip install -r requirements-dev.txt
pytest tests/ -q
Перед деплоем Jenkins запускает pytest (см. Jenkinsfile). CI на GitHub Actions нет — только Jenkins на linux-ноде.
Следующие фазы
- Taiga/fitness в утреннем дайджесте
- LLM-мотивация в фитнес-напоминаниях (сейчас шаблонные строки)
- API удаления документов и re-index при включении RAG задним числом
Модель
По умолчанию: deepseek/deepseek-chat через OpenRouter. Альтернатива для болтовни: google/gemini-2.0-flash.