Платформа для автоматизированного анализа пользовательского опыта на основе данных Яндекс.Метрики. Автоматически выявляет UX-проблемы, сравнивает версии продукта, анализирует воронки конверсии и генерирует гипотезы с помощью YandexGPT AI.
Разработано в рамках хакатона PSB "AI Challenge: Banking".
- Функциональность
- Технологический стек
- Установка и запуск
- Архитектура системы
- Основные функции и их реализация
- API документация
- Команды управления
- Структура проекта
- Конфигурация
- ETL процесс: Импорт данных из Parquet-файлов Яндекс.Метрики, нормализация, расчет метрик
- Детекция UX-проблем: Автоматическое выявление 10+ типов проблем пользовательского опыта
- AI-инсайты: Генерация гипотез и решений с помощью YandexGPT
- Воронки конверсии: Создание, анализ и сравнение воронок с разбивкой по когортам
- Сегментация пользователей: K-Means кластеризация с AI-генерацией названий когорт
- Сравнение версий: Детальное сравнение метрик, проблем и когорт между версиями продукта
- Веб-интерфейс: Dashboard, Issues List, Compare, Funnels с интуитивным UI
- RAGE_CLICK - Множественные клики по одному элементу за короткое время
- DEAD_CLICK - Клики по неинтерактивным элементам или элементам с ошибками
- HIGH_BOUNCE - Высокий процент отказов на страницах
- LOOPING - Навигационные петли (A→B→A, A→B→C→A)
- NAVIGATION_BACK - Частое использование кнопки "Назад"
- WANDERING - Блуждание по сайту (10+ просмотров без достижения целей)
- FORM_ABANDON - Брошенные формы
- FORM_FIELD_ERRORS - Проблемы с заполнением форм (долгий ввод, множественные правки)
- FUNNEL_DROPOFF - Критические точки отказа в воронках конверсии
- SCAN_AND_DROP - Быстрый скролл до конца страницы с последующим выходом
- SEARCH_FAIL - Неудачные поисковые запросы
- Python 3.11 - Основной язык разработки
- Django 5.0 - Web-фреймворк
- PostgreSQL 15 - База данных
- Pandas 2.0+ - Обработка данных
- PyArrow - Оптимизированное чтение Parquet
- scikit-learn - Машинное обучение (K-Means кластеризация)
- YandexGPT - Генерация гипотез и решений через Yandex Cloud API
- React 18+ - UI-фреймворк
- Vite - Сборщик и dev-сервер
- Tailwind CSS - Стилизация
- Chart.js + react-chartjs-2 - Визуализация графиков
- React Router DOM - Роутинг (в package.json, в текущей реализации используется условный рендеринг в App.jsx)
- Кастомные шрифты - Bahnschrift через fonts.css
Примечание: В текущей версии проекта фронтенд реализован через Django Templates (HTML в папке templates/). React-версия находится в разработке.
- Docker & Docker Compose - Контейнеризация и оркестрация
- PostgreSQL - Реляционная БД
- Docker & Docker Compose (версия 2.0+)
- Файлы данных
.parquet:2022_yandex_metrika_visits.parquet2022_yandex_metrika_hits.parquet2024_yandex_metrika_visits.parquet2024_yandex_metrika_hits.parquet
git clone https://github.com/your-username/ux-insight.git
cd ux-insightРазместите Parquet-файлы в корне проекта (рядом с manage.py):
ux-insight/
├── manage.py
├── 2022_yandex_metrika_visits.parquet
├── 2022_yandex_metrika_hits.parquet
├── 2024_yandex_metrika_visits.parquet
└── 2024_yandex_metrika_hits.parquet
Создайте файл .env на основе .env.example:
cp .env.example .envМинимальная конфигурация .env:
# Django
SECRET_KEY=your-secret-key-here
DEBUG=False
# Database (опционально, если используете внешнюю БД)
DB_HOST=db
DB_PORT=5432
DB_NAME=postgres
DB_USER=postgres
DB_PASSWORD=postgres
# Yandex Cloud AI (опционально, для AI-инсайтов)
FOLDER_ID=your-folder-id
API_KEY=your-api-key
# Автоматическая загрузка данных при старте
AUTO_INGEST=1
AUTO_FUNNELS=1Важно:
- Если
FOLDER_IDиAPI_KEYне указаны, AI-функции будут использовать заглушки AUTO_INGEST=1автоматически загрузит данные при первом запускеAUTO_FUNNELS=1автоматически создаст и рассчитает preset-воронки
Фронт находится в каталоге frontend (отдельно от Django). Требуется Node.js 18+.
cd frontend
npm install
npm run dev # по умолчанию http://localhost:5173ENV-файл для фронта: frontend/.env.example — при необходимости скопируйте в .env и пропишите VITE_API_BASE_URL (адрес Django API).
docker-compose up --build -dЭта команда:
- Соберет Docker-образы
- Запустит PostgreSQL и Django приложение
- Применит миграции БД
- При
AUTO_INGEST=1автоматически загрузит данные (если БД пустая) - При
AUTO_FUNNELS=1создаст preset-воронки
Проверьте логи контейнеров:
docker-compose logs web
docker-compose logs dbdocker-compose exec web python manage.py createsuperuserОткройте браузер: http://localhost:8000
┌─────────────────┐
│ Parquet Files │ (Yandex Metrica)
└────────┬────────┘
│
▼
┌─────────────────┐
│ ingest_data │ → ETL процесс
│ (Management │ - Загрузка данных
│ Command) │ - Нормализация
└────────┬────────┘ - Расчет метрик
│ - Детекция проблем
▼ - Сегментация когорт
┌─────────────────┐
│ PostgreSQL │ ← База данных
└────────┬────────┘
│
▼
┌─────────────────┐
│ Django Views │ → Web-интерфейс
│ + Templates │ - Dashboard
└────────┬────────┘ - Issues List
│ - Compare
▼ - Funnels
┌─────────────────┐
│ YandexGPT API │ ← AI-инсайты
└─────────────────┘
Версия продукта для сравнения (например, "v1.0 (2022)", "v2.0 (2024)")
Сессия пользователя (Visit из Метрики):
- Базовые метрики:
duration_sec,bounced,page_views - Технические данные:
browser,os,screen_width/height,device_category - Цели:
goals_id(JSON список ID достигнутых целей)
Действие внутри сессии (Hit):
- URL, timestamp, page_title
- Метрики:
time_on_page,scroll_depth,is_exit - Технические данные:
browser,os,device_category
Обнаруженная UX-проблема:
- Тип проблемы, severity, описание
- Метрики влияния:
affected_sessions,impact_score - AI-инсайты:
ai_hypothesis,ai_solution - Роутинг:
trend,priority,recommended_specialists
Сегмент пользователей (результат K-Means кластеризации):
- Название (AI-генерированное)
- Метрики:
percentage,avg_bounce_rate,avg_duration member_client_ids(JSON) - список client_id для фильтрации воронок
Воронка конверсии:
- Название, описание, шаги (JSON)
- Настройки:
require_sequence,allow_skip_steps - Флаг
is_preset(preset vs кастомная)
Кэшированные метрики воронки:
- Общие метрики:
total_entered,total_completed,overall_conversion - Разбивка по шагам и когортам
- Флаг
includes_cohortsдля разделения общих и когортных метрик
Агрегированные метрики по страницам:
total_views,unique_visitorsexit_rate,avg_time_on_page,avg_scroll_depthdominant_device,dominant_cohort
Дневная статистика по версиям:
total_sessions,total_bounces,bounce_rateavg_duration
Файл: analytics/management/commands/ingest_data.py
-
Загрузка данных из Parquet
df_visits = pd.read_parquet(visits_path, columns=visits_columns) df_hits = pd.read_parquet(hits_path, columns=hits_columns)
-
Нормализация client_id
- Приведение к единому формату
- Обработка
counterUserIDHashдля связи hits с visits
-
Создание VisitSession
- Batch-обработка (batch_size=10000) для оптимизации памяти
- Парсинг
goalsID(может быть список, строка списка или число) - Сохранение технических метрик (browser, OS, screen size)
-
Создание PageHit
- Batch-обработка (batch_size=50000)
- Связывание с VisitSession через client_id
-
Расчет time_on_page
- Используется SQL window function для производительности
- Расчет как разница между последовательными hits в сессии
UPDATE analytics_pagehit ph1 SET time_on_page = EXTRACT(EPOCH FROM ( SELECT ph2.timestamp FROM analytics_pagehit ph2 WHERE ph2.session_id = ph1.session_id AND ph2.timestamp > ph1.timestamp ORDER BY ph2.timestamp ASC LIMIT 1 ) - ph1.timestamp)
-
Расчет метрик страниц (
calculate_page_metrics)- Агрегация по URL: views, unique visitors, exit rate
- Определение dominant device и page_title
- Оптимизация: один запрос для всех страниц, затем группировка
-
Детекция UX-проблем (
run_analysis)- Вызов отдельных детекторов для каждого типа проблемы
- Расчет impact_score на основе метрик
- Генерация AI-инсайтов
-
Сегментация пользователей (
segment_users_into_cohorts)- K-Means кластеризация на основе поведения
- AI-генерация названий когорт
- Сохранение
member_client_idsдля фильтрации воронок
Файл: analytics/management/commands/ingest_analysis.py
RAGE_CLICK:
- Группировка кликов по координатам (x, y) и URL
- Обнаружение 3+ кликов в радиусе 10px за 5 секунд
- Impact = количество кликов × 0.5
DEAD_CLICK:
- Клики без последующего перехода или действия
- Impact = количество кликов × 0.3
HIGH_BOUNCE:
- Страницы с bounce_rate > 70% и views > 20
- Impact = (bounce_rate - 70) × views / 100
LOOPING:
- Обнаружение паттернов A→B→A или A→B→C→A
- Канонизация (A↔B считается как один паттерн)
- Impact = количество петель × 1.0
WANDERING:
- Сессии с >10 просмотрами без достижения целей (goalsID пустой)
- Impact = количество сессий × 0.4
FORM_ABANDON:
- Формы с долгим временем заполнения (>60 сек) без завершения
- Impact = количество брошенных форм × 0.6
FUNNEL_DROPOFF:
- Анализ воронок на предмет аномальных оттоков на шагах
- Impact = процент оттока × количество пользователей / 10
Файл: analytics/ai_service.py
-
Интеграция с YandexGPT
url = "https://llm.api.cloud.yandex.net/foundationModels/v1/completion" headers = { "Authorization": f"Api-Key {API_KEY}", "x-folder-id": FOLDER_ID }
-
Формирование промпта
- Системный промпт с контекстом проекта (приемная комиссия МАИ)
- Пользовательский промпт с описанием проблемы и метриками
- Расширенный контекст: page_title, dominant_cohort, dominant_device
-
Формат ответа
- Строгий формат: "Гипотеза: ... Исправить: ..."
- Парсинг ответа для разделения hypothesis и solution
-
Fallback
- Если API недоступен, используются заглушки на основе типа проблемы
Файлы:
analytics/funnel_utils.py- расчет метрикanalytics/management/commands/create_funnels.py- создание preset-воронокanalytics/management/commands/calculate_funnels.py- расчет метрик
- URL-шаги: Точное совпадение или prefix matching URL
- Goal-шаги: Достижение цели из
goals.yamlidentifier: Проверка черезgoalsIDв сессииurl_prefix: URL начинается с заданного префиксаurl_contains: URL содержит заданную строку
-
Фильтрация сессий
- Поиск сессий, которые достигли первого шага воронки
- Проверка последовательности шагов (если
require_sequence=True)
-
Подсчет метрик по шагам
for step_idx, step in enumerate(funnel.steps): # Находим сессии, достигшие этого шага sessions_reached = filter_sessions_by_step(...) step_metrics.append({ 'step_name': step['name'], 'reached': len(sessions_reached), 'conversion_from_prev': conversion_rate })
-
Разбивка по когортам
- Для каждой когорты фильтруются сессии по
member_client_ids - Рассчитываются отдельные метрики для каждой когорты
- Генерируется AI-анализ для каждой когорты
- Для каждой когорты фильтруются сессии по
-
Кэширование
- Метрики сохраняются в
FunnelMetricsдля быстрого доступа - Отдельные записи для общих метрик и метрик по когортам
- Метрики сохраняются в
-
Web-интерфейс:
/funnels/create/- Форма с динамическим добавлением шагов
- Выбор типа шага (URL или Goal)
- Автодополнение целей из
goals.yaml
-
Валидация
- Минимум 2 шага
- Проверка корректности URL/Goal
-
Редактирование и удаление
- Только для кастомных воронок (preset защищены)
Файл: analytics/management/commands/ingest_data.py → segment_users_into_cohorts
-
Сбор признаков пользователя
user_behavior = df_visits.groupby('clientID').agg({ 'visitDuration': 'mean', # Средняя длительность 'pageViews': 'mean', # Средняя глубина 'visitID': 'count', # Количество визитов 'bounce': 'mean' # Вероятность отказа })
-
Достижение целей
- Для каждой цели из
goals.yamlдобавляется бинарный признак - Identifier goals: проверка через
goalsID - URL goals: проверка через hits
- Для каждой цели из
-
Интересы пользователя
- Анализ URL на наличие ключевых слов:
rating,spis→ interest_ratingnews,novosti→ interest_newscontact,kontak→ interest_contactsapply,priem→ interest_admission- и т.д.
- Анализ URL на наличие ключевых слов:
-
K-Means кластеризация
scaler = StandardScaler() X = scaler.fit_transform(user_behavior[feature_cols]) n_clusters = min(10, max(5, len(user_behavior)//30)) kmeans = KMeans(n_clusters=n_clusters, random_state=42) user_behavior['cluster'] = kmeans.fit_predict(X)
-
AI-генерация названий
- Для каждой когорты формируется описание метрик
- Отправка в YandexGPT для генерации названия
- Добавление деталей (интересы, цели) для уникальности
-
Сохранение
- Сохранение
member_client_idsдля каждой когорты - Используется для фильтрации сессий в воронках
- Сохранение
Файл: analytics/views_compare.py → _build_comparison
-
Общие метрики
- Visits, bounce rate, duration
- Дельта между версиями
-
UX Issues
- Индексация по (issue_type, normalized_url)
- Статусы: new, worse, improved, stable, resolved
- Расчет
impact_diff
-
Страницы
- Сравнение PageMetrics
- Статусы: new, removed, changed, stable
- Дельта exit_rate и avg_time_on_page
- Фильтр малотрафиковых страниц (<20 views)
-
Когорты
- Сравнение по названиям
- Статусы: new, removed, changed
- Изменение процента аудитории
-
Устройства и браузеры
- Разбивка по device_category и browser
- Доля трафика, bounce rate, duration
- Дельта между версиями
Все API endpoints возвращают JSON.
GET /api/versions/
{
"versions": [
{
"id": 1,
"name": "v1.0 (2022)",
"release_date": "2022-01-01",
"is_active": true
}
]
}GET /api/dashboard/
{
"version_stats": [
{
"id": 1,
"name": "v1.0 (2022)",
"total_visits": 3000,
"avg_duration": 245.5,
"bounce_rate": 45.2,
"issue_count": 23,
"critical_issues": 5
}
],
"recent_issues": [...]
}GET /api/compare/?v1=1&v2=2
{
"v1": {"id": 1, "name": "v1.0 (2022)"},
"v2": {"id": 2, "name": "v2.0 (2024)"},
"visits_diff": 500,
"bounce_diff": -5.2,
"duration_diff": 30.5,
"stats_v1": {...},
"stats_v2": {...},
"issues_diff": [
{
"id": 1,
"issue_type": "HIGH_BOUNCE",
"status": "worse",
"impact_diff": 2.5,
"location_readable": "Главная страница"
}
],
"pages_diff": [...],
"cohorts_diff": [...]
}GET /api/issues/?version=1&severity=CRITICAL&issue_type=HIGH_BOUNCE
{
"count": 5,
"results": [
{
"id": 1,
"issue_type": "HIGH_BOUNCE",
"severity": "CRITICAL",
"description": "...",
"location_url": "...",
"readable_location": "Главная страница",
"impact_score": 8.5,
"ai_hypothesis": "...",
"ai_solution": "...",
"trend": "worse",
"priority": "HIGH"
}
]
}GET /api/funnels/?version=1
{
"count": 4,
"results": [
{
"id": 1,
"name": "Поиск рейтингов",
"description": "...",
"steps_count": 2,
"is_preset": true,
"has_metrics": true,
"total_entered": 900,
"total_completed": 450,
"overall_conversion": 50.0
}
]
}GET /api/funnels/<id>/by-cohorts/
{
"funnel": {"id": 1, "name": "..."},
"overall_metrics": {
"total_entered": 900,
"total_completed": 450,
"overall_conversion": 50.0
},
"cohort_breakdown": {
"Когорта 1": {
"entered": 300,
"completed": 180,
"conversion": 60.0,
"ai_analysis": "..."
}
}
}GET /api/cohorts/?version=1
{
"cohorts": [
{
"name": "Активные исследователи",
"percentage": 35.5,
"avg_bounce_rate": 25.0,
"avg_duration": 320,
"users_count": 1065,
"metrics": {...},
"conversion_rates": {...}
}
]
}GET /api/pages/?version=1&limit=50&min_views=20&order=-exit_rate
{
"pages": [
{
"url": "https://priem.mai.ru/",
"norm_url": "https://priem.mai.ru/",
"readable": "Главная страница",
"total_views": 1500,
"unique_visitors": 800,
"exit_rate": 45.2,
"avg_time_on_page": 120,
"dominant_device": "desktop",
"dominant_cohort": "Активные исследователи"
}
]
}docker-compose exec web python manage.py ingest_data \
--visits "2022_yandex_metrika_visits.parquet" \
--hits "2022_yandex_metrika_hits.parquet" \
--product-version "v1.0 (2022)" \
--year 2022Параметры:
--visits- путь к файлу visits--hits- путь к файлу hits--product-version- название версии--year- год данных--clear- очистить существующие данные версии перед загрузкой
docker-compose exec web python manage.py create_funnels \
--product-version "v1.0 (2022)"Создает предустановленные воронки на основе популярных путей пользователей.
# Общие метрики
docker-compose exec web python manage.py calculate_funnels \
--product-version "v1.0 (2022)"
# С разбивкой по когортам
docker-compose exec web python manage.py calculate_funnels \
--product-version "v1.0 (2022)" \
--by-cohorts
# Для конкретной воронки
docker-compose exec web python manage.py calculate_funnels \
--product-version "v1.0 (2022)" \
--funnel-id 1docker-compose exec web python manage.py discover_funnels \
--product-version "v1.0 (2022)" \
--min-support 15 \
--min-percentage 0.5 \
--max-funnels 10Параметры:
--min-support- минимальное количество сессий для последовательности--min-percentage- минимальный процент от общего числа сессий--max-funnels- максимальное количество воронок для создания--min-length/--max-length- длина последовательности (по умолчанию 2-4)
docker-compose exec web python manage.py run_analysis_only \
--product-version "v1.0 (2022)" \
--clear-existingПолезно, если данные уже загружены, но нужно пересчитать только проблемы.
docker-compose exec web python manage.py check_ingestion_statusПоказывает:
- Количество загруженных сессий, hits, метрик
- Количество обнаруженных проблем
- На каком этапе остановилась загрузка (если была прервана)
docker-compose exec web python manage.py refresh_ai \
--product-version "v1.0 (2022)" \
--issue-type HIGH_BOUNCEПерегенерирует AI-гипотезы и решения для существующих проблем.
ux-insight/
├── analytics/ # Основное Django-приложение
│ ├── management/
│ │ └── commands/ # Management команды
│ │ ├── ingest_data.py # ETL + детекция проблем
│ │ ├── ingest_analysis.py # Детекторы UX-проблем
│ │ ├── create_funnels.py # Создание preset-воронок
│ │ ├── calculate_funnels.py # Расчет метрик воронок
│ │ ├── discover_funnels.py # Автообнаружение воронок
│ │ ├── run_analysis_only.py # Только анализ проблем
│ │ └── check_ingestion_status.py # Проверка статуса
│ ├── migrations/ # Миграции БД
│ ├── models.py # Django модели
│ ├── views.py # Главный файл views (импорты)
│ ├── views_dashboard.py # Dashboard views
│ ├── views_compare.py # Compare views
│ ├── views_issues.py # Issues views
│ ├── views_funnels.py # Funnels views (CRUD)
│ ├── views_api_extra.py # Дополнительные API
│ ├── views_helpers.py # Вспомогательные функции
│ ├── ai_service.py # Интеграция с YandexGPT
│ ├── funnel_utils.py # Утилиты для воронок
│ ├── funnel_discovery.py # Автообнаружение воронок
│ ├── forms.py # Формы для воронок
│ ├── utils.py # Общие утилиты
│ ├── urls.py # URL маршруты
│ └── goals.yaml # Конфигурация целей Метрики
├── templates/ # Django шаблоны
│ ├── base.html
│ └── analytics/
│ ├── dashboard.html
│ ├── issues.html
│ ├── compare.html
│ ├── funnels.html
│ ├── funnel_detail.html
│ ├── funnel_create.html
│ ├── funnel_edit.html
│ └── funnel_delete.html
├── config/ # Настройки Django
│ ├── settings.py
│ └── urls.py
├── docker-compose.yml # Docker Compose конфигурация
├── Dockerfile # Docker образ
├── entrypoint.sh # Entrypoint скрипт
├── requirements.txt # Python зависимости
├── .env.example # Пример переменных окружения
└── README.md # Этот файл
| Переменная | Описание | Обязательная | По умолчанию |
|---|---|---|---|
SECRET_KEY |
Django secret key | Да | - |
DEBUG |
Режим отладки | Нет | False |
DB_HOST |
Хост БД | Нет | db |
DB_PORT |
Порт БД | Нет | 5432 |
DB_NAME |
Имя БД | Нет | postgres |
DB_USER |
Пользователь БД | Нет | postgres |
DB_PASSWORD |
Пароль БД | Нет | postgres |
FOLDER_ID |
Yandex Cloud Folder ID | Нет | - |
API_KEY |
Yandex Cloud API Key | Нет | - |
AUTO_INGEST |
Автозагрузка данных | Нет | 1 |
AUTO_FUNNELS |
Автосоздание воронок | Нет | 1 |
Файл содержит конфигурацию целей Яндекс.Метрики:
- code: apply_it_button
name: 'Кнопка "Поступить" IT-магистратура'
ym_goal_id: 39566071
match: {type: identifier, value: APPLY_IT}
- code: contacts_view
name: 'просмотр контактов'
ym_goal_id: 53625076
match: {type: url_prefix, value: 'https://mai.ru/priem/contacts.php'}Типы match:
identifier- проверка черезgoalsIDв сессииurl_prefix- URL начинается с префиксаurl_contains- URL содержит строкуclick- клик по элементу (требует специальной обработки)
- VisitSession: batch_size=10000
- PageHit: batch_size=50000
- Расчет
time_on_pageчерез SQL window functions (в 10-100 раз быстрее Python) - Агрегация метрик страниц одним запросом
- Метрики воронок кэшируются в
FunnelMetrics - Разделение общих и когортных метрик для быстрого доступа
- Воронки рассчитываются отдельной командой (не блокируют основной ETL)
- AI-инсайты генерируются асинхронно
- Проверьте наличие Parquet-файлов в корне проекта
- Проверьте логи:
docker-compose logs web - Проверьте статус:
docker-compose exec web python manage.py check_ingestion_status
- Проверьте
FOLDER_IDиAPI_KEYв.env - Проверьте доступность Yandex Cloud API
- При отсутствии ключей используются заглушки (это нормально)
- Убедитесь, что воронки созданы:
create_funnels - Рассчитайте метрики:
calculate_funnels - Проверьте, что шаги воронки соответствуют реальным URL/целям в данных
docker-compose exec web python manage.py migrate --fake-initialMIT License
Проект разработан в рамках хакатона PSB "AI Challenge: Banking".
Версия документа: 1.0
Последнее обновление: 2025