solo leveling
Локальный MVP на чистом Python: подбор до трёх event-подрядчиков из CSV организаторов с объяснением из двух предложений и цитатой из описания профиля.
О проекте
Команда: solo leveling
Кейс HackAlem.ai: Креативные индустрии — Firebird
Стадия: Рабочий MVP
Задача кейса: «#79-lite»: подобрать до 3 подрядчиков для мероприятия и объяснить выбор
Суть
Локальный MVP на чистом Python: подбор до трёх event-подрядчиков из CSV организаторов с объяснением из двух предложений и цитатой из описания профиля. Есть веб-форма и JSON API.
Как устроено
catalog.py читает CSV и типизирует поля в памяти. matcher.py последовательно исключает занятых на дату, неподходящих по бюджету, формату, часам и языку, записывая первую сработавшую причину. Оставшимся выбирается фрагмент их собственного описания и начисляется лексический балл: совпадение слов пожелания и формата, редкость слов в каталоге, небольшой вес за экономию бюджета. Сортировка стабильная по ID. Всё работает без LLM, эмбеддингов и внешних API.
Ключевые решения
- последовательная фильтрация с причиной отсева
- лексическое ранжирование с весом редкости слов
- цитирование фрагмента описания
- стабильная сортировка
Особенности
Ноль внешних зависимостей. Даты вне известного окна календаря отклоняются, чтобы отсутствие записи о занятости не приняли за свободный день. Синтетические профили и восстановленные город и цена помечаются на карточках. SHA-256 подтверждает, что исходный CSV не изменён.
Стек
Python (stdlib http.server) · HTML · unittest · Render
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-cf27094c-solo-leveling.
README
README.mdПодрядчик под не ваше событие — HackAlem AI #79-lite #67676767
Локальный MVP для подбора до трёх event-подрядчиков из каталога по городу, дате, формату, категории и бюджету. Дополнительно можно указать длительность, язык и пожелание. Каждая карточка объясняет совпадение условий и приводит конкретный фрагмент описания профиля. Бронирования и заявок в MVP нет.
Что реализовано
- Фильтрация по городу и категории, затем по календарю занятости, бюджету, формату, длительности и языку. Занятый подрядчик никогда не попадает в результат.
- До трёх карточек с именем, категориями, городом, ценой «от» и объяснением из двух предложений. Для площадок используется тот же каталог и календарь.
- Разные сообщения для трёх исходов: рекомендации найдены; в городе нет такой категории; категория есть, но все кандидаты исключены условиями. Если прошло меньше трёх, показано почему.
- В объяснении присутствует дата доступности и цитируемый фрагмент описания. Интерфейс показывает признаки синтетических профилей и восстановленных города/цены.
- Повтор запроса сохраняет порядок: при равном балле применяется стабильная сортировка по ID.
- Четыре кнопки для демонстрации, включая повтор заявки на другую дату.
Запуск
Нужен Python 3.10+. Зависимостей вне стандартной библиотеки нет. В каталоге проекта:
python app.py
Откройте http://127.0.0.1:8000. Для другого порта: python app.py --port 8080. По умолчанию сервер доступен только на локальном компьютере (127.0.0.1). Остановить: Ctrl+C.
Публичный адрес в облаке (Render)
Для демонстрации без локального адреса загрузите этот Git-репозиторий в репозиторий команды BAITC-Hacks/hack-cf27094c-solo-leveling, затем создайте на Render New → Web Service из него. В архиве origin уже настроен. Из распакованной папки проекта выполните git remote -v, проверьте адрес и после авторизации в GitHub выполните git fetch origin. Если удалённый репозиторий пуст, отправьте пять коммитов командой git push -u origin main. Если там уже есть коммиты, сначала просмотрите и объедините изменения; не используйте --force для ветки команды. Затем укажите в Render:
| Настройка | Значение |
|---|---|
| Language | Python 3 |
| Build Command | python -m py_compile app.py catalog.py matcher.py |
| Start Command | python app.py |
Render задаёт переменную PORT. Приложение автоматически слушает этот порт на 0.0.0.0. Когда развёртывание завершится, откройте выданную Render ссылку вида https://имя-сервиса.onrender.com/ и передайте её жюри. Локальный компьютер после этого не нужен. Эта инструкция подготовлена для развёртывания, но сама публичная версия пока не опубликована: для неё требуется ваш репозиторий и учётная запись облачного сервиса.
Проверки:
python -m unittest discover -s tests -v
Как проверить в интерфейсе
| Кнопка | Параметры | Ожидаемый исход |
|---|---|---|
| Ведущие · осень | Алматы, 15.10.2026, свадьба, ведущий, 1 500 000 ₸, пожелание «юмор» | Три карточки; часть профилей исключена по дате и формату |
| Флористы · редкая категория | Алматы, 14.11.2026, свадьба, флорист, 500 000 ₸ | Одна карточка; объяснение, почему меньше трёх |
| Нет результата | Астана, 31.12.2026, свадьба, флорист, 500 000 ₸ | Профиль есть, но он занят на выбранную дату |
| Та же заявка · другая дата | Первая заявка, но 16.10.2026 | Состав и число карточек меняются; указаны исключения по занятости |
Чтобы увидеть другой тип пустого результата, выберите Астана → Декоратор: этой категории в городе нет.
Доступен и JSON API:
curl -G 'http://127.0.0.1:8000/api/recommend' \
--data-urlencode 'city=Алматы' \
--data-urlencode 'event_date=2026-10-15' \
--data-urlencode 'event_format=свадьба' \
--data-urlencode 'category=Ведущий' \
--data-urlencode 'budget=1500000' \
--data-urlencode 'wishes=юмор'
GET /api/options возвращает допустимые значения списков и границы календаря. Ошибочные параметры GET /api/recommend дают HTTP 400 с текстом ошибки. Параметры hours, language, wishes необязательны.
Как устроен подбор
catalog.pyчитает исходный CSV, переводит списки через разделитель|в наборы значений, а цену и часы — в числа. Исходные записи вdata/contractors.csvне изменяются.matcher.pyпроверяет запрос и последовательно исключает занятых, неподходящих по бюджету, формату, часам или языку. Каждый профиль учитывается в одной причине исключения — первой сработавшей. Отсутствиеmax_hoursозначает работу, не привязанную к пребыванию на площадке, и не блокирует запрос по часам.- Для оставшихся профилей выбирается фрагмент их собственного описания. Короткие фрагменты оцениваются по совпадению слов пожелания/формата и редкости слов в каталоге; небольшой вес получает экономия относительно бюджета. Выдача сортируется по баллу, затем по ID. Это прозрачное лексическое ранжирование, без LLM, эмбеддингов и внешних API. Цитируемый фрагмент вместе с проверенными условиями формирует объяснение; модель не придумывает факты.
app.pyотдаёт HTML и JSON через стандартный HTTP-сервер Python.static/index.htmlпоказывает форму, результаты и причины исключения. Данные в интерфейсе вставляются как текст, а не как HTML.
Данные и ограничения
- Происхождение файла: приложенный
hackathon dataset anonymized .csvскопирован в проект какdata/contractors.csvбез изменения содержимого. SHA-256 обоих файлов:6a724b6b7dfb5973343e68ba18dadb60fc807d87e3d78f03ee86fb26cb089f7d. Переименование сделано только для короткого пути в коде. - Идентификаторы не менялись: значения столбца
idи имена изanon_nameсохранены как в CSV. Приложение не создаёт новые ID, не объединяет и не удаляет профили. В JSON-ответе возвращается исходныйid; он также используется для стабильного порядка при равном балле. - Преобразования только в памяти при чтении: поля
categories,event_formats,languages,busy_datesразделяются по|;price_from_kztи заполненныйmax_hoursпревращаются в числа; пустойmax_hoursстановитсяNone;synthetic,city_imputed,price_imputedчитаются как логические признаки. Эти преобразования не перезаписывают CSV. Краевые пробелы вcityиdescriptionубираются для работы приложения. - Используются 66 анонимизированных профилей из приложенного организаторами CSV, в том числе 13 синтетических. Дополнительных профилей MVP не создаёт.
synthetic,city_imputedиprice_imputedвидны на карточках, когда соответствующий флаг установлен. - Календарь известен только с 23.09.2026 по 31.12.2026. Даты за пределами этого окна отклоняются, чтобы отсутствие записи о занятости не трактовалось как свободное время.
- Цена в данных указана «от»; соответствие бюджету означает только, что начальная цена не превышает бюджет. Финальная смета и фактическая доступность требуют подтверждения подрядчиком.
- Пожелание сопоставляется по словам с описанием. Синонимы и смысловые перефразирования без общих слов этот MVP может не распознать. Факты в исходных описаниях не проверяются независимо.
- Нет авторизации, постоянного хранения запросов, бронирования и опубликованной версии. Для публичной эксплуатации сверх хакатонной демонстрации понадобятся настройки защиты и эксплуатации.
Структура и история
app.py HTTP-сервер и API
catalog.py чтение и типизация CSV
matcher.py фильтрация, ранжирование, объяснения
static/index.html веб-интерфейс
data/contractors.csv исходный датасет
tests/test_matcher.py сценарии и API-проверки
Авторы коммитов
История коммитов
- 2026-09-23 16:21If i cant afford that bag that u wantAIZ
- 2026-09-23 15:57Document setup, demo cases, architecture, and limitsIsnkshwiz
- 2026-09-23 15:44Verify demo, hard constraints, and API behaviorIsnkshwiz
- 2026-09-23 15:33Expose matching API and interactive demo interfaceIsnkshwiz
- 2026-09-23 15:15Filter availability and rank grounded recommendationsIsnkshwiz
- 2026-09-23 15:13Load and validate the supplied contractor catalogIsnkshwiz
- 2026-09-23 14:59but im tryna fix it for uIsnkshwiz
- 2026-09-23 13:29I got commitment issuesIsnkshwiz
Структура репозитория (8 файлов)
.gitignore README.md app.py catalog.py data/contractors.csv matcher.py static/index.html tests/test_matcher.py
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.