EventMatch
Сервис подбора до трёх event-подрядчиков из каталога 66 профилей с жёсткими фильтрами, тремя различимыми исходами (matched / no_category_in_city /…
О проекте
Команда: 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.mdEventMatch: умный подбор event-подрядчиков (#79-lite)
EventMatch помогает заказчику выбрать до трёх подрядчиков из уже имеющегося каталога своего города. Вместо расширения списка сервис проверяет ограничения заказа, показывает конкретные причины выбора каждой карточки и объясняет, почему вариантов меньше трёх или нет вовсе. В каталоге есть ведущие, фотографы, флористы, банкетные залы и другие категории.
Главный принцип: ценность в объяснении, а не в сортировке. OpenAI API при настройке ключа выбирает подтверждённые основания и формулирует объяснение для каждой выбранной карточки. Код проверяет, что текст не содержит утверждений за пределами фактов профиля и точной цитаты. Модель не может объявить занятого подрядчика свободным или добавить кандидата из другого города.
Что реализовано
- Подбор по городу, дате, типу мероприятия, категории и бюджету в тенге; язык и длительность необязательны.
- Один механизм для людей и площадок: банкетный зал проверяется по такому же календарю, как ведущий.
- Жёсткие фильтры по занятости, цене «от», формату, языку и длительности; максимум три карточки в стабильном порядке.
- Карточки с ценой, происхождением данных и основанием выбора из структурированных полей и описания подрядчика. Цитата относится именно к показанному профилю; рекламные заявления из описания не выдаются за независимую проверку.
- Три различимых исхода:
matched(подобрали),no_category_in_city(такой категории нет в городе),no_match(профили есть, но не проходят условия). Отдельно возвращается ошибка валидации некорректного запроса. - Причины исключения, число допустимых кандидатов и проверяемые подсказки по другой дате или бюджету для пустой выдачи.
- Сравнение двух дат с указанием кандидатов, исключённых из-за занятости; чат с уточнением заказа при наличии OpenAI API.
- HTTP API, локальная веб-форма, CLI, тесты, SQLite-хранилище заказов, решений OpenAI и журнала действий.
- Финальная верссия проекта находится в зип архиве под названием final-version
Как работает подбор
- Заказчик указывает город, дату, формат, категорию и бюджет. Например: ведущий в Алматы на корпоратив 12 октября 2026 года, бюджет 1 300 000 ₸, 6 часов, русский язык.
- Сервис валидирует запрос и выделяет только профили этой категории в указанном городе. Если таких профилей нет, возвращает
no_category_in_city. - Для каждого профиля проверяются календарь
busy_dates, ценаprice_from_kzt,event_formats, а при запросе языка или длительности такжеlanguagesиmax_hours. Если ни один не проходит, возвращаетсяno_matchс причинами. - Допустимые профили упорядочиваются детерминированно: в режиме по умолчанию учитывается лексическое соответствие описания формату, затем цена «от» и
id. Это не рейтинг качества подрядчика. В выдачу попадает не более трёх. - Для выбранных профилей сервис подготавливает проверенные факты и дословные фрагменты их описаний. При настроенном OpenAI API модель выбирает 1–2 факта и, при наличии содержательного фрагмента, цитату, составляет персональное объяснение; backend проверяет принадлежность источников профилю и наличие только подтверждённого текста. Непрошедший проверку ответ модели не публикуется.
- Результат содержит карточки, объяснение количества и исключений, метки
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.py | OpenAI 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.html | SQLite для диалога, результатов, кеша объяснений и аудита; локальный 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.
-
Запустите локальный сервер:
python -m eventmatch serve -
Откройте http://127.0.0.1:8000. Форма и демонстрационные кнопки работают без ключей, но в этом случае объяснения будут помечены
template explanation fallback, а свободный текст в чате недоступен. Остановка сервера:Ctrl+C; другой порт:python -m eventmatch serve --port 8080. -
Чтобы 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-версию нет. Для демонстрации запустите локальный сервер по инструкции выше. Ссылка на исходный репозиторий не является ссылкой на работающий деплой.
Дополнительно: анализ данных, архитектура, сценарий демонстрации, настройка провайдеров.
Авторы коммитов
История коммитов
- 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 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.