AAYMA
RU
Войти

Подбор подрядчиков (SA-3)

Веб-сервис фильтрует каталог подрядчиков для мероприятий по условиям заказа и через отдельный API-маршрут просит LLM выбрать до 3 кандидатов с…

Прототип
Команда «SA-3» 1💬 0 комментариевс 23 сентября 2026 г.

О проекте

Команда: SA-3
Кейс HackAlem.ai: Креативные индустрии — Firebird
Стадия: Прототип

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

Суть

Веб-сервис фильтрует каталог подрядчиков для мероприятий по условиям заказа и через отдельный API-маршрут просит LLM выбрать до 3 кандидатов с объяснениями. Интерфейс пока вызывает только фильтрацию.

Как устроено

Браузерная форма отправляет JSON в FastAPI. Параметры валидируются через Pydantic, затем Python фильтрует CSV по категории, городу, цене, языкам, формату, дате и длительности и возвращает ID подходящих карточек. Маршрут /api/contractors/search передаёт оставшихся кандидатов в OpenAI Responses API со строгой JSON Schema и проверяет, что ответ содержит не больше 3 уникальных ID из допустимого набора. Этот маршрут в UI не подключён, реальный вызов модели не проверен.

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

  • rule-based filtering
  • JSON Schema structured output
  • валидация ID ответа LLM

Особенности

README честно описывает, что не работает: AI-маршрут не подключён к UI, из тестов проходят 9, падают 4, AI_MODE=mock не действует. Ответ LLM проверяется на принадлежность ID кандидатам, поэтому модель не может подставить выдуманных подрядчиков.

Стек

Python · FastAPI · Pydantic · Uvicorn · HTTPX · HTML/CSS/JS · CSV · unittest

Модели

GPT-4o-mini


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

README

README.md

Подбор подрядчиков — прототип команды SA-3

Сервис для заказчиков и организаторов мероприятий: помогает сократить каталог подрядчиков по условиям заказа и получить до трёх рекомендаций с объяснениями. Основная идея проекта — показать, почему кандидат подходит, а не просто отсортировать список.

Проект создан для хакатона. Каталог содержит 66 вымышленных профилей и 17 категорий: ведущие, фотографы, площадки, флористы и другие подрядчики. Это демонстрационные данные, а не предложение реальных услуг.

Статус текущей версии. На сайте подключена серверная фильтрация и показ подходящих карточек. Для рекомендаций с объяснениями есть отдельный API-маршрут, но интерфейс его пока не вызывает. Реальная работа с OpenAI не подтверждена, а AI-код, тесты и сопутствующая документация частично расходятся. Подробнее — в разделах «Проверка» и «Ограничения».

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

  • Каталог карточек с категорией, городом, начальной ценой, языками, форматами мероприятий и описанием.
  • Форма фильтров: категория, город, диапазон цен, языки, формат, дата, длительность и текст пожеланий.
  • Передача параметров из браузера в FastAPI в формате JSON.
  • Фильтрация CSV на Python и обновление видимых карточек по полученным ID.
  • Проверка типов, диапазонов цен и границ календаря через Pydantic.
  • API для получения справочников фильтров и полных профилей после отбора.
  • Отдельный маршрут рекомендаций: отправка допустимых кандидатов в OpenAI, разбор ответа, проверка количества, уникальности и принадлежности выбранных ID.
  • Интерактивная документация API через Swagger UI.

В репозитории также есть вспомогательные модули ранжирования по текстовым признакам и кеширования. Они не подключены к текущей функции recommend_contractors() и не считаются действующими возможностями приложения.

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

Текущий сценарий на сайте

  1. Заказчик открывает каталог и нажимает кнопку категории или «Все».
  2. В панели указывает условия мероприятия и нажимает «Применить».
  3. JavaScript отправляет JSON в POST /api/filter.
  4. FastAPI проверяет параметры, Python читает CSV и исключает неподходящих кандидатов.
  5. Сервер возвращает contractor_ids; браузер показывает карточки с этими ID и обновляет счётчик.

В терминале сервера появляются строки:

успешно получено
успешно обработано

Первое сообщение подтверждает приём корректного JSON, второе — завершение фильтрации. Даже при нуле совпадений запрос считается успешно обработанным.

Отдельный сценарий рекомендаций через API

POST /api/contractors/search применяет те же фильтры и передаёт всех оставшихся кандидатов в Backend/ai_service.py. Текущая реализация отправляет их вместе с параметрами заказа в OpenAI Responses API и ожидает до трёх ID с объяснениями. Сервер проверяет ответ и подставляет исходные карточки из CSV.

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

Технологии

