AAYMA
RU
Войти

Совпало

Сервис подбирает до трёх подрядчиков на мероприятие по городу, дате, формату, категории, бюджету, языку и длительности, объясняет совпадение и…

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

О проекте

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

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

Суть

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

Как устроено

CSV-каталог (66 профилей) загружается в память. RecommendationService строго фильтрует по всем условиям, начисляет детерминированный скор, сортирует по релевантности или цене и отдаёт топ-3 со статусом MATCHED, CATEGORY_NOT_FOUND или NO_MATCH. ExplanationService формирует локальное фактологическое объяснение, которое при включённой опции переформулирует OpenAI Responses API. Модель не влияет на отбор и порядок.

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

  • strict filtering
  • deterministic scoring
  • LLM explanation with local fallback
  • i18n

Особенности

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

Стек

Python · FastAPI · Pydantic · uv · HTML/CSS/JavaScript · Node.js

Модели

gpt-4.1-mini


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

README

README.md

Совпало

«Совпало» - веб-сервис для точного подбора подрядчиков на мероприятия. Вместо длинного каталога пользователь получает до трёх подходящих вариантов с объяснением, почему каждый из них соответствует параметрам события.

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

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

Пользователь может указать город, дату, тип мероприятия, категорию подрядчика и бюджет, а при необходимости уточнить длительность и язык работы (RU, KZ или EN). Города, категории и форматы загружаются из backend, поэтому значения формы соответствуют текущему датасету. Сервис строго фильтрует кандидатов по всем условиям, рассчитывает детерминированную оценку, сохраняет стабильный порядок результатов и позволяет сортировать их по релевантности или цене.

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

Объяснения могут формироваться через OpenAI Responses API. Если интеграция выключена или внешний запрос завершается ошибкой, backend возвращает локальное фактологическое объяснение. Также реализованы адаптивная вёрстка, REST API и интерактивная документация OpenAPI/Swagger.

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

При открытии страницы frontend запрашивает GET /api/meta и заполняет форму значениями из датасета. После отправки формы параметры события передаются в POST /api/recommendations. Backend сначала формирует пул подрядчиков нужной категории в выбранном городе, а затем исключает тех, кто занят в указанную дату, превышает бюджет, не работает с нужным форматом или языком либо не может обеспечить требуемую длительность.

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

Технологии

Backend написан на Python 3.13+ и использует FastAPI, Uvicorn, Pydantic и python-dotenv. Frontend построен на HTML5, CSS3 и JavaScript ES Modules без UI-фреймворка; его раздаёт небольшой Node.js-сервер на встроенных модулях node:http, node:fs/promises и node:path. Каталог читается из CSV стандартным модулем Python csv.

AI-часть использует OpenAI Python SDK и Responses API; модель по умолчанию - gpt-4.1-mini. Интеграция опциональна: в стандартной конфигурации OPENAI_ENABLED=false, поэтому проект полностью работает без API-ключа. Для оформления интерфейса подключены Google Fonts и тематические изображения Unsplash.

Архитектура

Пользователь
    │
    ▼
Frontend (HTML / CSS / JavaScript)
    │  GET /api/meta
    │  POST /api/recommendations
    ▼
FastAPI
    ├── ContractorRepository ─────► dataset/dataset.csv
    ├── RecommendationService
    │     ├── строгая фильтрация
    │     ├── скоринг и сортировка
    │     └── выбор до 3 кандидатов
    └── ExplanationService
          ├── локальное объяснение
          └── OpenAI Responses API (опционально)

Точка входа FastAPI, настройка CORS и маршруты находятся в backend/src/main.py, а конфигурация из переменных окружения - в backend/src/config.py. Модуль repository.py читает CSV и формирует каталог, recommendations.py отвечает за фильтрацию, скоринг, сортировку и статусы результата, explanations.py создаёт локальные или AI-объяснения, а models.py валидирует входной REST-запрос через Pydantic.

На клиентской стороне frontend/app.js взаимодействует с API и отрисовывает карточки и состояния, frontend/styles.css содержит адаптивное оформление, а frontend/server.mjs раздаёт статические файлы и формирует runtime-конфигурацию адреса API.

