266 lines
10 KiB
Markdown
266 lines
10 KiB
Markdown
# Home AI Assistant
|
||
|
||
Домашний ИИ-ассистент с REST API, веб-интерфейсом и помидоро-таймером. LLM — OpenRouter (по умолчанию DeepSeek).
|
||
|
||
## Возможности (MVP)
|
||
|
||
- Чат с потоковыми ответами (SSE)
|
||
- Управление помидоро из чата через tool calling
|
||
- REST API для внешних клиентов (Telegram-бот, мобильное приложение)
|
||
- Веб-морда: вкладки «Чат» и «Помидоро»
|
||
|
||
## Быстрый старт
|
||
|
||
### 1. Настройка окружения
|
||
|
||
```bash
|
||
cp .env.example .env
|
||
```
|
||
|
||
Заполните в `.env`:
|
||
|
||
```env
|
||
OPENROUTER_API_KEY=sk-or-v1-...
|
||
BACKEND_PORT=8080
|
||
FRONTEND_PORT=3080
|
||
```
|
||
|
||
Если порт занят (например, 3000 уже используется Gitea), смените `FRONTEND_PORT` на свободный.
|
||
|
||
### 2. Запуск через Docker
|
||
|
||
```bash
|
||
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
|
||
|
||
Порты в `.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 (фаза 3) |
|
||
|
||
### 3. Локальная разработка
|
||
|
||
**Backend:**
|
||
|
||
```bash
|
||
cd backend
|
||
python -m venv .venv
|
||
.venv\Scripts\activate # Windows
|
||
pip install -r requirements.txt
|
||
uvicorn app.main:app --reload --port 8080
|
||
```
|
||
|
||
**Frontend:**
|
||
|
||
```bash
|
||
cd frontend
|
||
npm install
|
||
npm run dev
|
||
```
|
||
|
||
Vite dev-server: http://localhost:5173 (проксирует `/api` на backend).
|
||
|
||
## REST API
|
||
|
||
| Method | Path | Описание |
|
||
|--------|------|----------|
|
||
| GET | `/api/v1/health` | Healthcheck |
|
||
| POST | `/api/v1/chat/sessions` | Создать чат-сессию |
|
||
| GET | `/api/v1/chat/sessions` | Список сессий |
|
||
| GET | `/api/v1/chat/sessions/{id}` | История сообщений |
|
||
| POST | `/api/v1/chat/sessions/{id}/messages` | Отправить сообщение (SSE) |
|
||
| DELETE | `/api/v1/chat/sessions/{id}` | Удалить сессию |
|
||
| GET | `/api/v1/pomodoro/status` | Статус таймера |
|
||
| POST | `/api/v1/pomodoro/start` | Старт `{duration_min, task_note}` |
|
||
| POST | `/api/v1/pomodoro/pause` | Пауза |
|
||
| POST | `/api/v1/pomodoro/resume` | Продолжить |
|
||
| POST | `/api/v1/pomodoro/stop` | Стоп `{result, completed}` |
|
||
| GET | `/api/v1/pomodoro/history` | История сессий |
|
||
| GET | `/api/v1/projects` | Проекты Taiga + привязка Gitea |
|
||
| POST | `/api/v1/projects/sync-taiga` | Синхронизировать проекты из Taiga |
|
||
| PUT | `/api/v1/projects/{slug}/gitea` | Привязать Gitea repo |
|
||
| POST | `/api/v1/work-items` | Создать фичу/баг → Taiga + Gitea |
|
||
| GET | `/api/v1/work-items` | Список work items |
|
||
| POST | `/api/v1/webhooks/gitea` | Webhook для автозакрытия по push |
|
||
|
||
## Taiga + Gitea (фаза 2)
|
||
|
||
Taiga и Gitea работают **на хосте** (не в Docker):
|
||
- Taiga: `127.0.0.1:9000` → `taiga.grigowashere.ru`
|
||
- Gitea: `127.0.0.1:3000` → `git.grigowashere.ru`
|
||
|
||
Контейнер backend достучится через `host.docker.internal` (настроено в `docker-compose.yml`).
|
||
|
||
### Настройка `.env`
|
||
|
||
```env
|
||
TAIGA_BASE_URL=http://host.docker.internal:9000
|
||
TAIGA_USERNAME=...
|
||
TAIGA_PASSWORD=...
|
||
TAIGA_PUBLIC_URL=https://taiga.grigowashere.ru
|
||
|
||
GITEA_BASE_URL=http://host.docker.internal:3000
|
||
GITEA_TOKEN=... # Settings → Applications → Generate Token
|
||
GITEA_PUBLIC_URL=https://git.grigowashere.ru
|
||
GITEA_WEBHOOK_SECRET=... # произвольная строка
|
||
```
|
||
|
||
### Первый запуск
|
||
|
||
```bash
|
||
# 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, SQLite, помидоро
|
||
frontend/ React + Vite, чат и таймер
|
||
data/ SQLite БД (создаётся автоматически)
|
||
```
|
||
|
||
## Память и контекст (фаза 3a)
|
||
|
||
Долгосрочная память в SQLite, без векторов:
|
||
|
||
| Слой | Что хранит |
|
||
|------|------------|
|
||
| **Профиль** | имя, 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` — удалить факт по id
|
||
- `update_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` | сводка чата |
|
||
|
||
## Фитнес-трекер
|
||
|
||
Профиль, дневник (еда/вода/вес/тренировки), калькуляторы TDEE, LLM-оценка ккал/БЖУ,
|
||
lookup wger + Open Food Facts, напоминания в чат (`💪`), вкладка `/fitness`.
|
||
|
||
Чат: «обед: гречка 200г, курица 150г», «выпил 300 мл воды», «жим 80×5×3».
|
||
|
||
## Homelab API (фаза 4)
|
||
|
||
Интеграции с домашней инфраструктурой:
|
||
|
||
| Сервис | URL по умолчанию | Назначение |
|
||
|--------|------------------|------------|
|
||
| Open-Meteo | `http://192.168.1.109:8085` | Погода СПб в контексте и tool `get_weather` |
|
||
| ComfyUI | `http://192.168.1.109:8188` | 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-диагностика (из контейнера или с хоста):
|
||
|
||
```bash
|
||
curl -s http://localhost:${BACKEND_PORT:-8202}/api/v1/homelab/status | python3 -m json.tool
|
||
```
|
||
|
||
Или изнутри backend через Python:
|
||
|
||
```bash
|
||
docker compose exec backend python -c "
|
||
import httpx
|
||
for url in [
|
||
'http://192.168.1.109:8085/v1/forecast?latitude=59.93&longitude=30.33¤t=temperature_2m',
|
||
'http://192.168.1.109:8188/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`.
|
||
|
||
## Следующие фазы
|
||
|
||
- RAG по файлам (Qdrant)
|
||
- Telegram-бот
|
||
- Taiga/fitness в утреннем дайджесте
|
||
- Графики веса, LLM-мотивация в напоминаниях
|
||
|
||
## Модель
|
||
|
||
По умолчанию: `deepseek/deepseek-chat` через OpenRouter. Альтернатива для болтовни: `google/gemini-2.0-flash`.
|