КомпонентИспользование
PythonЗагрузка данных, фильтрация и обработка ответа ИИ
FastAPIHTTP API и раздача HTML-страницы
PydanticПроверка входных данных и моделей ответа
UvicornЗапуск ASGI-приложения
HTML, CSS, JavaScriptКаталог, панель фильтров и запросы через fetch
CSV и стандартный модуль csvХранение и чтение каталога; база данных не используется
HTTPXHTTP-запросы к OpenAI и тестирование API
python-dotenvЗагрузка серверных настроек из .env
OpenAI Responses APIВнешний сервис; структурированный ответ через JSON Schema
unittest, unittest.mockАвтоматические проверки и подставные ответы

В .env.example указана модель gpt-4o-mini. Имя задаётся через OPENAI_MODEL; доступность модели для конкретного аккаунта и качество реального ответа не проверены. OpenAI SDK в зависимостях нет: запрос выполняется через HTTPX. Собственная модель не обучается.

Архитектура

Один FastAPI-сервер и статическая страница, без отдельных микросервисов.

Браузер → FastAPI → проверка SearchRequest → CSV → Python-фильтрация
                                                       │
                   /api/filter ← список ID ←───────────┤
                                                       │
          /api/contractors/search → AI-сервис → OpenAI → проверка ответа
                                                       │
                              клиент ← карточки + объяснения
Файл или каталогНазначение
main.pyЗапуск сервера на 127.0.0.1:8000
Backend/main.pyМаршруты API, HTTP-ошибки, раздача страницы
Backend/schemas.pyПараметры поиска, профили и форматы ответов
Backend/dataset.pyЧтение CSV и проверка уникальности ID
Backend/filtering.pyОбязательные фильтры без ИИ
Backend/ai_service.pyВызов OpenAI и проверка рекомендаций
Backend/prompts.pyИнструкция для модели
Backend/ai_facts.py, Backend/ai_cache.pyДополнительные AI-компоненты; подключение не завершено
frontend/hackathon dataset preview.htmlHTML, стили, JavaScript и исходные карточки
hackathon dataset anonymized .csvОсновной каталог подрядчиков
tests/Проверки API, фильтрации и AI-части
docs/ai/Документация, примеры и отчёт AI-участника; часть утверждений расходится с текущим кодом

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

Команды выполняются из корня репозитория. Основной вариант — Windows PowerShell. Текущая локальная проверка выполнена на Python 3.14.0; обязательная точная версия Python в репозитории не закреплена.

1. Получить проект

Если проект уже скачан, пропустите клонирование:

git clone https://github.com/BAITC-Hacks/hack-130eadd9-sa-3.git
cd hack-130eadd9-sa-3

Для клонирования нужен доступ к репозиторию команды.

2. Создать окружение и установить зависимости

Если .venv уже существует, повторно создавать его не нужно.

py -m venv .venv
.venv/Scripts/python.exe -m pip install -r requirements.txt

Зависимости перечислены в requirements.txt. Node.js, npm, PostgreSQL и Docker для текущего запуска не требуются.

3. Запустить сайт и API

.venv/Scripts/python.exe main.py

Оставьте терминал открытым. Доступные адреса:

Для остановки нажмите Ctrl+C. Если порт 8000 занят другим сервером, сначала остановите его. Не открывайте HTML двойным щелчком через file://: запрос к API должен идти через сервер приложения.

Для разработки с автоматической перезагрузкой:

.venv/Scripts/python.exe -m uvicorn Backend.main:app --reload

Регистр Backend важен: это имя пакета в репозитории. В PyCharm выберите .venv/Scripts/python.exe как интерпретатор и запускайте корневой main.py.

4. Настройки OpenAI — только для маршрута рекомендаций

Для сайта и фильтрации не нужны .env, ключ или доступ к OpenAI.

Для отдельной проверки рекомендаций создайте .env по образцу, если файла ещё нет:

if (-not (Test-Path .env)) { Copy-Item .env.example .env }

Заполните локально:

OPENAI_API_KEY=ваш_ключ
OPENAI_MODEL=gpt-4o-mini

Перезапустите сервер после изменения настроек. Ключ используется только на сервере; .env исключён из Git. Реальный вызов модели требует доступа к API и может расходовать средства аккаунта.

CORS_ORIGINS задаёт разрешённые адреса отдельного frontend-сервера. При открытии сайта через FastAPI менять его не требуется. Само разрешение CORS не перенаправляет запросы с другого сервера в API.

В .env.example также присутствуют AI_MODE, AI_TIMEOUT_SECONDS и AI_CACHE_TTL_SECONDS, однако текущая функция recommend_contractors() их не читает. AI_MODE=mock сейчас не включает автономный режим.

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

