ВСЕГДА ОТВЕЧАТЬ НА РУССКОМ ЯЗЫКЕ!
AI-стилист, который анализирует фото и показывает, как человек воспринимается в разных контекстах (знакомства, карьера, соцсети), с генерацией улучшенных образов.
Стек: Python 3.12 (FastAPI + ARQ + aiogram) + React SPA (Vite + Tailwind) + PostgreSQL + Redis
Платформы: Telegram bot, Web app (Vercel), OK/VK Mini Apps, RU edge
Проект развёрнут в двух независимых регионах с одним codebase:
- Global (Railway,
MARKET_ID=global) — доменailookstudio.vercel.app, Telegram-бот@AI_Look_Studio_bot(единственный, webhook, Telegram Stars), платежи Xsolla для веба. - RU edge (VPS,
MARKET_ID=ru) — доменailookstudio.ru, только веб-приложение, платежи YooKassa. Бота на VPS нет начиная с 1.62.0 — РКН блокирует исходящий трафик кapi.telegram.org, поэтому polling из РФ невозможен; webhook-бот живёт на Railway и принимает обновления одинаково для всех языков.
Ключевые инварианты, которые надо знать перед любым изменением кода:
- PII RU-юзеров никогда не покидает VPS для веб-флоу. Edge →
primary вызовы используют синтетический
internal_user_idиextra="forbid"на Pydantic-схеме (см.RemoteAnalysisRequestв src/api/v1/internal.py). - Один Telegram-бот на Railway. Лендинг-ссылки внутри бота
выбираются по
language_codeчерезsettings.resolve_landing_url(src/config.py). Кросс-регион нужен только приlink-tokenredeem: RU edge тянетimage_creditsбота с Railway через подписанный internal endpoint (src/api/v1/internal_bot.py). - Каждое фото сначала проходит через
PrivacyLayer.sanitize_and_normalize(EXIF/ICC strip), потом — в модель. Оригиналы хранятся в Redis-stash 15 мин и удаляются. - Логи маскируют PII через
PIIFilter. - Админки две и независимые — у каждого региона свой
/admin.AdminStatusBanner(web/src/components/admin/AdminStatusBanner.tsx) делает разделение явным.
Полный документ: docs/ARCHITECTURE.md — содержит диаграммы, описание DNS-rollout'а, CMS-репликацию, бот-маршрутизацию и список ограничений.
| Документ | Содержание |
|---|---|
| docs/ARCHITECTURE.md | Двухрегиональная архитектура: домены, PII-инварианты, edge→primary delegation, бот-маршрутизация по языку, DNS rollout |
| docs/DEPLOYMENT.md | CI/CD, Railway, Vercel, RU edge, переменные окружения, чеклисты, troubleshooting |
| docs/DEVELOPMENT.md | Локальный запуск, структура проекта, тесты, гайды по добавлению режимов/стилей/auth |
| docs/master_product_constitution.md | Продуктовая конституция: концепция AI-стилиста, принципы, UX, scoring, pipeline requirements |
| docs/architecture/reserved.md | Карта reserved-кода (orchestrator/advanced/, запасные провайдеры): что там лежит, зачем сохранено, как включать и дорожная карта Phase 2 (Scenario Engine) и Phase 3 (FLUX через FAL.ai) |
Runtime сегодня работает по одному single-pass пути (AnalysisPipeline → ImageGenerationExecutor.single_pass → Reve). Multi-pass планнер, capability-router и fallback-цепочки провайдеров намеренно изолированы в src/orchestrator/advanced/ и под флагами MULTI_PASS_ENABLED / SEGMENTATION_ENABLED / IMAGE_GEN_PROVIDER. Подробности — в docs/architecture/reserved.md.
Любое изменение в коде бессмысленно, пока все процессы с очередью не обновлены одной и той же сборкой:
| Сервис | Зачем |
|---|---|
| API (app) | Принимает фото, пишет задачу и кладёт вход в Redis (ratemeai:task_input:…) |
| Worker | Берёт задачу из ARQ и должен та же логика, что и API (чтение из Redis → пайплайн) |
| Bot | Ходит в API; при изменении контрактов API стоит обновлять и его |
Если обновить только API, а воркер оставить старый образ, снова возможны ошибки вроде File not found: inputs/... — воркер не умеет новый протокол.
Что делать после мерджа / смены ветки:
- Пересобрать образ(ы) и задеплоить app + worker + bot с одного commit (на Railway — redeploy каждого сервиса или общий pipeline).
- Проверка:
GET https://<ваш-api>/health— в ответе должны бытьversion(см.src/version.py) и при необходимостиgit, если задалиDEPLOY_GIT_SHAв env. - В логах воркера при старте должна быть строка
Worker started RateMeAI version=…с той жеversion, что и у API.
Версию приложения поднимайте в src/version.py при каждой выкладке, чтобы по логам и /health было видно расхождение сервер ↔ репозиторий. Описание изменений добавляйте в CHANGELOG.md (newest-first) — src/version.py теперь содержит только литерал APP_VERSION, чтобы не раздувать импорт на старте app/worker/bot.
| Компонент | Что должен видеть | Зачем |
|---|---|---|
| app, worker | Публичный HTTPS URL API (или туннель типа ngrok в разработке) | LocalStorageProvider.get_url строит ссылки на файлы в /storage/.... Эти URL попадают в результат задачи и в share-карточку. |
| bot (Docker Compose) | Внутренний URL сервиса app, например http://app:8000 |
В docker-compose.yml для сервиса bot задано API_BASE_URL: http://app:8000, чтобы запросы шли по Docker-сети, а не наружу. |
| Telegram | Только публичный HTTPS | send_photo по URL заставляет серверы Telegram скачать файл по указанному адресу. http://localhost и приватные IP не подходят. |
Итог: в .env для локальной разработки с ботом в Docker оставьте публичный API_BASE_URL для app/worker (туннель или боевой домен), а переопределение для бота задавайте только через Compose, как в репозитории.
При STORAGE_PROVIDER=local файлы лежат на диске; в Compose для app, worker, bot смонтирован один каталог ./storage. Входное фото дублируется в Redis (ratemeai:task_input:<task_id>) до обработки воркером — так анализ не зависит от общего тома между API и worker.
В облаке без общего тома (несколько реплик API или воркера) используйте S3-совместимое хранилище:
- Задайте
STORAGE_PROVIDER=s3. - Заполните
S3_ENDPOINT,S3_ACCESS_KEY,S3_SECRET_KEY,S3_BUCKET,S3_REGION. - Либо настройте публичный доступ к объектам и укажите
S3_PUBLIC_BASE_URL(CDN или публичный endpoint бакета), либо полагайтесь на presigned URL изS3StorageProvider. - Установите
S3_PRESIGN_TTL_SECONDSс запасом (например 3600), чтобы ссылка не истекла до того, как пользователь откроет результат в боте.
Для Replicate провайдер передаёт в модель публичный URL референса — объекты должны быть доступны по URL извне.
cp .env.example .env
# Заполните секреты. API_BASE_URL — публичный, если тестируете Telegram с медиа по URL.
docker compose up --build- API:
http://localhost:8000 - Проверка:
GET http://localhost:8000/health
Интеграционные API-тесты (tests/test_api/) требуют PostgreSQL и Redis на 127.0.0.1 (порты по умолчанию как в Compose). Если сервисы не запущены, эти тесты пропускаются (skipped), остальные всё равно выполняются.
python -m pytest
# или: docker compose up -d postgres redis && python -m pytestВ CI см. .github/workflows/ci.yml.
- Поднять стек,
GET /health→status, полеversionсовпадает сsrc/version.py. - В боте:
/start, отправить портретное фото (с лицом). - Режим rating (через
/rating): дождаться результата, проверить текст и карточку. - Режим dating: выбор стиля (На прогулке / Студия / Кафе) → сгенерированное фото + скор.
- Режим cv: выбор стиля (Корпоративный / Креативный / Нейтральный) → профессиональный образ.
- Режим social: выбор стиля (Influencer / Luxury / Casual / Artistic) → образ для соцсетей.
- Режим emoji (через
/emoji): стикер-аватар из фото. - При ошибке пайплайна: в боте отображается сообщение из
error_messageзадачи.