REST API

МетодПутьНазначение
GET/краткая информация о сервисе
GET/api/healthсостояние API, размер каталога и режим объяснений
GET/api/metaгорода, категории, типы мероприятий и языки
POST/api/recommendationsподбор до трёх подрядчиков

Backend возвращает один из трёх статусов: MATCHED, если подходящие кандидаты найдены; CATEGORY_NOT_FOUND, если в городе нет выбранной категории; NO_MATCH, если категория представлена, но все кандидаты отсеяны условиями.

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

Требования

Для запуска нужны Python 3.13 или новее, uv для установки Python-зависимостей, а также Node.js и npm для frontend. API-ключ OpenAI требуется только при включении AI-объяснений.

1. Клонировать репозиторий

git clone https://github.com/BAITC-Hacks/hack-99690795-spectra.git
cd hack-99690795-spectra

2. Настроить backend

Скопируйте пример конфигурации:

cp backend/.env.example backend/.env

В PowerShell эквивалентная команда:

Copy-Item backend/.env.example backend/.env

Установите зависимости и запустите API из корня проекта:

uv sync
uv run python main.py

Backend будет доступен по адресу http://localhost:8000, Swagger UI - http://localhost:8000/docs.

3. Настроить и запустить frontend

В отдельном терминале из корня проекта:

cp frontend/.env.example frontend/.env
cd frontend
npm run dev

Для PowerShell:

Copy-Item frontend/.env.example frontend/.env
Set-Location frontend
npm run dev

Откройте http://localhost:5173.

В package.json нет сторонних frontend-зависимостей, поэтому выполнять npm install для текущей версии не требуется.

4. Опционально включить OpenAI

Измените backend/.env:

OPENAI_ENABLED=true
OPENAI_API_KEY=your_api_key
OPENAI_MODEL=gpt-4.1-mini

Если запрос к OpenAI завершится ошибкой, сервис автоматически использует локально сформированные объяснения.

Переменные окружения

Backend:

ПеременнаяЗначение по умолчаниюНазначение
APP_NAMEСовпало APIназвание API
APP_HOST127.0.0.1адрес backend
APP_PORT8000порт backend
DATASET_PATH../dataset/dataset.csvпуть к CSV относительно backend/
ALLOWED_ORIGINSлокальные адреса frontend на порту 5173разрешённые CORS origins
OPENAI_ENABLEDfalseвключить AI-объяснения
OPENAI_API_KEYпустоAPI-ключ OpenAI
OPENAI_MODELgpt-4.1-miniмодель для объяснений
OPENAI_TIMEOUT_SECONDS10тайм-аут запроса к OpenAI

Frontend:

ПеременнаяЗначение по умолчаниюНазначение
FRONTEND_PORT5173порт frontend-сервера
API_URLhttp://localhost:8000/apiбазовый URL REST API

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

После запуска backend и frontend откройте http://localhost:5173 и повторите сценарий:

ПолеЗначение
ГородАлматы
Дата18.10.2026
Тип мероприятиясвадьба
КатегорияВедущий
Бюджет1 200 000 ₸
Длительность6 часов
ЯзыкRU
Сортировкасначала точное совпадение

Нажмите «Найти подрядчиков». В текущем датасете условиям соответствуют четыре профиля, а интерфейс должен показать три наиболее релевантные карточки. В каждой карточке будут цена, язык, описание, совпавшие параметры и конкретное объяснение выбора.

Для проверки API без frontend можно выполнить:

curl -X POST http://localhost:8000/api/recommendations \
  -H "Content-Type: application/json" \
  -d '{
    "city": "Алматы",
    "date": "2026-10-18",
    "event_type": "свадьба",
    "category": "Ведущий",
    "budget": 1200000,
    "duration": 6,
    "language": "ru",
    "sort": "relevance"
  }'

Дополнительно можно проверить отрицательные сценарии. Если выбрать сочетание города и категории, отсутствующее в каталоге, интерфейс сообщит, что в этом городе такой категории нет. Если категория существует, но бюджет слишком мал или все кандидаты заняты в указанную дату, пользователь увидит сообщение о том, что кандидаты есть, но ни один не проходит условия, а также конкретные причины отсева.

