AAYMA
RU
Войти

EventMatch

Сервис подбора до трёх event-подрядчиков из каталога 66 профилей с жёсткими фильтрами, тремя различимыми исходами (matched / no_category_in_city /…

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

О проекте

Команда: Slafuw
Кейс HackAlem.ai: Креативные индустрии — Firebird
Стадия: Рабочий MVP

Задача кейса: «#79-lite»: подобрать до 3 подрядчиков для мероприятия и объяснить выбор

Суть

Сервис подбора до трёх event-подрядчиков из каталога 66 профилей с жёсткими фильтрами, тремя различимыми исходами (matched / no_category_in_city / no_match), проверяемыми объяснениями через OpenAI, сравнением двух дат, чатом, HTTP API, CLI и веб-формой.

Как устроено

Запрос валидируется, выбираются профили категории в городе, затем фильтры по busy_dates, цене «от», формату, языку и max_hours; допустимые упорядочиваются детерминированно (лексическое соответствие, цена, id), берётся top-3. OpenAI Responses API с function calling (gpt-4.1-mini) выбирает 1–2 подтверждённых факта и цитату для объяснения, backend отклоняет чужие ID, цитаты и неподтверждённые слова; при сбое помеченный шаблонный fallback. Опционально семантическое ранжирование на заранее подготовленных эмбеддингах NVIDIA; SQLite хранит диалоги, кэш объяснений и аудит, есть интеграция с Google Calendar.

Ключевые решения

  • жёсткая фильтрация
  • детерминированный top-3
  • function calling
  • валидация объяснений по фактам
  • семантическое ранжирование эмбеддингами
  • SHA-256 контроль каталога
  • подсказки по дате/бюджету

Особенности

Работает на полном каталоге организаторов (66 профилей, 17 категорий) только на стандартной библиотеке Python. В конце много коммитов с удалением исходников, финальная версия лежит в final-version.zip; README сам предупреждает, что .env не исключён из Git.

Стек

Python 3.11 · http.server · SQLite · unittest · HTML/CSS/JS · Google Calendar API

Модели

gpt-4.1-mini-2025-04-14 · nvidia/llama-3.2-nv-embedqa-1b-v2


Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-9e488d0f-slafuw.

README

README.md

EventMatch: умный подбор event-подрядчиков (#79-lite)

EventMatch помогает заказчику выбрать до трёх подрядчиков из уже имеющегося каталога своего города. Вместо расширения списка сервис проверяет ограничения заказа, показывает конкретные причины выбора каждой карточки и объясняет, почему вариантов меньше трёх или нет вовсе. В каталоге есть ведущие, фотографы, флористы, банкетные залы и другие категории.

Главный принцип: ценность в объяснении, а не в сортировке. OpenAI API при настройке ключа выбирает подтверждённые основания и формулирует объяснение для каждой выбранной карточки. Код проверяет, что текст не содержит утверждений за пределами фактов профиля и точной цитаты. Модель не может объявить занятого подрядчика свободным или добавить кандидата из другого города.

Что реализовано

  • Подбор по городу, дате, типу мероприятия, категории и бюджету в тенге; язык и длительность необязательны.
  • Один механизм для людей и площадок: банкетный зал проверяется по такому же календарю, как ведущий.
  • Жёсткие фильтры по занятости, цене «от», формату, языку и длительности; максимум три карточки в стабильном порядке.
  • Карточки с ценой, происхождением данных и основанием выбора из структурированных полей и описания подрядчика. Цитата относится именно к показанному профилю; рекламные заявления из описания не выдаются за независимую проверку.
  • Три различимых исхода: matched (подобрали), no_category_in_city (такой категории нет в городе), no_match (профили есть, но не проходят условия). Отдельно возвращается ошибка валидации некорректного запроса.
  • Причины исключения, число допустимых кандидатов и проверяемые подсказки по другой дате или бюджету для пустой выдачи.
  • Сравнение двух дат с указанием кандидатов, исключённых из-за занятости; чат с уточнением заказа при наличии OpenAI API.
  • HTTP API, локальная веб-форма, CLI, тесты, SQLite-хранилище заказов, решений OpenAI и журнала действий.
  • Финальная верссия проекта находится в зип архиве под названием final-version

Как работает подбор

  1. Заказчик указывает город, дату, формат, категорию и бюджет. Например: ведущий в Алматы на корпоратив 12 октября 2026 года, бюджет 1 300 000 ₸, 6 часов, русский язык.
  2. Сервис валидирует запрос и выделяет только профили этой категории в указанном городе. Если таких профилей нет, возвращает no_category_in_city.
  3. Для каждого профиля проверяются календарь busy_dates, цена price_from_kzt, event_formats, а при запросе языка или длительности также languages и max_hours. Если ни один не проходит, возвращается no_match с причинами.
  4. Допустимые профили упорядочиваются детерминированно: в режиме по умолчанию учитывается лексическое соответствие описания формату, затем цена «от» и id. Это не рейтинг качества подрядчика. В выдачу попадает не более трёх.
  5. Для выбранных профилей сервис подготавливает проверенные факты и дословные фрагменты их описаний. При настроенном OpenAI API модель выбирает 1–2 факта и, при наличии содержательного фрагмента, цитату, составляет персональное объяснение; backend проверяет принадлежность источников профилю и наличие только подтверждённого текста. Непрошедший проверку ответ модели не публикуется.
  6. Результат содержит карточки, объяснение количества и исключений, метки synthetic, city_imputed, price_imputed, сведения о режиме работы. Подтверждённое решение OpenAI сохраняется: повторный запрос с той же версией данных, моделью и условиями использует тот же текст. Без ключа или при сбое API доступен явно помеченный шаблонный fallback.

max_hours=null означает, что ограничение времени присутствия неприменимо, например для флориста; это не обещание неограниченной работы. Цена «от» является нижней границей, а не окончательной сметой.

Технологии и архитектура

КомпонентНазначение
Python 3.11+, стандартная библиотекаБизнес-логика, локальный HTTP-сервер (http.server), CLI, валидация, тесты (unittest), SQLite (sqlite3). Для основного сценария pip install не требуется.
eventmatch/models.py, catalog.pyСхемы запроса и профиля, загрузка JSONL, контроль целостности по SHA-256.
eventmatch/engine.py, service.pyФильтрация и устойчивый порядок, диагностика отказов, подготовка фактов и проверка объяснений.
eventmatch/agent.py, providers.pyOpenAI Responses API с function calling: разбор чата и написание проверяемых объяснений по уже выбранным профилям. Модель по умолчанию: gpt-4.1-mini-2025-04-14; задаётся через OPENAI_MODEL.
eventmatch/semantic.pyНеобязательное семантическое ранжирование на предварительно подготовленном артефакте NVIDIA (nvidia/llama-3.2-nv-embedqa-1b-v2); онлайн-вызовов NVIDIA при подборе нет. Без артефакта работает детерминированный baseline.
eventmatch/storage.py, server.py, web/index.htmlSQLite для диалога, результатов, кеша объяснений и аудита; локальный HTTP API и адаптивная страница на обычных HTML/CSS/JavaScript без фронтенд-фреймворка.
eventmatch/integrations/google_calendar.pyНеобязательное создание и удаление событий в личном Google Calendar через OAuth; не бронирование подрядчика. Для подбора не используется.

Пайплайн: форма или чат -> валидация -> каталог города/категории -> жёсткие фильтры -> устойчивый top-3 -> OpenAI для объяснений (либо помеченный fallback) -> проверка источников -> ответ и журнал SQLite. OpenAI получает только выбранные карточки с разрешёнными фактами и цитатами, а не все 66 профилей. Неправильный id, чужая цитата и неподтверждённые слова отклоняются. Режим виден в metadata.explanation_mode; записи вызовов доступны в actions.

Установка и запуск

Команды ниже выполняются из корня репозитория, где лежат eventmatch/ и examples/. Нужен Python 3.11+. Основной подбор не требует установки пакетов, Docker, NVIDIA или ключей API.

  1. Запустите локальный сервер:

    python -m eventmatch serve
    
  2. Откройте http://127.0.0.1:8000. Форма и демонстрационные кнопки работают без ключей, но в этом случае объяснения будут помечены template explanation fallback, а свободный текст в чате недоступен. Остановка сервера: Ctrl+C; другой порт: python -m eventmatch serve --port 8080.

  3. Чтобы OpenAI API действительно писал объяснения, задайте серверу OPENAI_API_KEY. Например, в bash перед запуском:

    export OPENAI_API_KEY="<ваш ключ>"
    python -m eventmatch serve
    

    Альтернатива: локальный .env в корне проекта со строкой OPENAI_API_KEY=... (загрузчик читает его только для команд serve и agent). Ключ не передаётся в браузер; .env сейчас не исключён из Git, не добавляйте его в коммиты. OPENAI_MODEL необязателен. В metadata.explanation_mode ищите validated OpenAI grounded explanation или cached validated OpenAI explanation, а в metadata.openai и actions проверяйте фактический режим вызова. Наличие ключа само по себе не доказывает, что API ответил успешно.

Для локальной проверки без браузера:

python -X utf8 -m eventmatch recommend --request examples/01-dense.json  # ядро без OpenAI
python -X utf8 -m eventmatch agent --request examples/01-dense.json      # агент с OpenAI при наличии ключа
python -X utf8 -m tools.demo                                               # семь запросов, пересчёт на месте

-X utf8 полезен для отображения кириллицы на Windows. Для опционального режима NVIDIA потребуются NVIDIA_API_KEY, подготовка python -X utf8 -m tools.prepare_semantics --output data/nvidia-artifact.json и EVENTMATCH_RANKER=nvidia перед запуском; детали в инструкции по провайдерам. Готового артефакта в репозитории нет; отсутствие или повреждение артефакта при EVENTMATCH_RANKER=nvidia останавливает запуск вместо незаметной подмены результата.

Как проверить на защите

Откройте страницу и нажмите следующие кнопки над формой: это живые запросы, а не показ сохранённых ответов.

СценарийЧто проверить
«Ведущий · 12 окт.»Алматы, корпоратив, 1 300 000 ₸, 6 ч, русский: 7 допустимых из 10 в городе/категории; показаны 3 карточки с различающимися цитатами и ценами.
«Та же заявка · 13 окт.»Состав top-3 меняется: Сон Гоку был занят 12 октября и попадает в выдачу 13 октября; причины исключения на каждую дату видны в сводке.
«Флорист · редкая категория»Показаны ровно 2 профиля из 2; видны метки исходного и синтетического профилей и объяснение, почему третьего нет.
«Заняты на дату»no_match на 12 декабря: профили в городе и категории есть, но все кандидаты, проходящие остальные условия, заняты. Сервис объясняет пустую выдачу.
«Нет такой категории»no_category_in_city для категории «Декоратор» в Астане, без подстановки подрядчиков из Алматы.
«Банкетный зал»Проверяется календарь площадки на 14 ноября, применяются те же правила, что для ведущих.

Сопоставьте ответ при повторном запуске одного запроса. Если задан ключ OpenAI, проверяйте именно режим объяснения: при сетевом сбое ответ всё равно может быть получен в шаблонном режиме. Для прямой проверки API во время работы сервера:

curl -sS http://127.0.0.1:8000/recommend \
  -H 'Content-Type: application/json' \
  --data-binary @examples/01-dense.json

Коды исходов matched, no_match, no_category_in_city возвращаются с HTTP 200; ошибка в параметрах запроса (например, дата вне календаря) даёт HTTP 422. Полный контракт: docs/API.md.

Данные и внешние сервисы

  • Канонический каталог: data/raw/hackathon-dataset-anonymized.jsonl, 66 профилей в 17 категориях: 53 с synthetic=false, 13 с synthetic=true. Команда не добавляла новых профилей. Данные преобразованы из CSV организаторов; шаги и хеши указаны в манифесте. Имена анонимизированы.
  • Календарь дат в каталоге: 23.09.2026–31.12.2026 включительно (100 дней). Сервис не знает доступность за его пределами и отклоняет такие даты; повышенная занятость в декабре является свойством данных.
  • OpenAI Responses API (https://api.openai.com/v1/responses) вызывается только при наличии ключа для написания объяснений и обработки чата. В этом окружении ключ не предоставлен, поэтому живую генерацию здесь подтвердить нельзя; тесты OpenAI используют тестовые ответы.
  • NVIDIA Embeddings API (https://integrate.api.nvidia.com/v1/embeddings) необязателен и используется только при подготовке локального семантического артефакта. В обычном запуске дополнительных сетевых запросов для ранжирования нет.
  • Google Calendar API является отдельной необязательной интеграцией. Для него нужны пакеты google-api-python-client, google-auth, google-auth-oauthlib, локальный credentials.json и OAuth-авторизация (token.json создаётся локально); они не входят в pyproject.toml и не нужны для сценария рекомендации. Кнопка добавляет событие в календарь пользователя, а не бронирует подрядчика.

Ограничения и состояние проверок

  • Состав top-3 и порядок определяет backend; OpenAI не выбирает подрядчиков. Смысловые рекомендации зависят от качества описаний; цитата доказывает лишь наличие фразы в профиле, а не истинность рекламного заявления. Лексические эвристики не дают полноценного понимания отрицаний и сложных связей.
  • Цена «от» не гарантирует итоговую стоимость; сервис не бронирует, не отправляет заявки и не уведомляет подрядчиков. Нет рейтингов, подтверждённой вместимости каждой площадки и расписания по часам.
  • Локальный сервер слушает 127.0.0.1; публичный деплой и эксплуатационные гарантии времени ответа не настроены. Запрос к OpenAI ограничен общим deadline 10 секунд и может перейти на помеченный fallback.
  • Команда python -X utf8 -m tools.demo проверена на текущем каталоге. Часть сохранённых ответов examples/responses/ устарела относительно текущего движка; tools.check_repository и полный unittest discover сейчас не проходят (в том числе из-за устаревших проверок разметки и Google Calendar). Не используйте снимки как доказательство текущего ответа: запускайте реальные запросы. Проверка агента и основных HTTP-исходов: python -X utf8 -m unittest tests.test_agent tests.test_http.HTTPTest.test_real_request tests.test_http.HTTPTest.test_valid_empty_result_uses_200 -q.
  • Безопасность перед публикацией: в Git отслеживается credentials.json.json с OAuth client secret. Этот файл не используется подбором; перед передачей репозитория третьим лицам секрет нужно отозвать/перевыпустить у провайдера и удалить из истории репозитория. Новый credentials.json и token.json не следует публиковать.

Публичная версия

Подтверждённой ссылки на deployed-версию нет. Для демонстрации запустите локальный сервер по инструкции выше. Ссылка на исходный репозиторий не является ссылкой на работающий деплой.

Дополнительно: анализ данных, архитектура, сценарий демонстрации, настройка провайдеров.

Звёзды
⭐ 0
Форки
0
Язык
Python
Последний push
в прошлом месяце
Коммиты
102
Авторы
3
Файлы
58
Ветки
3

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

coffinko 88kyou 7Graftrikst 7

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

  • 2026-09-23 17:38Добавить README.md с описанием проекта EventMatchkyou
  • 2026-09-23 17:36Add files via uploadGraftrikst
  • 2026-09-23 17:34Delete 01-dense.jsoncoffinko
  • 2026-09-23 17:34Delete 02-other-date (4).jsoncoffinko
  • 2026-09-23 17:34Delete 02-other-date.jsoncoffinko
  • 2026-09-23 17:34Delete 03-rare (5).jsoncoffinko
  • 2026-09-23 17:34Delete 03-rare.jsoncoffinko
  • 2026-09-23 17:34Delete 04-busy (6).jsoncoffinko
Все коммиты (102)
  • 2026-09-23 17:34Delete 04-busy.jsoncoffinko
  • 2026-09-23 17:33Delete 05-no-category (7).jsoncoffinko
  • 2026-09-23 17:33Delete 05-no-category.jsoncoffinko
  • 2026-09-23 17:33Delete 06-venue (8).jsoncoffinko
  • 2026-09-23 17:33Delete 06-venue.jsoncoffinko
  • 2026-09-23 17:33Delete 07-budget (9).jsoncoffinko
  • 2026-09-23 17:33Delete 07-budget.jsoncoffinko
  • 2026-09-23 17:33Delete API.mdcoffinko
  • 2026-09-23 17:33Delete ARCHITECTURE.mdcoffinko
  • 2026-09-23 17:32Delete DATA_AUDIT.mdcoffinko
  • 2026-09-23 17:32Delete DECISIONS.mdcoffinko
  • 2026-09-23 17:32Delete DEMO.mdcoffinko
  • 2026-09-23 17:32Delete EVALUATION.mdcoffinko
  • 2026-09-23 17:32Delete IMPLEMENTATION_PLAN.mdcoffinko
  • 2026-09-23 17:32Delete PROVIDERS.mdcoffinko
  • 2026-09-23 17:32Delete README (1).mdcoffinko
  • 2026-09-23 17:31Delete README.mdcoffinko
  • 2026-09-23 17:31Delete SOLUTION.mdcoffinko
  • 2026-09-23 17:31Delete __init__ (11).pycoffinko
  • 2026-09-23 17:31Delete __init__ (2).pycoffinko
  • 2026-09-23 17:31Delete __init__.cpython-312 (12).pyccoffinko
  • 2026-09-23 17:30Delete __init__.cpython-312.pyccoffinko
  • 2026-09-23 17:30Delete __init__.pycoffinko
  • 2026-09-23 17:30Delete __main__.cpython-312.pyccoffinko
  • 2026-09-23 17:30Delete __main__.pycoffinko
  • 2026-09-23 17:30Delete agent.cpython-312.pyccoffinko
  • 2026-09-23 17:30Delete agent.pycoffinko
  • 2026-09-23 17:30Delete audit.cpython-312.pyccoffinko
  • 2026-09-23 17:30Delete audit.jsoncoffinko
  • 2026-09-23 17:29Delete audit.pycoffinko
  • 2026-09-23 17:29Delete benchmark.cpython-312.pyccoffinko
  • 2026-09-23 17:29Delete benchmark.jsoncoffinko
  • 2026-09-23 17:29Delete benchmark.pycoffinko
  • 2026-09-23 17:29Delete catalog.cpython-312.pyccoffinko
  • 2026-09-23 17:29Delete catalog.pycoffinko
  • 2026-09-23 17:29Delete check_repository.cpython-312.pyccoffinko
  • 2026-09-23 17:29Delete check_repository.pycoffinko
  • 2026-09-23 17:28Delete check_web.cpython-312.pyccoffinko
  • 2026-09-23 17:28Delete check_web.pycoffinko
  • 2026-09-23 17:28Delete config.cpython-312.pyccoffinko
  • 2026-09-23 17:28Delete config.pycoffinko
  • 2026-09-23 17:28Delete credentials.json.jsoncoffinko
  • 2026-09-23 17:28Delete demo.cpython-312.pyccoffinko
  • 2026-09-23 17:28Delete demo.pycoffinko
  • 2026-09-23 17:27Delete downloadcoffinko
  • 2026-09-23 17:27Delete engine.cpython-312.pyccoffinko
  • 2026-09-23 17:27Delete engine.pycoffinko
  • 2026-09-23 17:27Delete eventmatch.sqlite3coffinko
  • 2026-09-23 17:27Delete final wrsioncoffinko
  • 2026-09-23 17:27Delete google_calendar.pycoffinko
  • 2026-09-23 17:27Delete hackathon-dataset-anonymized.jsonlcoffinko
  • 2026-09-23 17:27Delete import_dataset.cpython-312.pyccoffinko
  • 2026-09-23 17:26Delete import_dataset.pycoffinko
  • 2026-09-23 17:26Delete index (13).htmlcoffinko
  • 2026-09-23 17:26Delete index.htmlcoffinko
  • 2026-09-23 17:26Delete manifest.jsoncoffinko
  • 2026-09-23 17:26Delete providers.pycoffinko
  • 2026-09-23 17:26Delete semantic.pycoffinko
  • 2026-09-23 17:26Delete server.pycoffinko
  • 2026-09-23 17:25Delete models.cpython-312.pyccoffinko
  • 2026-09-23 17:24Delete models.pycoffinko
  • 2026-09-23 17:24Delete prepare_semantics.pycoffinko
  • 2026-09-23 17:24Delete providers.cpython-312.pyccoffinko
  • 2026-09-23 17:24Delete pyproject.tomlcoffinko
  • 2026-09-23 17:23Delete semantic.cpython-312.pyccoffinko
  • 2026-09-23 17:23Delete service.pycoffinko
  • 2026-09-23 17:23Delete server.cpython-312.pyccoffinko
  • 2026-09-23 17:23Delete service.cpython-312.pyccoffinko
  • 2026-09-23 17:22Delete storage.cpython-312.pyccoffinko
  • 2026-09-23 17:22Delete storage.pycoffinko
  • 2026-09-23 17:22Delete test_agent.cpython-312.pyccoffinko
  • 2026-09-23 17:22Delete test_agent.pycoffinko
  • 2026-09-23 17:21Delete test_http.cpython-312.pyccoffinko
  • 2026-09-23 17:21Delete test_http (10).pycoffinko
  • 2026-09-23 17:21Delete test_http.pycoffinko
  • 2026-09-23 17:21Create final wrsionGraftrikst
  • 2026-09-23 17:21Delete test_recommendations.cpython-312.pyccoffinko
  • 2026-09-23 17:20Delete test_recommendations.pycoffinko
  • 2026-09-23 17:19Delete 01-dense (3).jsonGraftrikst
  • 2026-09-23 17:10Add files via uploadGraftrikst
  • 2026-09-23 16:52Add Google Calendar event deletioncoffinko
  • 2026-09-23 16:41Update README.mdkyou
  • 2026-09-23 15:51Add Google Calendar button to contractor cardscoffinko
  • 2026-09-23 15:46Update POLICY_VERSION and improve evidence handlingkyou
  • 2026-09-23 15:45Update service.pykyou
  • 2026-09-23 15:42Restore Google Calendar integrationcoffinko
  • 2026-09-23 15:26Add files via uploadGraftrikst
  • 2026-09-23 15:11Add Google Calendar integrationcoffinko
  • 2026-09-23 14:46Add grounded OpenAI agent and reproducible NVIDIA semantic pipelineGraftrikst
  • 2026-09-23 14:37Add files via uploadcoffinko
  • 2026-09-23 14:36Enhance test for static UI and metadatakyou
  • 2026-09-23 14:35Refactor HTML structure and styles for EventMatckyou
  • 2026-09-23 14:33Add files via uploadkyou
  • 2026-09-23 13:52Add files via uploadGraftrikst
Структура репозитория (58 файлов)
.gitignore
README.md
data/README.md
data/eventmatch.sqlite3
data/manifest.json
data/raw/hackathon-dataset-anonymized.jsonl
data/reports/audit.json
data/reports/benchmark.json
docs/API.md
docs/ARCHITECTURE.md
docs/DATA_AUDIT.md
docs/DECISIONS.md
docs/DEMO.md
docs/EVALUATION.md
docs/IMPLEMENTATION_PLAN.md
docs/PROVIDERS.md
docs/SOLUTION.md
eventmatch/__init__.py
eventmatch/__main__.py
eventmatch/agent.py
eventmatch/catalog.py
eventmatch/config.py
eventmatch/engine.py
eventmatch/integrations/__init__.py
eventmatch/integrations/google_calendar.py
eventmatch/models.py
eventmatch/providers.py
eventmatch/semantic.py
eventmatch/server.py
eventmatch/service.py
eventmatch/storage.py
examples/01-dense.json
examples/02-other-date.json
examples/03-rare.json
examples/04-busy.json
examples/05-no-category.json
examples/06-venue.json
examples/07-budget.json
examples/responses/01-dense.json
examples/responses/02-other-date.json
examples/responses/03-rare.json
examples/responses/04-busy.json
examples/responses/05-no-category.json
examples/responses/06-venue.json
examples/responses/07-budget.json
final-version.zip
tests/test_agent.py
tests/test_http.py
tests/test_recommendations.py
tools/__init__.py
tools/audit.py
tools/benchmark.py
tools/check_repository.py
tools/check_web.py
tools/demo.py
tools/import_dataset.py
tools/prepare_semantics.py
web/index.html
Ветки (3)

Обсуждение 0 комментариев

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

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