Гибридный ML-сервис для кейса Авито на IT Purple Hack 2026: система определяет, когда объявление о ремонте стоит разделить на отдельные услуги, и при необходимости генерирует черновики новых объявлений.
Проект занял 2 место в своём треке. В репозитории лежит рабочее решение: FastAPI API, демо-интерфейс, скрипты оценки, данные, обученная модель и архив исследовательских экспериментов.
На вход подаётся объявление из широкой категории Ремонт квартир и домов под ключ. На выходе система возвращает:
detectedMcIds— какие дополнительные микрокатегории найдены в тексте.shouldSplit— нужно ли разделять объявление.splitMcIds— какие микрокатегории стоит вынести в отдельные объявления.drafts— опциональные тексты черновиков, сгенерированные через OpenRouter.
Ключевое решение принимается локально и быстро: LLM используется только как копирайтер после того, как ML-модель уже решила, что сплит нужен.
-
Rule-based детектор микрокатегорий Регулярные выражения находят упоминания самостоятельных услуг: сантехника, электрика, плитка, обои, полы, гипсокартон, демонтаж и другие работы.
-
ML-модель для решения о сплите CatBoost-классификатор использует TF-IDF признаки текста и ручные meta-features: длину объявления, количество найденных категорий, плотность услуг, маркеры списков, цены, слова про комплексный ремонт и слова про отдельные работы.
-
Опциональный LLM-копирайтер Если задан
OPENROUTER_API_KEYи включена генерация черновиков, сервис отправляет запрос в OpenRouter и получает текст объявления в стиле автора. Без API-ключа проект всё равно полностью работает и возвращает локальный fallback-текст.
.
├── solution.py # Pipeline: детектор, ML-инференс, опциональная генерация
├── detector.py # Отдельный детектор микрокатегорий
├── api/
│ ├── api.py # FastAPI приложение
│ └── static/index.html # Демо-интерфейс
├── model/
│ └── split_detector.pkl # Предобученная модель для быстрого запуска
├── tests/ # Контрактные тесты API и pipeline
├── eval_speed_metrics.py # Локальная оценка без LLM-вызовов
├── run_test_dataset.py # Пакетный прогон rnc_test.csv
├── rnc_dataset_markup.json # Обучающая/оценочная выборка
├── rnc_mic_key_phrases.csv # Словарь микрокатегорий
├── rnc_test.csv # Тестовая выборка
└── experiments/ # Архив R&D-экспериментов
Если файла model/split_detector.pkl нет, Pipeline обучит модель заново на rnc_dataset_markup.json и сохранит её локально.
Нужен Python 3.10+.
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtЗапустить тесты:
python -m pytest tests -qПроверить скорость и метрики без LLM:
python eval_speed_metrics.pyЗапустить web-демо:
uvicorn api.api:app --reload --port 8000Открыть в браузере: http://127.0.0.1:8000.
Генерация черновиков по умолчанию выключена в API-запросах и в пакетном прогоне датасета. Чтобы включить OpenRouter-копирайтер:
cp .env.example .env
# Заполните .env, затем загрузите переменные в текущую shell-сессию:
set -a
source .env
set +aЛокальный ML-путь не требует API-ключа.
curl -X POST http://127.0.0.1:8000/analyze \
-H "Content-Type: application/json" \
-d '{
"itemId": 42,
"mcId": 101,
"mcTitle": "Ремонт квартир и домов под ключ",
"description": "Делаем ремонт под ключ. Отдельно выполняем электрику, сантехнику и укладку плитки.",
"use_ai_drafts": false
}'Быстрый локальный прогон без черновиков:
python run_test_dataset.py --limit 20Полный прогон с опциональной генерацией черновиков:
python run_test_dataset.py --generate-draftsПо умолчанию результат сохраняется в predictions_test.json. Другой путь можно указать через --output path.json.
На включённом датасете eval_speed_metrics.py обрабатывает 2480 объявлений примерно за 5 секунд на локальном ноутбуке и работает без LLM-вызовов. Исторический результат на хакатонном hold-out: F1 = 0.70, Precision = 0.81 для класса Split.
- Production path намеренно гибридный: сначала детерминированная детекция и ML-классификация, затем LLM-копирайтинг.
- Реальные API-ключи не хранятся в репозитории. OpenRouter настраивается через переменные окружения.
experiments/оставлен как R&D-архив: он показывает путь исследования, но рабочий сценарий проекта —solution.py+api/api.py.