Данные и интеграции

Каталог подрядчиков

Файл dataset/dataset.csv содержит 66 профилей: 50 из Алматы, 15 из Астаны и один с местоположением «Зарубежье». Для каждого профиля хранятся идентификатор, анонимизированное имя, категории, город, цена, форматы мероприятий, языки, максимальная длительность, занятые даты и описание. Поля synthetic и price_imputed позволяют явно обозначать синтетические профили и восстановленные значения цены.

CSV загружается в память при старте backend. Путь к другому файлу можно передать через DATASET_PATH, если он соответствует ожидаемой структуре.

OpenAI

При включённой интеграции backend отправляет параметры запроса и данные уже отобранных кандидатов в OpenAI Responses API. Модель возвращает краткие объяснения на русском языке. Решение о допуске кандидата и его позиция формируются локально до обращения к модели.

Ресурсы интерфейса

Google Fonts загружает семейства Inter, Manrope и Playfair Display, а Unsplash используется для тематических изображений карточек. Если внешнее изображение загрузить не удаётся, интерфейс показывает локальный файл frontend/assets/event-fallback.svg.

Ограничения текущей версии

Каталог хранится в статическом CSV и не обновляется через интерфейс. Занятость также берётся из этого файла: синхронизации с календарями подрядчиков нет. В текущей версии не реализованы регистрация, авторизация, личные кабинеты, отправка заявки, бронирование, оплата и прямая связь с подрядчиком. Сервис всегда возвращает максимум три карточки, даже если условиям соответствует больше кандидатов.

Изображения назначаются по категории и идентификатору и не являются фотографиями конкретных подрядчиков. AI-объяснения требуют отдельного API-ключа и доступа к OpenAI, а Google Fonts и изображения Unsplash - подключения к интернету. Автоматические тесты, Docker-конфигурация и production-инфраструктура в репозитории отсутствуют.

Развёрнутая версия

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

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

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

twinfox20 8hawts4uce 5Zlata 2

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

  • 2026-09-23 17:14Upd: README filehawts4uce
  • 2026-09-23 17:06Add: i18n & mobile adaptationtwinfox20
  • 2026-09-23 16:28Upd: sidebar now sticky & card are y-scrollinghawts4uce
  • 2026-09-23 15:51Merge branch 'main' of https://github.com/BAITC-Hacks/hack-99690795-spectratwinfox20
  • 2026-09-23 15:48Add: backend & frontendtwinfox20
  • 2026-09-23 15:10Merge branch 'main' of https://github.com/BAITC-Hacks/hack-99690795-spectrahawts4uce
  • 2026-09-23 15:09Updated Archirecturehawts4uce
  • 2026-09-23 14:24Merge branch 'main' of https://github.com/BAITC-Hacks/hack-99690795-spectraZlata
Все коммиты (15)
  • 2026-09-23 14:24add: reference siteZlata
  • 2026-09-23 14:12Added README.mdhawts4uce
  • 2026-09-23 13:33Add: csv datasettwinfox20
  • 2026-09-23 13:30Add: dataset previewtwinfox20
  • 2026-09-23 12:51Upd: .env; Add: OpenAI client & editorconfigtwinfox20
  • 2026-09-23 11:13Add: uv venvtwinfox20
  • 2026-09-23 11:02Add: .env to gitignoretwinfox20
Структура репозитория (25 файлов)
.editorconfig
.gitignore
.python-version
README.md
backend/.env.example
backend/__init__.py
backend/src/__init__.py
backend/src/config.py
backend/src/explanations.py
backend/src/i18n.py
backend/src/main.py
backend/src/models.py
backend/src/recommendations.py
backend/src/repository.py
dataset/dataset.csv
dataset/preview.html
frontend/.env.example
frontend/app.js
frontend/assets/event-fallback.svg
frontend/index.html
frontend/package.json
frontend/server.mjs
frontend/styles.css
main.py
pyproject.toml

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

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

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