Повторяемый сценарий для жюри без OpenAI

  1. Запустите приложение и откройте сайт.
  2. Нажмите «Ведущий».
  3. Укажите: город Алматы, максимальная цена 1 000 000 ₸, язык русский, формат корпоратив, дата 10 октября 2026 года, длительность 6 часов.
  4. Нажмите «Применить».

Для текущего CSV API возвращает четыре ID:

{
  "contractor_ids": ["HK-88430", "HK-77838", "HK-27222", "HK-29829"]
}

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

Тот же сценарий можно проверить в Swagger UI: откройте POST /api/filter, нажмите Try it out, вставьте JSON и нажмите Execute:

{
  "categories": ["Ведущий"],
  "city": "Алматы",
  "max_price_kzt": 1000000,
  "languages": ["русский"],
  "event_format": "корпоратив",
  "event_date": "2026-10-10",
  "duration_hours": 6
}

Дополнительные проверки:

  • {"max_price_kzt": 0} → HTTP 200 и пустой список ID.
  • {"event_date": "2027-01-01"} → HTTP 422: дата вне известного календаря.
  • POST /api/contractors/filter с основным примером → полные профили и счётчики.

При подготовке README основной API-сценарий, пустая выдача, отклонение даты, GET / и /health проверены через Python/FastAPI TestClient. Ручная проверка кликов в браузере и реальный запрос к OpenAI не выполнялись.

Автоматические тесты и их текущий статус

.venv/Scripts/python.exe -m unittest discover -s tests -v

На текущем состоянии репозитория полный набор не проходит. При проверке с заблокированным внешним HTTP получено 9 успешных проверок и 4 ошибки:

  • test_ai_service и test_ai_integration не импортируются: в Backend.ai_service отсутствует ожидаемая функция clear_cache.
  • В двух проверках tests/test_backend.py используется неопределённая функция choice.

Скрипт docs/ai/demo.py тоже импортирует clear_cache, поэтому пока не подходит для запуска демонстрации. Сохранённые отчёты в docs/ai/ не заменяют проверку текущей версии. Эти ошибки не мешают отдельно проверенному сценарию фильтрации, но не позволяют заявлять готовность всей AI-части.

API и параметры

Метод и путьРезультат
GET /Страница каталога
GET /health{"status": "ok"}; проверяет сервер, не внешний API
GET /api/filtersКатегории, города, языки, форматы и границы календаря
POST /api/filtercontractor_ids; используется сайтом
POST /api/contractors/filtertotal_candidates, matched_candidates, candidates
POST /api/contractors/searchСчётчики и recommendations: карточка contractor и объяснение reason

Все три POST-маршрута принимают одинаковые параметры. Они необязательны; {} отключает ограничения.

ПолеФормат и правило
categoriesМассив строк; достаточно любой выбранной категории
cityСтрока; точное совпадение без учёта регистра и внешних пробелов
min_price_kzt, max_price_kztЦелые неотрицательные суммы в тенге; границы включаются
languagesМассив строк; необходимы все указанные языки
event_formatОдин формат мероприятия
event_dateYYYY-MM-DD; исключает занятых в этот день
duration_hoursЧисло больше 0 и не больше 24
wishesДо 4000 символов; передаётся ИИ, не участвует в обязательной фильтрации

Разные фильтры применяются одновременно. При заданной длительности кандидаты с пустым max_hours исключаются. Цена сравнивается с price_from_kzt — ценой «от», без умножения на часы. API поддерживает несколько категорий, текущая форма выбирает одну или «Все».

Ошибки возвращаются в поле detail: 422 — некорректный запрос; 503 — отсутствие настроек ИИ или ошибка загрузки каталога; 502 — ошибка провайдера или неправильный ответ; 504 — тайм-аут ИИ.

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

Источник профилей — hackathon dataset anonymized .csv в корне проекта. Он содержит ID, вымышленное имя, категории, город, начальную цену, форматы, языки, максимальную длительность, занятые даты и описание. Списки внутри ячеек разделены символом |.

  • price_imputed и city_imputed отмечают значения, проставленные при подготовке данных.
  • synthetic — отдельный флаг синтетического профиля; весь каталог используется только как прототип.
  • Календарь ограничен 2026-09-23 — 2026-12-31, 100 днями.
  • Исходные карточки дополнительно записаны в HTML; сервер фильтрует CSV, а браузер сопоставляет ответ с карточками по ID.

