EventMatch AI
Веб-сервис на Flask, который подбирает до 3 event-подрядчиков из каталога (66 профилей) по городу, дате, формату, категории и бюджету и объясняет…
О проекте
Команда: Aijok
Кейс HackAlem.ai: Креативные индустрии — Firebird
Стадия: Рабочий MVP
Задача кейса: «#79-lite»: подобрать до 3 подрядчиков для мероприятия и объяснить выбор
Суть
Веб-сервис на Flask, который подбирает до 3 event-подрядчиков из каталога (66 профилей) по городу, дате, формату, категории и бюджету и объясняет каждый выбор.
Как устроено
Из CSV-каталога последовательно отсекаются профили по городу и категории, занятости (busy_dates), формату, бюджету (price_from_kzt) и длительности (max_hours). Прошедшие получают детерминированную оценку из 4 компонентов: язык, запас бюджета, запас длительности и число форматов. Затем стабильная сортировка с tie-breaker по id и TOP-3. Объяснения собираются шаблонами из полей профиля и фрагмента description, без LLM. Для пустой выдачи различаются случаи «категории нет в городе» и «никто не прошёл условия», причём показывается статистика причин исключения.
Ключевые решения
- rule-based filtering
- deterministic scoring
- stable tie-breaker sorting
- template-based explanations
Особенности
Решение прозрачное и полностью детерминированное, без внешних API. В пустой выдаче показываются счётчики причин исключения (занятость, формат, бюджет, длительность), и один профиль может попасть в несколько из них. Синтетические профили отмечены отдельно.
Стек
Python · Flask · Jinja2 · SQLite · Werkzeug · HTML · CSS · JavaScript · pytest
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-d6f9f1c0-aijok.
README
README.mdEventMatch AI
Умный и прозрачный подбор event-подрядчиков
HackAlem AI • Case #79-lite
1. О проекте
Выбор подрядчика для мероприятия требует одновременно учесть город, дату, формат, бюджет и особенности услуги. Простого длинного каталога для этого недостаточно: заказчику всё равно приходится вручную сравнивать профили и выяснять, кто действительно доступен и подходит.
EventMatch AI не расширяет каталог, а помогает выбрать до трёх наиболее подходящих вариантов из уже существующего каталога. Главная ценность решения — прозрачное объяснение того, почему каждый конкретный подрядчик попал в рекомендации.
Сервис предназначен для заказчиков свадеб, тоев, корпоративов, конференций, юбилеев и дней рождения. Основной сценарий: пользователь задаёт параметры события, получает не более трёх прошедших обязательные условия профилей, сравнивает их оценки и объяснения «Почему подходит». Сам подбор доступен без регистрации.
2. Что реализовано
- Форма подбора с обязательными полями: город, дата мероприятия, тип мероприятия, категория подрядчика и бюджет.
- Опциональные параметры: длительность и предпочтительный язык.
- Серверная валидация допустимых города, даты, формата, категории, языка, положительного целого бюджета и длительности от 0 до 24 часов.
- Последовательная фильтрация по занятости в
busy_dates, поддерживаемому формату, бюджету и допустимой длительности. - Детерминированная оценка кандидатов, стабильная сортировка и выдача TOP-3.
- Индивидуальное, локально сформированное объяснение «Почему подходит» для каждой карточки.
- Корректная выдача одного или двух профилей, если требованиям соответствует меньше трёх кандидатов.
- Разные состояния для случаев «в этом городе такой категории нет» и «кандидаты есть, но никто не проходит условия».
- Статистика исключений: занятость, формат, бюджет и длительность. Один профиль может учитываться сразу в нескольких причинах.
- Отображение синтетических профилей с отдельной визуальной меткой.
- Демонстрационные кнопки «Написать» и «Позвонить»: они не раскрывают настоящий контакт и сообщают, что контакт скрыт в демо.
- Локальные регистрация, вход и выход из аккаунта.
- Локальный Demo AI Assistant с предусмотренными сценариями ответа, без внешнего API.
- Информационные секции «Как это работает», «О сервисе» и блок ассистента.
3. Как работает подбор
Pipeline рекомендации:
Параметры пользователя
↓
Поиск подрядчиков выбранной категории в выбранном городе
↓
Проверка занятости на выбранную дату
↓
Проверка event format
↓
Проверка бюджета
↓
Проверка длительности
↓
Детерминированный scoring
↓
Стабильная сортировка
↓
TOP-3
↓
Индивидуальное объяснение
Обязательные фильтры
- Сначала выбираются профили, у которых совпадают город и категория.
- Если выбранная дата содержится в
busy_datesподрядчика, он помечается как занятый и не может попасть в результат. - Подрядчик должен поддерживать выбранный формат мероприятия.
- Его начальная стоимость
price_from_kztне должна превышать бюджет. - Если пользователь указал длительность и у профиля заполнен
max_hours, запрос не должен превышать этот лимит. Пустойmax_hoursозначает, что часовой лимит к услуге не применяется.
Язык не является жёстким фильтром: совпадение выбранного языка влияет на оценку, но отсутствие языка в профиле само по себе не исключает кандидата.
Ранжирование
Прошедшие фильтры кандидаты получают числовую оценку из четырёх составляющих:
- совпадение языка или нейтральный балл, если язык не выбран;
- запас относительно бюджета;
- запас по длительности либо нейтральная оценка, когда ограничение неприменимо;
- наполненность профиля по числу поддерживаемых форматов.
После этого записи сортируются по убыванию оценки. При равных оценках используется стабильный tie-breaker — строковый id подрядчика по возрастанию. В алгоритме нет случайности, поэтому полностью одинаковый запрос к неизменному датасету возвращает одинаковый порядок карточек.
4. Объяснимые рекомендации
Карточка каждого результата содержит не общее рекламное сообщение, а отдельное объяснение, составленное из данных именно этого профиля. Оно может включать:
- начальную стоимость, факт попадания в бюджет и размер бюджетного запаса;
- подтверждение выбранного формата;
- выбранный язык и его наличие либо перечень языков профиля;
- запрошенную длительность и доступный лимит, если длительность задана;
- релевантный содержательный фрагмент
descriptionпрофиля.
Служебные приветствия в начале описания пропускаются, а используемый фрагмент ограничивается по длине. Объяснения строятся только на полях профиля и параметрах запроса: алгоритм не добавляет неподтверждённые награды, опыт или достижения.
Генерация объяснений локальная и детерминированная. Она реализована шаблонными правилами приложения, не обращается к внешней LLM и при одинаковом запросе формирует одинаковый текст.
5. Обработка пустой выдачи
Интерфейс различает три результата:
- Подрядчики подобраны. Отображаются до трёх карточек, общее число подходящих профилей и дата события.
- В выбранном городе нет подрядчиков этой категории. Пользователь получает отдельное сообщение и предложение изменить город или категорию.
- Подрядчики категории существуют, но не проходят условия. Показываются число проверенных локальных профилей и статистика причин исключения:
- заняты на дату;
- не поддерживают формат;
- превышают бюджет;
- не подходят по длительности.
Причины считаются независимо, поэтому один профиль может попасть сразу в несколько счётчиков. Если требованиям соответствуют только один или два кандидата, сервис показывает все найденные профили, не дополняя TOP-3 неподходящими вариантами, и явно сообщает, что это полная подходящая выдача.
6. Данные
Приложение использует предоставленный HackAlem анонимизированный CSV-датасет data/hackathon-dataset-anonymized.csv. В текущей версии в нём 66 профилей и 17 категорий.
Основные поля, используемые рекомендательной системой:
| Поле | Назначение |
|---|---|
id | Стабильный анонимный идентификатор и tie-breaker сортировки |
anon_name | Анонимизированное отображаемое имя |
categories | Одна или несколько категорий подрядчика |
city | Город профиля |
price_from_kzt | Начальная стоимость в тенге |
event_formats | Поддерживаемые форматы мероприятий |
languages | Языки профиля |
max_hours | Максимальная длительность услуги в часах |
busy_dates | Даты, в которые подрядчик занят |
description | Текст профиля для персонализированной детали объяснения |
synthetic | Признак искусственно созданного демонстрационного профиля |
busy_dates — список дат в ISO-формате, разделённых символом |; совпадение с датой запроса исключает профиль. max_hours может быть пустым для услуг, где присутствие по часам неприменимо; такой профиль не отбрасывается по длительности. Значение synthetic=true означает синтетический, а не реальный профиль. Такие записи визуально отмечаются на карточке надписью «Синтетический профиль».
7. Технологии
| Технология | Роль в проекте |
|---|---|
| Python | Серверная логика, загрузка CSV, валидация и рекомендации |
| Flask | HTTP-маршруты, формы, сессии и рендеринг страниц |
| HTML5 | Семантическая структура интерфейса и форм |
| CSS | Адаптивное визуальное оформление |
| JavaScript | Состояние отправки формы, демо-кнопки контактов и локальный ассистент |
| Jinja2 | Серверные HTML-шаблоны, карточки и состояния выдачи |
| SQLite | Локальное хранение аккаунтов |
| Werkzeug | Генерация и проверка хешей паролей |
| pytest | Автоматические тесты приложения и рекомендательной логики |
Runtime-зависимости, зафиксированные в requirements.txt, — Flask 3.1.2 и pytest 8.4.2. OpenAI API и NVIDIA API приложением не используются.
8. Архитектура
app.py— точка входа Flask, конфигурация, маршруты главной страницы, регистрации/входа и выхода.services/recommender.py— чтение CSV, модели данных, валидация, фильтрация, scoring, сортировка и объяснения.services/auth.py— схема SQLite, нормализация email/телефона, создание и проверка аккаунтов.templates/index.html— форма, результаты, пустые состояния и информационные секции.templates/auth.html— интерфейс регистрации и входа.static/style.css— стили страниц и компонентов.static/script.js— клиентские демо-взаимодействия и ответы ассистента.data/— рабочий CSV-датасет.tests/— проверки Flask-маршрутов и рекомендательного движка.instance/eventmatch.db— создаваемая при локальном запуске база аккаунтов; она не хранится в репозитории.
Поток рекомендации
Пользователь
↓
Flask (`app.py`)
↓
Валидация запроса
↓
Recommendation Engine (`services/recommender.py`) ← CSV dataset (`data/`)
↓
Фильтрация → scoring → стабильная сортировка → TOP-3 → объяснения
↓
HTML / Jinja2 (`templates/index.html`)
Поток регистрации и входа
Пользователь
↓
Flask Auth (`app.py`, `services/auth.py`)
↓
Werkzeug password hashing / проверка хеша
↓
SQLite (`instance/eventmatch.db`)
↓
Flask session
9. Регистрация и авторизация
Локальная система аккаунтов поддерживает:
- регистрацию по email или телефону, имени и паролю;
- минимальную длину пароля 8 символов;
- хранение хеша пароля через Werkzeug вместо открытого пароля;
- вход по email/телефону и паролю;
- состояние входа через Flask session;
- выход с очисткой сессии;
- хранение аккаунтов в SQLite.
Регистрация не обязательна: основной подбор подрядчиков доступен без аккаунта. База создаётся локально по адресу instance/eventmatch.db. Каталог instance/, а также файлы *.db и *.sqlite3, исключены через .gitignore и не должны попадать в Git.
10. EventMatch AI Assistant
В текущей демо-версии EventMatch AI Assistant работает локально на наборе предусмотренных сценариев и не использует внешнюю LLM/API.
Ответы заданы в JavaScript и выбираются детерминированно по тексту вопроса. Ассистент умеет отвечать на пять предусмотренных вопросов:
- как выбрать подрядчика;
- почему показано меньше трёх;
- что означает показатель совпадения;
- почему выдача изменилась после смены даты;
- что такое синтетический профиль.
Для других формулировок выводится сообщение об ограниченном демонстрационном режиме. Это справочный UI-компонент, а не генеративная AI-модель.
11. Структура проекта
EventMatch-AI/
├── app.py
├── requirements.txt
├── README.md
├── data/
│ └── hackathon-dataset-anonymized.csv
├── services/
│ ├── __init__.py
│ ├── auth.py
│ └── recommender.py
├── templates/
│ ├── auth.html
│ └── index.html
├── static/
│ ├── script.js
│ └── style.css
└── tests/
├── test_app.py
└── test_recommender.py
В репозитории также сохранены исходные материалы хакатона и предварительные файлы в HackalemAI/; рабочее приложение использует перечисленные выше корневые модули и датасет из data/.
12. Установка и запуск
Требуется установленный Python с поддержкой создания виртуального окружения.
Базовый способ
-
Скачайте или клонируйте репозиторий.
-
Перейдите в корневую папку проекта.
-
Установите зависимости:
python -m pip install -r requirements.txt -
Запустите приложение:
python app.py -
Откройте в браузере http://127.0.0.1:5000.
Рекомендуемый способ для Linux/macOS
python -m venv .venv
.venv/bin/python -m pip install -r requirements.txt
.venv/bin/python app.py
Активация окружения не обязательна. При желании его можно активировать командой source .venv/bin/activate.
Windows PowerShell без активации
PowerShell может блокировать выполнение Activate.ps1 политикой исполнения. Безопасный вариант — не менять эту политику и запускать Python окружения напрямую:
python -m venv .venv
.\.venv\Scripts\python.exe -m pip install -r requirements.txt
.\.venv\Scripts\python.exe app.py
Затем откройте http://127.0.0.1:5000.
13. Тестирование
Запуск полного набора тестов:
python -m pytest -q
Подтверждённый результат локальной проверки текущей версии: 21 passed.
Тесты проверяют:
- загрузку всех 66 профилей и 17 категорий;
- стабильный TOP-3 и наличие индивидуальных объяснений;
- выдачу одного и двух результатов;
- различие двух типов пустой выдачи и точные счётчики исключений;
- исключение занятого подрядчика и влияние даты;
- поведение пустого
max_hours; - влияние языка на ranking без превращения его в жёсткий фильтр;
- валидацию дат и ожидаемых значений бюджета;
- детерминированность и содержательность объяснений;
- читаемый формат даты без изменения типа
busy_dates; - главную страницу, отправку формы и информационные демо-секции;
- регистрацию, вход, выход и отклонение неверных учётных данных.
14. Как проверить решение жюри
Перед сценариями запустите приложение и откройте главную страницу.
Сценарий A — плотная категория
- Выберите: Алматы, дата 23.09.2026, формат свадьба, категория Банкетный зал, бюджет 10 000 000 ₸, язык русский.
- Убедитесь, что показаны три карточки и у каждой своё объяснение «Почему подходит».
Сценарий B — редкая категория
- Для запроса из сценария A установите бюджет 2 000 000 ₸ и длительность 6 часов.
- Убедитесь, что сервис показывает один подходящий профиль и сообщение о том, что это все подходящие варианты.
Дополнительная проверка двух результатов: Алматы, 24.09.2026, свадьба, Ведущий, бюджет 2 000 000 ₸, длительность 6 часов, язык русский.
Сценарий C — пустая выдача
- Проверка отсутствующей категории в городе: выберите Астана и категорию Декоратор — интерфейс должен показать отдельное состояние отсутствия категории.
- Проверка отфильтрованных кандидатов: Алматы, 24.09.2026, свадьба, Банкетный зал, бюджет 2 000 000 ₸, длительность 6 часов, язык русский. Интерфейс должен сообщить, что профили существуют, но не прошли условия, и показать причины исключения.
Сценарий D — влияние даты
- Выполните запрос из сценария A для 23.09.2026, затем повторите его для 24.09.2026.
- Сравните карточки: записи из
busy_datesновой даты исключаются, поэтому состав или порядок выдачи может измениться.
Сценарий E — детерминизм
- Дважды без изменений отправьте запрос из сценария A.
- Убедитесь, что подрядчики отображаются в одном порядке; одинаковые оценки разрешаются по стабильному
id.
15. Безопасность
- Пароли не сохраняются открытым текстом: в SQLite записывается хеш, созданный Werkzeug.
- Проверка пароля выполняется сравнением с сохранённым хешем.
- Локальная пользовательская SQLite и весь каталог
instance/исключены из Git. - Приложение не требует внешних AI API, а API-ключи в репозитории не хранятся.
- Датасет анонимизирован; реальные контакты подрядчиков не публикуются, а кнопки связи показывают только демо-сообщение.
16. Ограничения
- Сервис не выполняет реальное бронирование и не отправляет заявки подрядчикам.
- Кнопки контактов демонстрационные; реальные телефонные номера не используются и не раскрываются.
- Demo AI Assistant не использует внешнюю LLM и отвечает только по локально заданным сценариям.
- Рекомендации ограничены профилями текущего CSV-датасета.
- Допустимый календарный диапазон запроса ограничен 23.09.2026–31.12.2026, то есть диапазоном дат демо-данных.
- Регистрация и база аккаунтов локальны; интеграции с внешними провайдерами авторизации нет.
- Публичная deployed-версия в репозитории не указана; проект рассчитан на локальный запуск.
17. Возможности развития
Следующие пункты — направления будущего развития, а не функции текущей версии:
- подключение актуальных каталогов подрядчиков;
- синхронизация с реальными календарями доступности;
- безопасное открытие контактов, отправка заявок и бронирование;
- расширение географии и набора категорий;
- семантический поиск и embeddings для работы с описаниями;
- подключение LLM для более гибких, но проверяемых объяснений;
- внешняя авторизация и постоянное серверное хранилище;
- публичный deployment.
18. Использование AI при разработке
Codex использовался как AI-агент для разработки и помощи с кодом. Это инструмент процесса разработки, а не runtime-компонент сервиса.
Codex помогал разрабатывать проект, но подбор подрядчиков в текущей версии выполняется локальным детерминированным алгоритмом приложения. Рекомендательная система не вызывает Codex, OpenAI API, NVIDIA API или внешнюю LLM.
19. Deployment
Публичная deployed-версия на момент сдачи отсутствует. Проект запускается локально по инструкции выше.
20. HackAlem AI
HackAlem AI
Case #79-lite — «Умный подбор подрядчиков»
2026
Авторы коммитов
История коммитов
- 2026-09-23 16:58Merge pull request #5 from BAITC-Hacks/hbb8a6-codex/-readme.mdaliya990
- 2026-09-23 16:57Prepare EventMatch AI hackathon READMEaliya990
- 2026-09-23 16:39Merge pull request #4 from BAITC-Hacks/reci1a-codex/-eventmatch-demoaliya990
- 2026-09-23 16:39Expand EventMatch demo experiencealiya990
- 2026-09-23 16:21Expand EventMatch demo experiencealiya990
- 2026-09-23 15:51Fix CSV dataset loadingTmNaz
- 2026-09-23 15:48Merge branch 'main' into codex-nwrh85aliya990
- 2026-09-23 15:47Fix data file error message and format codeTmNaz
Все коммиты (15)
- 2026-09-23 15:46Fix numeric form input validationaliya990
- 2026-09-23 15:29Merge pull request #1 from BAITC-Hacks/codexaliya990
- 2026-09-23 15:27Add files via uploadTmNaz
- 2026-09-23 15:16Add files via uploadTmNaz
- 2026-09-23 15:15Add files via uploadTmNaz
- 2026-09-23 15:12Build deterministic EventMatch contractor appaliya990
- 2026-09-23 14:51Add files via uploadaliya990
Структура репозитория (22 файлов)
.gitignore 2026-09-23_14-52-28.png HackalemAI/HackAlem AI_ Хакатон-задача_ умный подбор подрядчиков (1).pdf HackalemAI/README.md HackalemAI/app.ру HackalemAI/hackathon-dataset-anonymized.csv HackalemAI/recommender.py HackalemAI/requirements.txt README.md app.py data/hackathon-dataset-anonymized.csv hackathon dataset preview.html requirements.txt services/__init__.py services/auth.py services/recommender.py static/script.js static/style.css templates/auth.html templates/index.html tests/test_app.py tests/test_recommender.py
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.