AAYMA
RU
Войти

solo leveling

Локальный MVP на чистом Python: подбор до трёх event-подрядчиков из CSV организаторов с объяснением из двух предложений и цитатой из описания профиля.

Рабочий MVP
Команда «solo leveling» 1💬 0 комментариевс 23 сентября 2026 г.

О проекте

Команда: 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:

НастройкаЗначение
LanguagePython 3
Build Commandpython -m py_compile app.py catalog.py matcher.py
Start Commandpython 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 необязательны.

Как устроен подбор

  1. catalog.py читает исходный CSV, переводит списки через разделитель | в наборы значений, а цену и часы — в числа. Исходные записи в data/contractors.csv не изменяются.
  2. matcher.py проверяет запрос и последовательно исключает занятых, неподходящих по бюджету, формату, часам или языку. Каждый профиль учитывается в одной причине исключения — первой сработавшей. Отсутствие max_hours означает работу, не привязанную к пребыванию на площадке, и не блокирует запрос по часам.
  3. Для оставшихся профилей выбирается фрагмент их собственного описания. Короткие фрагменты оцениваются по совпадению слов пожелания/формата и редкости слов в каталоге; небольшой вес получает экономия относительно бюджета. Выдача сортируется по баллу, затем по ID. Это прозрачное лексическое ранжирование, без LLM, эмбеддингов и внешних API. Цитируемый фрагмент вместе с проверенными условиями формирует объяснение; модель не придумывает факты.
  4. 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-проверки
Звёзды
⭐ 1
Форки
0
Язык
Python
Последний push
в прошлом месяце
Коммиты
8
Авторы
1
Файлы
8
Ветки
1

Авторы коммитов

AIZ 8

История коммитов

  • 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 комментариев

Войдите, чтобы комментировать

Пока нет комментариев — будьте первым.