Подбор подрядчиков (SA-3)
Веб-сервис фильтрует каталог подрядчиков для мероприятий по условиям заказа и через отдельный API-маршрут просит LLM выбрать до 3 кандидатов с…
О проекте
Команда: 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() и не считаются действующими возможностями приложения.
Как работает решение
Текущий сценарий на сайте
- Заказчик открывает каталог и нажимает кнопку категории или «Все».
- В панели указывает условия мероприятия и нажимает «Применить».
- JavaScript отправляет JSON в
POST /api/filter. - FastAPI проверяет параметры, Python читает CSV и исключает неподходящих кандидатов.
- Сервер возвращает
contractor_ids; браузер показывает карточки с этими ID и обновляет счётчик.
В терминале сервера появляются строки:
успешно получено
успешно обработано
Первое сообщение подтверждает приём корректного JSON, второе — завершение фильтрации. Даже при нуле совпадений запрос считается успешно обработанным.
Отдельный сценарий рекомендаций через API
POST /api/contractors/search применяет те же фильтры и передаёт всех оставшихся кандидатов в Backend/ai_service.py. Текущая реализация отправляет их вместе с параметрами заказа в OpenAI Responses API и ожидает до трёх ID с объяснениями. Сервер проверяет ответ и подставляет исходные карточки из CSV.
Если кандидатов нет, возвращается пустой список без обращения к модели. Если их один или два, ответ не должен дополняться выдуманными профилями. Этот сценарий требует ключа и отдельной проверки AI-интеграции: текущий промпт и вызывающий код ещё не согласованы.
Технологии
| Компонент | Использование |
|---|---|
| Python | Загрузка данных, фильтрация и обработка ответа ИИ |
| FastAPI | HTTP API и раздача HTML-страницы |
| Pydantic | Проверка входных данных и моделей ответа |
| Uvicorn | Запуск ASGI-приложения |
| HTML, CSS, JavaScript | Каталог, панель фильтров и запросы через fetch |
CSV и стандартный модуль csv | Хранение и чтение каталога; база данных не используется |
| HTTPX | HTTP-запросы к 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.html | HTML, стили, 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 000 000 ₸, язык русский, формат корпоратив, дата 10 октября 2026 года, длительность 6 часов.
- Нажмите «Применить».
Для текущего 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/filter | contractor_ids; используется сайтом |
POST /api/contractors/filter | total_candidates, matched_candidates, candidates |
POST /api/contractors/search | Счётчики и recommendations: карточка contractor и объяснение reason |
Все три POST-маршрута принимают одинаковые параметры. Они необязательны; {} отключает ограничения.
| Поле | Формат и правило |
|---|---|
categories | Массив строк; достаточно любой выбранной категории |
city | Строка; точное совпадение без учёта регистра и внешних пробелов |
min_price_kzt, max_price_kzt | Целые неотрицательные суммы в тенге; границы включаются |
languages | Массив строк; необходимы все указанные языки |
event_format | Один формат мероприятия |
event_date | YYYY-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/ доступен на компьютере, где запущен сервер, и не является публичным размещением.
Авторы коммитов
История коммитов
- 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 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.