AAYMA
RU
Войти

Firebird Match

Подбор до 3 event-подрядчиков с объяснениями.

Доведённый продукт
Команда «goyim» 6💬 0 комментариевс 23 сентября 2026 г.

О проекте

Команда: goyim
Кейс HackAlem.ai: Креативные индустрии — Firebird
Стадия: Доведённый продукт

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

Суть

Подбор до 3 event-подрядчиков с объяснениями. Есть подсказки по соседним датам и минимальному бюджету, сравнение карточек и AI-разбор текстового описания мероприятия в параметры формы.

Как устроено

matcher.py строго проверяет занятость, бюджет, формат, язык и длительность, затем ранжирует прошедших по TF-IDF-сходству с пожеланиями, потом по цене и ID. Статусы выдачи: matched, no_category, no_matches. При пустом результате ищутся соседние даты в пределах ±7 дней и минимальный бюджет, который дал бы хотя бы один вариант. event_parser через OpenAI Structured Outputs со строгой JSON Schema извлекает город, дату, формат, категорию и бюджет из свободного текста, результат дополнительно валидируется на сервере. Также по желанию OpenAI выбирает факт, на котором строится объяснение.

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

  • TF-IDF ranking
  • rule-based filtering
  • OpenAI Structured Outputs
  • strict JSON Schema validation
  • nearby-date and budget recovery suggestions

Особенности

Для пустой выдачи есть проверенные подсказки: «искать с бюджетом …» и «проверить соседнюю дату», которые меняют только один параметр. Работает без внешних пакетов. AI только заполняет форму и не влияет на фильтры и ранжирование. Отдельно разобрано различие «способ участия» и «тип мероприятия».

Стек

Python · HTML · CSS · JavaScript · Node.js test

Модели

OpenAI


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

README

README.md

Firebird Match