Единственная подключённая в коде внешняя AI-интеграция — https://api.openai.com/v1/responses. Текущий сервис передаёт параметры заказа и все поля отфильтрованных профилей. Ключ остаётся в заголовке серверного запроса и не передаётся браузеру. Форма отправляет JSON, а не файл: CSV уже находится на сервере.

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

  • Рекомендации TOP-3 и объяснения ещё не показаны в интерфейсе: сайт использует только фильтрацию.
  • AI-интеграция не завершена: промпт требует объяснять заранее выбранных людей и использовать evidence_refs, но текущий сервис передаёт всех кандидатов и схему только с ID и reason. Успешный реальный ответ не подтверждён.
  • Ранжирование из ai_facts.py, кеш из ai_cache.py, mock и резервные объяснения описаны в AI-документации, но не подключены к действующему сервису. Стабильность выбора ИИ не гарантируется.
  • Автоматический набор тестов содержит ошибки, перечисленные выше.
  • Данные вымышлены; доступность по календарю не является бронированием, цена «от» не является итоговой сметой.
  • Форма не ограничивает дату границами CSV; неподдерживаемая дата отклоняется сервером. Подробная причина ошибки API пока не показывается в интерфейсе.
  • CSV и HTML-карточки требуют согласованного обновления. Пустой или повреждённый CSV обрабатывается не во всех случаях корректно.
  • Нет личных кабинетов, оформления заказа, оплаты, бронирования, базы данных и административного интерфейса.
  • Нет авторизации и ограничения частоты запросов; это локальный прототип, а не готовый публичный сервис.
  • Версии зависимостей заданы диапазонами. Проверка на Linux и подтверждённые измерения скорости реального OpenAI отсутствуют.

AI_Codex_Workplan.md и AI_FINAL_TASK.md содержат планы и задания. Их пункты нельзя считать реализованными только по наличию этих документов.

Опубликованная версия

Подтверждённой ссылки на deployed-версию в репозитории не найдено. Проверить прототип можно локально по инструкции выше. Адрес http://127.0.0.1:8000/ доступен на компьютере, где запущен сервер, и не является публичным размещением.

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

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

scp-682-joke 8sailaudinmuhammed-cmyk 7Ярослав 7

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

  • 2026-09-23 17:57Merge branch 'main' of https://github.com/BAITC-Hacks/hack-130eadd9-sa-3scp-682-joke
  • 2026-09-23 17:57update readmescp-682-joke
  • 2026-09-23 17:56fixed problemsailaudinmuhammed-cmyk
  • 2026-09-23 17:47Merge branch 'feature/ai'sailaudinmuhammed-cmyk
  • 2026-09-23 17:27ran through problems with APIsailaudinmuhammed-cmyk
  • 2026-09-23 17:26ran through apisailaudinmuhammed-cmyk
  • 2026-09-23 16:44Frontend(new frame and filtr)Ярослав
  • 2026-09-23 16:32New connection between Front and BackЯрослав
Все коммиты (22)
  • 2026-09-23 16:24commenting on backend for understandingscp-682-joke
  • 2026-09-23 16:19fixed problem with backend and frontend connectionЯрослав
  • 2026-09-23 16:09stacked frontend and backend requestsscp-682-joke
  • 2026-09-23 16:03Merge branch 'main' of https://github.com/BAITC-Hacks/hack-130eadd9-sa-3Ярослав
  • 2026-09-23 16:02frontend part(filters)Ярослав
  • 2026-09-23 15:49implemented OpenAI api part of agendasailaudinmuhammed-cmyk
  • 2026-09-23 15:45adding Fast API routes = take request, filter, give to AI, give resultscp-682-joke
  • 2026-09-23 15:28adding frontend from the hackathon ready-to-use datasetЯрослав
  • 2026-09-23 14:53testing connectionЯрослав
  • 2026-09-23 14:51deleting unnecessaryscp-682-joke
  • 2026-09-23 14:42Merge branch 'main' of https://github.com/BAITC-Hacks/hack-130eadd9-sa-3sailaudinmuhammed-cmyk
  • 2026-09-23 14:42Implementing the main page of the frontand side of the projectsailaudinmuhammed-cmyk
  • 2026-09-23 14:17create agents.md for AI codingscp-682-joke
  • 2026-09-23 13:55testing github connectionscp-682-joke
Структура репозитория (27 файлов)
.env.example
.gitignore
AGENTS.md
AI_Codex_Workplan.md
AI_FINAL_TASK.md
Backend/__init__.py
Backend/ai_cache.py
Backend/ai_facts.py
Backend/ai_service.py
Backend/dataset.py
Backend/filtering.py
Backend/main.py
Backend/prompts.py
Backend/schemas.py
README.md
docs/ai/README.md
docs/ai/VERIFICATION.md
docs/ai/demo.py
docs/ai/offline_examples.json
frontend/hackathon dataset preview.html
hackathon dataset anonymized .csv
main.py
requirements.txt
tests/ai_test_support.py
tests/test_ai_integration.py
tests/test_ai_service.py
tests/test_backend.py
Ветки (2)

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

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

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