Подбор до трёх event-подрядчиков с конкретным объяснением каждого выбора. Решение кейса «Умный подбор подрядчиков» (#79-lite), HackAlem AI, трек «Креативные индустрии».

Заказчик указывает условия мероприятия и получает короткую подборку из каталога. Каждая карточка объясняет совпадение по условиям и показывает характерный факт из описания подрядчика.

Для жюри: базовое демо запускается без ключей и установки пакетов. После запуска нажмите «Ведущий на корпоратив», «Флорист на свадьбу» и «Маленький бюджет»: это три обязательных сценария кейса. Подробный маршрут демонстрации и порядок сдачи — в docs/DEMO.md.

Быстрый запуск

Нужен Python 3.10 или новее. Сторонние библиотеки, npm, база данных и API-ключ для базового режима не нужны.

Получить исходники

Репозиторий команды закрытый: для скачивания нужен доступ команды или организаторов. Базовая версия уже объединена в main. Для проверки новых подсказок и сравнения до их интеграции используйте ветку:

git clone --branch main https://github.com/BAITC-Hacks/hack-d79ff3ed-goyim.git
cd hack-d79ff3ed-goyim

Без Git: откройте эту ветку в GitHub → Code → Download ZIP, распакуйте архив и откройте терминал в папке с app.py. После слияния PR интегратором исходники доступны также в main. Старый вложенный Firebird-Match.zip удалён: актуальным источником являются файлы репозитория, а не архив другой версии.

Запустить

python app.py

Откройте http://127.0.0.1:8000. Остановка сервера — Ctrl+C.

На Windows можно дважды нажать start.bat или выполнить:

py -3 app.py --open

На macOS и Linux:

python3 app.py --open

Если порт занят: python app.py --port 8001. Все команды выполняются из папки проекта, где лежит app.py. Если Python не установлен, установите Python 3.10+ с python.org.

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

  • Обязательные поля: город, дата, формат, категория, бюджет в тенге.
  • Дополнительные поля: язык, длительность, текст пожеланий до 600 символов.
  • Строгая проверка занятости, бюджета, формата, языка и длительности до ранжирования.
  • Не более трёх карточек: имя, категории, город, цена «от», объяснение и описание источника.
  • Фиксированный порядок карточек при одинаковом запросе и каталоге.
  • Раздельные состояния: matched, no_category, no_matches.
  • Объяснение короткой выдачи и пустого результата с количествами причин отказа.
  • Сравнение с предыдущим запросом при смене только даты: кто стал занят, кто появился или уступил место в первой тройке.
  • Предложения соседних дат в пределах ±7 дней, если на них больше вариантов при тех же условиях.
  • Подсказка минимального бюджета, если изменение только бюджета даст хотя бы один вариант.
  • Применение проверенных подсказок бюджета и даты одной кнопкой с сохранением остальных условий.
  • Таблица сравнения двух–трёх выбранных карточек по цене, соответствию, форматам, языкам, длительности, цитате об опыте и причинам выбора.
  • Явные отметки синтетических профилей, проставленных городов и цен.
  • Адаптивный интерфейс на русском и пять демонстрационных запросов.
  • AI-помощник «Опишите мероприятие своими словами»: извлекает пять параметров, заполняет редактируемую форму и сразу запускает подбор, если обязательные поля заполнены и корректны.
  • Процент соответствия проверяемым параметрам запроса на каждой карточке; текстовая оценка используется только для порядка результатов.
  • Опциональный OpenAI API для выбора факта, на котором акцентируется объяснение.
  • Автоматические проверки фильтров, календарей, порядка, пустых результатов, API и отказоустойчивости AI.

Бронирование, отправка заявок и сообщения подрядчикам не выполняются.

Пользовательский сценарий

  1. Открыть приложение. Сразу загружается пример с ведущими в Алматы.
  2. Изменить город, дату, формат, категорию и бюджет. При необходимости раскрыть дополнительные условия.
  3. Нажать «Подобрать подрядчиков».
  4. Сравнить до трёх карточек, объяснения и цитаты из профилей.
  5. Посмотреть причины исключения других кандидатов или сравнить соседнюю дату.

Пожелания влияют только на порядок прошедших проверку профилей. Приложение не снимает ограничения автоматически.

Что делать, если никто не подошёл

При no_matches экран сообщает «По вашим условиям подрядчиков не найдено» и показывает причины отказа. Если увеличение только бюджета даёт результат, показаны минимальная доплата и новый бюджет. Кнопка «Искать с бюджетом …» меняет только бюджет и выполняет обычный поиск. Если есть подходящие варианты в пределах соседних семи дней, показаны дата, количество подрядчиков и кнопка «Проверить эту дату». Остальные условия сохраняются. Если форма изменена после получения подсказки, приложение просит повторить подбор, чтобы не применить устаревшее предложение. Без подтверждённых альтернатив предлагается изменить параметры вручную. Способ участия (онлайн/офлайн/гибрид) не предлагается как решение; каталог его не проверяет. no_category по-прежнему отдельно сообщает об отсутствии категории в городе.

Сравнение подрядчиков

Отметьте «Сравнить» на двух или трёх карточках и нажмите «Сравнить выбранных». Таблица показывает начальную цену, существующий процент соответствия, типы мероприятий, языки, длительность, опыт из описания и объяснение рекомендации. Рейтинг качества и отзывы не добавляются. experience_excerpt — первый буквальный фрагмент с упоминанием опыта или стажа из уже подготовленных фрагментов профиля; он может содержать отрицание. Это утверждение автора описания, не независимое подтверждение. Возраст и даты не превращаются в годы опыта; без такого фрагмента показано «Не указано». Кнопка «Очистить сравнение» снимает все отметки. Новый поиск (в том числе AI) очищает сравнение, даже если в выдаче повторятся прежние ID. Чекбоксы и кнопки работают с клавиатуры; таблица имеет заголовки строк и столбцов. На узком экране прокручивается сама таблица, а не вся страница. Сравнение работает в браузере по уже полученным карточкам: дополнительных сетевых запросов и вызовов AI нет.

Соответствие обязательным требованиям

Сверено с документом организаторов «Хакатон-задача: умный подбор подрядчиков (#79-lite)».

ТребованиеРеализация и воспроизводимая проверка
Город, дата, тип мероприятия, категория, бюджет; опционально язык и часыОбычная форма и серверная валидация Matcher.validate; AI только помогает заполнить её
До трёх карточек с конкретными объяснениямиMatcher.recommend возвращает максимум три; в каждой цена, проверенные условия и буквальная цитата из профиля
Занятые исключены, у площадок тот же календарьbusy_dates проверяется до ранжирования; примеры со сменой даты и залом на 14 ноября
Меньше трёх — показать реальное число и объяснитьПример «Флорист на свадьбу»: два профиля; показаны число кандидатов и причины исключений
Один запрос — один порядокФиксированная TF-IDF-оценка, затем цена и ID; проверяются повторный запрос и перестановка строк CSV
Три различимых исходаmatched, no_category, no_matches; у каждого отдельный заголовок и понятное сообщение

Definition of Done проверяется через живую страницу, а не только тесты: плотная категория, редкая категория, пустой результат, повтор запроса и смена даты. Время ответа API отображается в интерфейсе. Локальный режим не обращается к внешним сервисам; ожидание помощника объяснений ограничено 4 секундами, парсера — 5 секундами. Общая задержка AI-сценария зависит от сети и проверяется отдельно.

Официальные веса: работоспособность — 25, техническая реализация — 25, README и воспроизводимость — 25, ценность — 15, развитие и оригинальность — 10. Это критерии жюри, а не присвоенная проекту оценка.

AI-заполнение параметров мероприятия

Над обычными фильтрами находится поле «Опишите мероприятие своими словами» и кнопка «Заполнить параметры с AI». Новый помощник только извлекает требования: city, date, format, category, budget. AI не выбирает подрядчиков и не рассчитывает оценки. Существующий фильтр и TF-IDF-ранжирование в matcher.py не изменены. Это отдельная функция от ранее реализованного необязательного выбора цитат для объяснений.

Пример:

Нужен фотограф для офлайн-свадьбы в Астане 5 октября, бюджет до 200 000 ₸.

Ожидаемые поля: Астана, 2026-10-05, офлайн, Фотограф, 200000. Год без явного указания берётся из показанного пользователю контекста календаря — 2026. Отсутствующие параметры имеют значение null и становятся пустыми полями формы. Прежние значения не подставляются вместо них. Слишком неопределённое описание, неверные типы, лишние поля, несуществующие даты и повреждённый JSON дают русское сообщение об ошибке. Structured Outputs использует строгую JSON Schema; сервер дополнительно проверяет типы без преобразования строк в числа, лишние/пропущенные ключи, дубликаты ключей и границы значений. Новых зависимостей нет.

Различие форматов. В исходном приложении event_format — тип мероприятия: свадьба, корпоратив и т. д. В примере format — способ участия. Значения офлайн/очная, онлайн/удалённая, гибрид/hybrid нормализуются. Если указан способ участия, он показывается в отдельном редактируемом поле с предупреждением, что каталог его не проверяет. Тип события локально определяется из однозначного слова в описании: «офлайн-свадьба» заполняет существующий фильтр значением «свадьба». Если тип не указан или неоднозначен, его нужно выбрать вручную. Способ участия в /api/recommend не передаётся.

Кнопка «Заполнить параметры с AI» заполняет поля и автоматически запускает обычный подбор — второй клик не нужен. Если обязательных параметров не хватает или форма невалидна, поиск не запускается: помощник подсказывает, что нужно дополнить. Поля остаются редактируемыми. После исправлений можно повторить поиск кнопкой «Подобрать подрядчиков». Если пользователь меняет описание или фильтры во время обработки, поздний AI-ответ не перезаписывает его изменения. Повторные клики во время обработки не создают дополнительные запросы.

Для парсера нужен OPENAI_API_KEY в локальном окружении серверного процесса. OPENAI_API_KEY= уже указан пустым в .env.example. Используется существующая модель по умолчанию gpt-4.1-mini-2025-04-14; максимум 250 выходных токенов, один вызов без повторов, ожидание не более 5 секунд. При отсутствии ключа или сбое API показывается:

AI-помощник сейчас недоступен. Заполните параметры вручную.

Существующие фильтры и ручной поиск продолжают работать. Ошибка AI не очищает уже заполненную форму. В новый API-запрос уходят только текст текущего описания, календарный контекст и допустимые названия значений полей. Каталог подрядчиков, исходный код и история запросов не передаются. Не вводите контакты, персональные данные и секреты. Частые шаблоны ключей, контактов, идентификаторов и кода блокируются локально до вызова API; эта эвристика не является универсальным классификатором конфиденциальных данных.

Ручная проверка:

  1. Настройте новый, не опубликованный ключ локально, как описано ниже, и запустите python app.py.
  2. Введите пример выше, нажмите кнопку AI и убедитесь, что заполнены Астана, 05.10.2026, свадьба, Фотограф и 200000, а формат участия — офлайн. Поиск выполняется автоматически; сводка результата должна соответствовать заполненной форме.
  3. Измените бюджет, затем нажмите «Подобрать подрядчиков» и проверьте обычную выдачу.
  4. Попробуйте «Нужен фотограф»: остальные поля должны остаться пустыми, появится просьба дополнить параметры, новый поиск не запустится.
  5. Удалите ключ из окружения (и .env, если используется), перезапустите приложение. Кнопка AI должна показать сообщение о недоступности, а ручной поиск — работать.

Как работает решение

1. Загрузка данных

data/contractors.csv — точная копия предоставленного организаторами CSV, переименованная для удобного пути. В файле 66 профилей, 17 категорий; категории могут пересекаться у одного профиля. Города: Алматы — 50 профилей, Астана — 15, Зарубежье — 1. 13 профилей помечены организаторами как синтетические; своих профилей мы не добавляли.

При финальной сверке data/contractors.csv побайтово совпал с предоставленным файлом hackathon dataset anonymized (1).csv. SHA-256: 6a724b6b7dfb5973343e68ba18dadb60fc807d87e3d78f03ee86fb26cb089f7d. Каталог используется только в закрытом репозитории команды и локальном демо; публичное распространение данных не выполняется.

Поля со списками разделяются символом |, логические поля преобразуются из True/False, цены становятся числами. При загрузке проверяются уникальность ID и границы дат занятости.

2. Проверка условий

Сначала выбираются город и категория. Если таких профилей нет, возвращается no_category. Затем для каждого профиля проверяются:

  • дата отсутствует в busy_dates;
  • price_from_kzt <= budget;
  • формат входит в event_formats;
  • запрошенный язык входит в languages, если язык задан;
  • запрошенная длительность не больше max_hours, если оба значения заданы.

Пустое max_hours означает отсутствие привязки к часам присутствия, а не нулевую длительность. Банкетные залы и другие площадки проходят тот же календарный фильтр, что и люди. Даты вне 23.09.2026–31.12.2026 отклоняются: занятость за пределами календаря неизвестна. За основу фильтров берутся структурированные поля; текст описания не отменяет ограничения.

Причины отказа считаются независимо: один профиль может одновременно не пройти по бюджету и дате. Сумма причин поэтому может быть больше количества исключённых профилей.

3. Детерминированное ранжирование

Используется небольшая лексическая TF-IDF эвристика, реализованная средствами Python:

  1. Слова приводятся к нижнему регистру, ё заменяется на е, удаляются короткие слова и стоп-слова.
  2. Сохраняются первые 5 символов слова как грубое приближение общей основы. Это не полноценная морфология.
  3. Считается TF-IDF: tf * (log((1 + N) / (1 + df)) + 1), затем L2-нормализация и косинусная близость.
  4. Для формата используется фиксированный словарь связанных слов в FORMAT_TERMS (например, корпоратив → деловой, бизнес, форум, компания, тимбилдинг).
  5. Без пожеланий оценка равна сходству с форматом. С пожеланиями: 0.75 * сходство с пожеланиями + 0.25 * сходство с форматом.
  6. Сортировка: оценка, округлённая до 8 знаков, по убыванию; начальная цена по возрастанию; ID по возрастанию.

Локальная текстовая оценка определяет только порядок и не показывается пользователю как процент. Это не вероятность успеха и не рейтинг качества подрядчика. Модель не участвует в расчёте порядка, поэтому её недоступность и вариативность не меняют выдачу.

В карточках отображается «Соответствие параметрам: … %». Оно считается как доля выполненных проверяемых условий: город, категория, свободная дата, формат и бюджет, а также язык и длительность, если пользователь их задал. В выдачу допускаются только профили, прошедшие все условия, поэтому показанные карточки имеют 100%. Рядом указано, сколько условий проверено, например «7 из 7». Поля parameter_match, matched_parameters и total_parameters не участвуют в ранжировании.

4. Объяснения без добавления новых фактов

Первое предложение собирается из проверенных полей: дата, формат, цена и бюджет, при наличии — язык и длительность. Второе показывает фрагмент описания, явно отмеченный как цитата из профиля. Длинные фрагменты обрезаются с многоточием. Фрагменты выбираются по близости к запросу и простым признакам конкретики; приветствия получают штраф.

Если подключён OpenAI, модель выбирает только индекс существующего фрагмента для каждого из уже выбранных профилей. Она не генерирует произвольные утверждения, не добавляет подрядчиков и не меняет порядок. Сервер проверяет ID, число результатов, отсутствие дублей и границы индексов. При любой ошибке сохраняются локальные объяснения и показывается уведомление.

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

КомпонентРеализация
Сервер и APIPython 3.10+, стандартный http.server.ThreadingHTTPServer
ДанныеCSV, стандартный модуль csv, хранение в памяти
РанжированиеЛокальная TF-IDF эвристика, без обучения модели
ИнтерфейсHTML, CSS, JavaScript, Fetch API
Опциональный AIOpenAI Responses API, Structured Outputs
Модель по умолчаниюgpt-4.1-mini-2025-04-14, заменяется через OPENAI_MODEL
ПроверкиPython unittest, JavaScript node:test и node:vm; без сторонних тестовых библиотек

Браузер отправляет JSON в POST /api/recommend. matcher.py проверяет параметры, фильтрует, сортирует и формирует объяснения. ai.py при наличии ключа уточняет выбор цитат. app.py возвращает JSON, а web/app.js отображает карточки и причины отказа. comparison.py после отбора добавляет выбранным карточкам список форматов и цитату об опыте. Сам алгоритм в matcher.py остаётся неизменным. Новый маршрут POST /api/parse-event вызывает event_parser.py, а web/event-parser.js переводит извлечённые значения в существующие поля формы. Контракт описан в docs/CONTRACT.md.

Структура:

app.py                 HTTP-сервер, валидация HTTP-запросов, загрузка .env
matcher.py             каталог, фильтры, ранжирование, объяснения
ai.py                  необязательный выбор фрагментов через OpenAI
event_parser.py        извлечение пяти полей и строгая проверка JSON
comparison.py          поля сравнения уже выбранных карточек, без изменения рейтинга
data/contractors.csv    исходные 66 профилей
web/                   интерфейс и значок приложения
tests/test_matcher.py   автоматические проверки
tests/test_event_parser.py  проверки парсера, API и ручного fallback
tests/test_event_form.cjs   проверки заполнения формы (Node.js 18+)
tests/test_app.cjs          обработчики интерфейса, автопоиск, проценты
tests/test_comparison.py    происхождение полей сравнения и отсутствие выдуманных данных
docs/CONTRACT.md        контракт AI-заполнения и совместимость форматов
docs/DEMO.md            демонстрация, ответы жюри и сдача
docs/BRIEF.md           границы задачи, критерии и ветка интеграции
docs/REPOSITORY_STARTER_README.md  сохранённый исходный README организаторов
.env.example           пример конфигурации без ключа
start.bat              запуск на Windows

Подключение OpenAI — необязательно

  1. Установить OPENAI_API_KEY в локальной переменной окружения серверного процесса через настройки терминала/IDE. Не публиковать значение в чатах, README и командах с сохраняемой историей.
  2. Либо скопировать .env.example в .env и вписать ключ локально: существующий загрузчик приложения перенесёт его в окружение процесса.
  3. Перезапустить python app.py. Обе AI-функции читают ключ через os.environ; клиент его не получает.

Ключ читается только сервером, не передаётся браузеру и исключён из Git через .gitignore. Переменные окружения имеют приоритет над .env. Содержимое .env не отдаётся HTTP-сервером. Не загружайте .env вручную через интерфейс GitHub: при таком способе .gitignore не защищает файл.

На Windows (командная строка CMD), если .env ещё не создан:

if not exist .env copy .env.example .env
notepad .env

Впишите новый ключ после OPENAI_API_KEY= только в открывшемся локальном файле, сохраните и перезапустите сервер. Значение OPENAI_MODEL можно оставить как в примере. Ключи, опубликованные в чате или репозитории, необходимо отозвать и заменить.

Существующий помощник объяснений отправляет параметры заказа и фрагменты описаний максимум трёх анонимизированных профилей. Новый парсер отправляет только описание мероприятия и перечисления допустимых значений полей. Перед отправкой пожеланий и фрагментов в помощник объяснений применяется та же проверка узнаваемых контактов, ключей и кода, что у парсера. При обнаружении такого текста запрос в OpenAI не выполняется, а обычный подбор и локальные объяснения сохраняются. Это ограниченная эвристика, не гарантия обнаружения любых персональных данных. В запросе указан store: false. Запросы к OpenAI расходуют API-кредиты. Сервер ждёт AI не более 4 секунд и затем возвращает локальный результат. Повторные успешные ответы кэшируются в памяти до 128 запросов. Кэш сбрасывается при перезапуске. Порядок карточек стабилен и без кэша, но выбор цитаты AI после перезапуска может отличаться.

Формат интеграции сверялся с официальной документацией: Structured Outputs, GPT-4.1 mini.

В этой сборке живой запрос к OpenAI не проверен: ключ не настроен в окружении разработки. Опубликованные в чате ключи не использовались. Успех, неправильный ответ и ошибка сети проверены с подставными ответами в тестах. В локальном режиме приложение работает без сети. В интерфейсе этот режим не обозначается как результат AI.

Как проверить жюри

В примерах язык и длительность не заданы, пожелания пустые.

СценарийПараметрыОжидаемый результат
Плотная категорияАлматы, 15.10.2026, корпоратив, Ведущий, 1 000 000 ₸6 проходят, показаны 3: Буллма, Кики, Мицури Канроджи
Изменение датыТот же запрос на 16.10.20264 проходят, показаны Буллма, Куррапика, Мицури Канроджи; Кики занят
Редкая категорияАлматы, 15.10.2026, свадьба, Флорист, 500 000 ₸2 карточки; объяснено, что в городе всего 2 профиля категории
Не проходит бюджетАлматы, 15.10.2026, корпоратив, Ведущий, 10 000 ₸no_matches, причины отказа и предложение бюджета
Нет категорииАстана, 15.10.2026, корпоратив, Декоратор, 3 000 000 ₸no_category, предложение сменить город
ПлощадкаАлматы, 14.11.2026, свадьба, Банкетный зал, 6 000 000 ₸2 карточки: Иноскэ Хашибира и Гохан; занятые исключены

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

Проверка новых функций:

  1. «Маленький бюджет»: предложение увеличить бюджет на 640 000 ₸, с 10 000 до 650 000 ₸. Нажмите кнопку: при остальных прежних условиях появится Мицури Канроджи.
  2. Алматы, 12.12.2026, корпоратив, Ведущий, 1 000 000 ₸, без дополнительных условий: результатов нет. Предложены 11 декабря (один кандидат), 13 декабря (два) и 10 декабря (два). Примените дату и проверьте сохранение остальных параметров.
  3. «Ведущий на корпоратив»: отметьте Буллму и Кики, откройте таблицу, затем добавьте Мицури Канроджи. У Буллмы показана цитата «Ведущий мероприятий с опытом работы более 10 лет.», у двух других — «Не указано».
  4. Очистите сравнение и повторите выбор. Запустите новый ручной или AI-поиск: выбор и таблица очищаются.
  5. Измените поле формы после пустого результата и нажмите старую подсказку: приложение предложит повторить поиск, не перезаписывая ваши правки.

Автоматические проверки:

python -m unittest discover -s tests -v
node --test tests/test_event_form.cjs tests/test_app.cjs
node --check web/app.js
node --check web/event-parser.js

На момент проверки проходят 37 тестов Python и 19 тестов JavaScript. Node.js 18+ нужен только для JS-проверок, запуск приложения остаётся на Python без дополнительных пакетов. Среди проверок: пять обязательных сценариев AI-парсера (полный ответ, null-поля, повреждённый ответ, отсутствие ключа, сбой API), совместимость форматов и сохранение ручного поиска. JS-проверки запускают обработчики приложения с минимальным DOM: автоматический поиск ровно один раз, неполные/невалидные параметры, повреждённый ответ, сбой API, защита от позднего ответа после редактирования, повторный ручной поиск и отображение процентов. Дополнительно проверяются минимальный достаточный бюджет, исключение занятых/неподходящих кандидатов из подсказки, сохранение параметров при смене даты, отсутствие придуманных альтернатив, сравнение двух/трёх уникальных ID и очистка после нового поиска. Исходные 16 тестов подбора сохранены, включая проверку всех возвращаемых карточек на 15 датах для ведущих, флористов и банкетных залов с заданными языком и длительностью. Добавлены две проверки, что распознаваемые чувствительные пожелания и фрагменты профилей не уходят во внешний AI. Локальное ядро на контрольном запросе выполнялось за единицы миллисекунд в среде разработки; это не гарантия времени на любом устройстве и не замер живого OpenAI API. Дополнительно проверен реальный запуск python3 app.py --port 8766: загрузка страницы и ресурсов, все шесть сценариев таблицы через HTTP, повторяемость порядка, исключение занятого профиля и ручной поиск после отказа AI без ключа. Шесть локальных HTTP-запросов заняли от 1,35 до 7,28 мс каждый; измерение не включает внешний OpenAI. Визуальную проверку в браузере в среде подготовки выполнить не удалось. Перед защитой откройте страницу у себя и пройдите кнопки примеров.

API

  • GET /api/health — состояние сервера и количество профилей.
  • GET /api/catalog — допустимые значения полей, календарь и наличие конфигурации AI (без ключа).
  • POST /api/recommend — подбор. Заголовок Content-Type: application/json обязателен.
  • POST /api/parse-event — { "text": "описание" } → { "fields": { "city": ..., "date": ..., "format": ..., "category": ..., "budget": ... } }; HTTP 422 при ошибке разбора, HTTP 503 при недоступности AI.

Пример тела запроса:

{
  "city": "Алматы",
  "date": "2026-10-15",
  "event_format": "корпоратив",
  "category": "Ведущий",
  "budget": 1000000,
  "language": null,
  "hours": null,
  "brief": ""
}

Ответ включает status, cards, counts, rejections, alternative_dates, suggested_min_budget, explanation_mode и elapsed_ms. В cards добавлены event_formats: string[], experience_excerpt: string | null, а также parameter_match, matched_parameters и total_parameters. Они относятся только к возвращённым профилям. Процент отражает выполненные проверяемые параметры; эти поля не влияют на отбор или сортировку. Маршрут и входные параметры не изменены. Неверные параметры дают HTTP 400 с объяснением; пустая корректная выдача — HTTP 200 с соответствующим статусом.

Если что-то не работает

СимптомДействие
app.py не найденОткройте терминал в распакованной папке, где лежит app.py
Команда py/python не найденаУстановите Python 3.10+ и заново откройте терминал
Порт занятЗапустите python app.py --port 8001, откройте http://127.0.0.1:8001
В браузере старая формаУбедитесь, что запущена актуальная ветка, перезапустите сервер и обновите страницу
AI недоступенПроверьте локальный ключ и разрешённый доступ к OpenAI; используйте ручную форму. При проблеме с сетью обратитесь к техническому специалисту организаторов
Нет результатов на декабрьский выходнойВ датасете высокая загрузка декабря; проверьте причины отказа и предложенные даты

Использование AI-агента

Код, интерфейс, автоматические проверки и документация подготовлены с помощью Codex. Отдельная работа AI внутри продукта необязательна и включается API-ключом. NVIDIA, дообучение, внешние базы и эмбеддинги в текущей версии не используются.

Практическая ценность и развитие

Пользователь сравнивает максимум три объяснимых варианта вместо длинного каталога. Условия проверяются одинаково для людей и площадок; пустой результат превращается в конкретную причину и, когда возможно, подсказку даты или бюджета. Необязательный AI снижает число ручных действий, а отделение AI от отбора сохраняет работу при сетевом сбое и позволяет повторить результат.

Дальнейшие шаги, пока не реализованные: оценить качество объяснений на размеченных запросах event-менеджеров; сравнить TF-IDF с готовыми русскоязычными эмбеддингами; подключить актуальные календари при разрешении владельцев данных. До масштабирования нужно измерить качество и стоимость на большем каталоге, а для публичного сервиса добавить аутентификацию и лимиты запросов. Эти возможности не заявляются как готовые.

Ограничения

  • Только предоставленные 66 профилей и календарь на 100 дней; изменения реальной занятости не синхронизируются.
  • Начальная цена не является подтверждённой сметой. Занятость проверяется по датасету, а не по живому календарю подрядчика.
  • 8 городов и 18 цен проставлены при подготовке датасета; это показано в карточках, но фильтры всё равно используют эти значения.
  • Содержимое описаний — заявления из каталога, а не независимо проверенные достижения. Имена вымышлены.
  • Лексическое ранжирование ограничено совпадением основ слов и небольшим словарём форматов, не понимает все синонимы и отрицания. Пожелания не считаются строгими условиями.
  • Если описание противоречит структурированным полям, фильтры опираются на поля, а цитата отображается как текст источника.
  • Нет регистрации, реальных контактов, бронирования, оплаты и сохранения истории между перезапусками страницы.
  • Стандартный HTTP-сервер предназначен для локального демо, а не публичного промышленного размещения. Нет аутентификации или ограничений расходов на API для публичного сервиса.
  • Реальный вызов OpenAI требует ключа, сети, доступной модели и кредитов; при сбое остаётся локальный режим.
  • Способ участия (офлайн/онлайн/гибрид) отсутствует в исходной модели каталога и не влияет на фильтрацию. AI может ошибиться в интерпретации текста: результат всегда требует проверки в форме.

Репозиторий и развёртывание

Целевой репозиторий команды: https://github.com/BAITC-Hacks/hack-d79ff3ed-goyim

Публичная deployed-версия не создавалась. Демонстрация запускается локально по инструкции выше. Ветка для сдачи — main. PR №1–3 объединены; AI-парсер, подсказки при пустой выдаче, сравнение и процент соответствия параметрам находятся в main.

После слияния проверьте main, откройте страницу треков → «Креативные индустрии» → «Умный подбор подрядчиков» → «Сдать решение», заполните название и описание. Инструкция и готовое описание находятся в docs/DEMO.md. Отправка в GitHub не заменяет сдачу на платформе. Точный дедлайн и часовой пояс нужно подтвердить у организаторов; здесь они не угадываются.

Короткий сценарий защиты

  1. Показать первый пример и объяснить разные цитаты у трёх карточек.
  2. Повторить запрос: порядок не изменился.
  3. Сравнить следующий день: Кики занят, появился Куррапика.
  4. Показать флористов: всего два, включая явно помеченный синтетический профиль.
  5. Показать маленький бюджет и отсутствующую категорию: разные причины пустого результата.
  6. Объяснить архитектуру: строгие фильтры → фиксированное ранжирование → фактические объяснения, AI выбирает цитату и не управляет ограничениями.
Звёзды
⭐ 0
Форки
0
Язык
Python
Последний push
в прошлом месяце
Коммиты
14
Авторы
1
Файлы
24
Ветки
5

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

dreamybu3 14

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

  • 2026-09-23 17:40Merge pull request #4 from BAITC-Hacks/codex/finalize-submission-docsdreamybu3
  • 2026-09-23 17:38docs: finalize submission instructionsdreamybu3
  • 2026-09-23 17:29Merge pull request #3 from BAITC-Hacks/codex/parameter-match-percentagedreamybu3
  • 2026-09-23 17:12fix: show verified parameter match percentagedreamybu3
  • 2026-09-23 16:40Merge pull request #2 from BAITC-Hacks/codex/recovery-comparisondreamybu3
  • 2026-09-23 16:36feat: compare selected contractors using verified catalogue fieldsdreamybu3
  • 2026-09-23 16:35feat: show verified recovery actions for empty search resultsdreamybu3
  • 2026-09-23 15:46Merge pull request #1 from BAITC-Hacks/codex/ai-event-request-parserdreamybu3
Все коммиты (14)
  • 2026-09-23 15:40chore: prepare verified hackathon handoff and remove outdated archivedreamybu3
  • 2026-09-23 15:08feat: search automatically after AI parsing and show match percentagesdreamybu3
  • 2026-09-23 14:42Merge current main into parser branch, preserving uploaded demo archivedreamybu3
  • 2026-09-23 14:39feat: add validated AI event request parser with manual fallbackdreamybu3
  • 2026-09-23 14:39Add files via uploaddreamybu3
  • 2026-09-23 14:39chore: import existing Firebird Match demo and preserve starter READMEdreamybu3
Структура репозитория (24 файлов)
.env.example
.gitignore
README.md
ai.py
app.py
comparison.py
data/contractors.csv
docs/BRIEF.md
docs/CONTRACT.md
docs/DEMO.md
docs/REPOSITORY_STARTER_README.md
event_parser.py
matcher.py
start.bat
tests/test_app.cjs
tests/test_comparison.py
tests/test_event_form.cjs
tests/test_event_parser.py
tests/test_matcher.py
web/app.js
web/event-parser.js
web/favicon.svg
web/index.html
web/style.css
Ветки (5)

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

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

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