AAYMA
RU
Войти

AI-ANA

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

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

О проекте

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

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

Суть

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

Как устроено

FastAPI загружает и проверяет каталог CSV/JSONL при старте; запрос (город, дата, формат, категория, бюджет, опционально язык и длительность) фильтрует профили, исключая занятых по busy_dates. Подходящие сортируются по начальной цене и идентификатору, берутся первые три. Объяснения собираются детерминированно из проверенных фрагментов описаний (проверка хеша и наличия цитаты); различаются исходы «подобрали», «нет категории в городе» и «никто не проходит условия». React-фронт показывает карточки и «Основания».

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

  • фильтрация по ограничениям
  • стабильная сортировка
  • объяснения из верифицированных цитат
  • причины исключения
  • diff при смене даты

Метрики

270 серверных и 39 браузерных проверок пройдено; среднее время выдачи 59.4 мс

Особенности

Сознательно без LLM: объяснения собираются из цитат, привязанных к исходному тексту по хешу, поэтому преимущества не выдумываются. При смене только даты интерфейс показывает, кто стал занят, а кто просто выпал из топ-3.

Стек

Python · FastAPI · Pydantic · React · TypeScript · Vite · Tailwind CSS · SQLite · Docker Compose · Nginx · Playwright · pytest


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

README

README.md

AI-ANA

Подбор event-подрядчиков в Казахстане с проверяемыми объяснениями. Решение хакатон-задачи #79-lite: 66 исходных профилей, до трёх карточек в ответе, конкретные причины выбора и отсутствия результата.

Запуск · Живая проверка · Алгоритм · Ограничения

Интерфейс AI-ANA: условия события и карточки подрядчиков

Для кого и какую проблему решает

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

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

ТребованиеПоведение приложения
Входные данныеОбязательны город, дата, формат мероприятия, категория и бюджет в тенге; язык и длительность опциональны
До трёх карточекИмя, категория, город, начальная цена и объяснение из 1–2 предложений; раскрываемые «Основания» позволяют проверить факты
ЗанятостьПодрядчики с выбранной датой в busy_dates исключаются до формирования карточек; площадки проверяются так же, как люди
Меньше трёх результатовПоказываются только подходящие профили, их количество и причины исключения остальных
Три разных исхода«Подобрали», «В городе нет такой категории» и «Категория есть, но условия не проходит никто» явно различаются
ПовторяемостьПри одинаковых данных и параметрах порядок одинаков: начальная цена, затем идентификатор
Смена датыПри изменении только даты интерфейс объясняет, кто стал занят, а кто просто вышел из первых трёх
Честность данныхСинтетические профили, дополненные города и цены отмечены; отсутствие сведений не заменяется выдуманными преимуществами

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

Есть регистрация, вход и редактирование имени и города; аккаунты сохраняются в SQLite. Для подбора регистрация не нужна. Эти дополнительные функции не меняют правила отбора.

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

  1. Пользователь задаёт условия или выбирает готовый пример. Браузер проверяет поля и отправляет POST /api/recommendations.
  2. FastAPI повторно проверяет запрос. Каталог заранее загружен и проверен при запуске сервера.
  3. Сначала выбираются город и категория. Затем проверяются свободная дата, формат и бюджет; язык и длительность проверяются, если они заданы.
  4. Подходящие профили сортируются по начальной цене и нормализованному идентификатору; в ответ попадают первые три. Это воспроизводимый порядок, а не оценка качества подрядчика.
  5. Сервер составляет объяснения из проверенных фрагментов описаний и полей профиля, добавляет основания, предупреждения и причины исключения. Браузер показывает карточки либо объяснённый пустой результат.

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

Технологии

КомпонентИспользуется
ИнтерфейсTypeScript 7.0.2, React 19.3.0, Vite 8.3.0, Tailwind CSS 4.3.3, React Hook Form 7.88.0, Zod 4.6.5
СерверPython 3.13, FastAPI 0.141.1, Pydantic 2.13.5, Uvicorn 0.53.0
ДанныеCSV/JSONL для каталога, JSON для разметки объяснений, SQLite для аккаунтов и сессий
ЗапускDocker Compose, Nginx; для разработки — Uvicorn и Vite
Проверкиpytest 9.1.1, Playwright 1.63.0 и Chromium
AI и внешние APIВнешние модели, эмбеддинги и LLM API не используются; объяснения формируются детерминированно

Зависимости закреплены в requirements.txt и package-lock.json. NVIDIA/Brev, Supabase и облачное хранилище к приложению сейчас не подключены; ключи внешних сервисов для запуска не нужны.

Архитектура проекта

flowchart LR
    U[Пользователь: условия события] --> F[React: форма и карточки]
    F --> N[Nginx или Vite proxy]
    N --> A[FastAPI: валидация и API]
    C[CSV / JSONL: 66 профилей] --> V[Проверка и загрузка в память]
    V --> M[Фильтры и стабильный порядок]
    A --> M
    M --> E[Объяснения и причины исключения]
    Q[Проверенная разметка описаний] --> E
    E --> F
    A --> S[SQLite: аккаунты и сессии]

Основные части: frontend/src/ — интерфейс; backend/app/catalog.py — импорт; matching.py — отбор; explanations.py — объяснения; main.py — API; auth.py — аккаунты. Каталог подрядчиков и база аккаунтов хранятся отдельно. Браузер работает через API, исходный CSV ему не передаётся.

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

Через Docker

Нужны Git и Docker Desktop либо Docker Engine с Compose. Python и Node.js на компьютере не требуются. Первый запуск скачивает образы и зависимости.

git clone https://github.com/BAITC-Hacks/hack-3c47c506-ai-ana.git
cd hack-3c47c506-ai-ana
docker compose up --build -d --wait
АдресНазначение
http://localhost:8080Сайт
http://localhost:8080/docsИнтерактивная документация API
http://localhost:8080/healthГотовность API: ожидаются ready и 66 профилей

Если репозиторий уже клонирован, выполните последнюю команду из его корня. Состояние и журналы:

docker compose ps
docker compose logs --tail=100
docker compose down

На новом компьютере база аккаунтов создаётся пустой; все 66 подрядчиков доступны сразу. Аккаунты хранятся в volume auth-data и переживают обычный docker compose down. Флаг --volumes удаляет эту базу. Настройки порта, HTTPS, независимых копий и переноса базы — в инструкции Docker; шаблон — .env.example.

Без Docker

Нужны Python 3.13, venv и Node.js 20.19+ в ветке 20 либо >=22.12.0. Проверенная локальная среда: Linux, Python 3.13.5, Node.js 20.19.2, npm 9.2.0. Из корня репозитория:

python3 -m venv backend/.venv
backend/.venv/bin/python -m pip install -r backend/requirements.txt
cd frontend
npm ci
cd ..

В первом терминале, из корня проекта:

backend/.venv/bin/python -m uvicorn app.main:app --app-dir backend --host 127.0.0.1 --port 8000

Во втором терминале:

cd frontend
npm run dev -- --port 5173 --strictPort

Откройте http://localhost:5173, API-документация — http://127.0.0.1:8000/docs. Оба процесса должны работать; остановка — Ctrl+C. Для просмотра сборки из frontend/: npm run build, затем npm run preview -- --port 4173 --strictPort; API должен оставаться запущенным.

Vite проксирует /api на порт 8000. CATALOG_PATH позволяет выбрать локальный CSV/JSONL; после смены данных нужно перезапустить API. Без Docker аккаунты хранятся в data/auth/accounts.sqlite3. Подробнее: каталог, API, аккаунты.

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

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

СценарийДействие и параметрыОжидаемый результат
Плотная категория«Ведущие в Алматы»: Алматы, Ведущий, 30.09.2026, 1 000 000 ₸Три из шести подходящих: Куррапика → Мицури Канроджи → Кики (HK-88430, HK-44923, HK-35215)
Другая датаВ предыдущем запросе изменить только дату на 01.10.2026Две карточки: Куррапика и Мицури; интерфейс указывает, что Кики занят на новую дату
ПовторяемостьНажать «Подобрать подрядчиков» ещё раз с теми же условиямиТе же две карточки в том же порядке; после возврата на 30.09.2026 — исходные три
Редкая категория«Редкая категория»: Алматы, Декоратор, 23.09.2026, 2 500 000 ₸, 5 чУинри Рокбелл и Франки; два подходящих из двух, Франки помечен синтетическим; max_hours = null не исключает их
Нет подходящих«Небольшой бюджет»: Алматы, Ведущий, 30.09.2026, 100 000 ₸Карточек нет, но категория есть; показаны причины исключения кандидатов
Нет категории«Декораторы в Астане»: Астана, Декоратор, 23.09.2026, 2 500 000 ₸Отдельное сообщение: такой категории в каталоге города нет
Площадка и занятостьВручную: Астана, Отель, корпоратив, 23.09.2026, 3 000 000 ₸, 5 ч; затем дата 25.09.2026Сначала Веджита (HK-90012), затем пустой результат с причиной занятости

Раскройте «Основания» и сравните объяснения трёх ведущих без имён: они должны опираться на конкретные факты профилей. Дополнительно проверьте запрос национальных ансамблей: Алматы, Национальный ансамбль, свадьба, 24.09.2026, 500 000 ₸, без языка и длительности. Этот сценарий проверяет различимость объяснений при бедных описаниях и одинаковых цене, языке и лимите часов.

Чтобы повторить запрос напрямую к Docker-версии API, выполните из корня проекта:

curl --fail-with-body --silent --show-error \
  -H 'Content-Type: application/json' \
  --data-binary @docs/queries/corporate-host.json \
  http://localhost:8080/api/recommendations

При запуске без Docker замените адрес на http://127.0.0.1:8000/api/recommendations. Остальные запросы — в docs/queries. Сценарий защиты содержит пояснение алгоритма и ответы на вопросы жюри.

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

Итог проверки требований 23.09.2026: 270 серверных и 39 браузерных проверок прошли, production-сборка успешна. Пять локальных повторов дали среднее 59.4 мс от отправки формы до готовой выдачи; начальная загрузка и ввод параметров не входят в замер. Проверены в том числе различимость объяснений ансамблей и все потенциально совместимые пары исходного каталога без имён/ID. Результаты и границы проверки.

После установки локальных зависимостей выполните из корня репозитория:

cd backend
.venv/bin/python -m pytest -q
cd ../frontend
npx playwright install chromium
npm run test:requirements

Браузерный прогон собирает сайт и запускает отдельные API и preview на портах 8002 и 4174; они должны быть свободны. Аккаунты создаются только во временной тестовой базе. На Linux Chromium может потребовать системные зависимости: npx playwright install-deps chromium.

Результаты сверки с требованиями, замеры скорости и границы проверки собраны в отчёте по требованиям. Отчёты этапа 7, аккаунтов и Docker описывают предыдущие прогоны. Их показатели относятся к соответствующим версиям и локальному окружению, а не к скорости будущего облачного размещения.

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

  • Основной источник: предоставленный hackathon dataset anonymized .csv — 66 анонимизированных профилей, 17 категорий. Исходный файл сохранён; команда не добавляла профили в рабочий каталог.
  • В исходном наборе 13 синтетических профилей, 8 дополненных городов и 18 дополненных цен. Флаг synthetic не означает, что запись добавила команда: происхождение показывается отдельно.
  • CSV содержит полные описания и занятые даты. HTML-превью служит справочным материалом; сокращённые описания и сводные числа из него не подменяют исходные данные.
  • Импорт поддерживает CSV и JSONL. Производный data/derived/catalog.jsonl — альтернативное представление того же каталога; одновременно загружать оба файла не нужно.
  • backend/app/data/profile_facts.json содержит разметку оснований и предупреждений, проверяемую относительно исходных описаний. Подробности происхождения — в описании каталога.
  • Собственный HTTP API связывает сайт и FastAPI. Supabase, NVIDIA API и другие внешние сервисы не вызываются; отправки профилей в модель нет. SQLite используется только для аккаунтов и сессий.

Ограничения

  • Календарь фиксирован: 23.09.2026–31.12.2026 включительно. Доступность известна только по датасету и не подтверждается подрядчиком в реальном времени.
  • Цена «от» не равна окончательной стоимости. max_hours = null означает, что услуга не привязана к присутствию и фильтр часов не применяется; это не обещание неограниченной работы.
  • Объяснения ограничены фактами набора. Для нового или изменённого описания может потребоваться обновить разметку; если данных для отличия профилей недостаточно, сервис сообщает об этом, а не придумывает преимущество.
  • Каталог загружается в память при старте API. Нет автоматической синхронизации календарей, редактирования каталога через сайт или подключения к облачной базе.
  • Нет бронирования, заявок, оплаты и уведомлений подрядчикам. Аккаунты не сохраняют историю подборов; город профиля не подставляется в форму. Подтверждение email, восстановление/смена пароля и удаление аккаунта не реализованы.
  • Проверки выполняются локально на небольшом каталоге; высокая параллельная нагрузка, публичная сеть и все семейства браузеров не проверены. Нативный запуск на Windows не проверялся.

Deployed-версия

Публичная deployed-ссылка не настроена. Для живой проверки используйте локальный запуск через Docker: http://localhost:8080. Этот адрес доступен на вашем компьютере; текущий Compose публикует порт только на 127.0.0.1. Порядок настройки HTTPS и внешнего доступа описан в docs/docker.md.

Дополнительная документация

Алгоритм · Каталог · API · OpenAPI · Docker · Аккаунты · Демонстрация · Проверка требований · История этапов

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

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

Akira-vs96 8AASSAA17 5

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

  • 2026-09-23 17:53the endAkira-vs96
  • 2026-09-23 17:38Enable automatic filters and fix interface controlsAASSAA17
  • 2026-09-23 17:38Present AI-ANA problem, solution, and setup in READMEAASSAA17
  • 2026-09-23 17:02Document fresh-clone SQLite setup and verify Docker persistenceAASSAA17
  • 2026-09-23 16:53Add account authentication and Docker Compose deploymentAASSAA17
  • 2026-09-23 16:266Akira-vs96
  • 2026-09-23 16:26Finalize project documentation and handoffAkira-vs96
  • 2026-09-23 16:07Add original task materials and workspace configurationArsen Aitkazinov
Все коммиты (13)
  • 2026-09-23 16:066Akira-vs96
  • 2026-09-23 16:00q1Akira-vs96
  • 2026-09-23 15:58dAkira-vs96
  • 2026-09-23 14:34filesAkira-vs96
  • 2026-09-23 14:331commitAkira-vs96
Структура репозитория (186 файлов)
.dockerignore
.env.example
.gitignore
HackAlem AI_ Хакатон-задача_ умный подбор подрядчиков.docx
HackAlem AI_ Хакатон-задача_ умный подбор подрядчиков.pdf
README.md
backend/.env.example
backend/Dockerfile
backend/app/__init__.py
backend/app/api_models.py
backend/app/auth.py
backend/app/catalog.py
backend/app/cli.py
backend/app/data/profile_facts.json
backend/app/explanations.py
backend/app/export_contract.py
backend/app/export_examples.py
backend/app/main.py
backend/app/match_cli.py
backend/app/matching.py
backend/app/matching_models.py
backend/app/models.py
backend/pyproject.toml
backend/requirements.txt
backend/tests/fixtures/README.md
backend/tests/fixtures/catalog_valid.csv
backend/tests/fixtures/catalog_valid.jsonl
backend/tests/test_auth.py
backend/tests/test_catalog.py
backend/tests/test_explanations.py
backend/tests/test_match_cli.py
backend/tests/test_matching.py
backend/tests/test_matching_determinism.py
backend/tests/test_recommendations_api.py
backend/tests/test_server.py
compose.yaml
data/derived/catalog.jsonl
data/derived/catalog.jsonl.meta.json
docs/account-check-2026-09-23/ai-ana-account-320-submit.png
docs/account-check-2026-09-23/ai-ana-account-320.png
docs/account-check-2026-09-23/ai-ana-account-profile-desktop.png
docs/account-check-2026-09-23/ai-ana-account-register-desktop.png
docs/account-check-2026-09-23/matching-browser-report.json
docs/accounts.md
docs/api-examples/corporate-host-next-date.json
docs/api-examples/corporate-host.json
docs/api-examples/minimum-duration.json
docs/api-examples/national-ensemble.json
docs/api-examples/no-category.json
docs/api-examples/no-matches.json
docs/api-examples/rare-category.json
docs/api-examples/venue-busy.json
docs/api-examples/venue-free.json
docs/api.md
docs/audit.md
docs/catalog-conversion.json
docs/catalog-report.json
docs/catalog.md
docs/cloud-csv-advice.md
docs/demo-guide.md
docs/design.md
docs/docker.md
docs/handoff.md
docs/interface-check.md
docs/matching-report.json
docs/matching.md
docs/openapi.json
docs/progress.md
docs/queries/corporate-host-next-date.json
docs/queries/corporate-host.json
docs/queries/national-ensemble.json
docs/queries/no-category.json
docs/queries/no-matches.json
docs/queries/rare-category.json
docs/queries/venue-busy.json
docs/queries/venue-free.json
docs/recheck-2026-09-23/README.md
docs/recheck-2026-09-23/stage7-browser-report.json
docs/recheck-2026-09-23/stage7-corporate-host-next-date.png
docs/recheck-2026-09-23/stage7-corporate-host.png
docs/recheck-2026-09-23/stage7-no-matches.png
docs/recheck-2026-09-23/stage7-rare-category.png
docs/recheck-2026-09-23/stage7-venue-busy.png
docs/recheck-2026-09-23/stage7-venue-free.png
docs/requirements-browser-report.json
docs/screenshots/appearance-green.png
docs/screenshots/filters-1440.png
docs/screenshots/filters-390.png
docs/screenshots/prototype-1440.png
docs/screenshots/prototype-320.png
docs/screenshots/prototype-390.png
docs/screenshots/prototype-768.png
docs/screenshots/requirements-1440.png
docs/screenshots/requirements-320.png
docs/screenshots/requirements-390.png
docs/screenshots/requirements-768.png
docs/screenshots/requirements-category_unavailable-320.png
docs/screenshots/requirements-category_unavailable-390.png
docs/screenshots/requirements-corporate-host-next-date.png
docs/screenshots/requirements-corporate-host.png
docs/screenshots/requirements-initial-390.png
docs/screenshots/requirements-loading-390.png
docs/screenshots/requirements-national-ensemble.png
docs/screenshots/requirements-no-category.png
docs/screenshots/requirements-no-matches.png
docs/screenshots/requirements-no_matches-320.png
docs/screenshots/requirements-no_matches-390.png
docs/screenshots/requirements-rare-category.png
docs/screenshots/requirements-timeout-390.png
docs/screenshots/requirements-venue-busy.png
docs/screenshots/requirements-venue-free.png
docs/screenshots/stage6-1440.png
docs/screenshots/stage6-320.png
docs/screenshots/stage6-390.png
docs/screenshots/stage6-768.png
docs/screenshots/stage7-1440.png
docs/screenshots/stage7-320.png
docs/screenshots/stage7-390.png
docs/screenshots/stage7-768.png
docs/screenshots/stage7-category_unavailable-320.png
docs/screenshots/stage7-category_unavailable-390.png
docs/screenshots/stage7-corporate-host-next-date.png
docs/screenshots/stage7-corporate-host.png
docs/screenshots/stage7-initial-390.png
docs/screenshots/stage7-loading-390.png
docs/screenshots/stage7-no-category.png
docs/screenshots/stage7-no-matches.png
docs/screenshots/stage7-no_matches-320.png
docs/screenshots/stage7-no_matches-390.png
docs/screenshots/stage7-rare-category.png
docs/screenshots/stage7-timeout-390.png
docs/screenshots/stage7-venue-busy.png
docs/screenshots/stage7-venue-free.png
docs/screenshots/stage8-restart.png
docs/stage7-browser-report.json
docs/stage7-visual-report.json
docs/stage8-links-report.json
docs/stage8-run-report.json
docs/verification-requirements.md
docs/verification-stage7.md
frontend/Dockerfile
frontend/components.json
frontend/index.html
frontend/nginx.conf
frontend/package-lock.json
frontend/package.json
frontend/playwright.config.ts
frontend/public/brand/favicon.svg
frontend/public/brand/logo.jpeg
frontend/src/App.tsx
frontend/src/components/Account.tsx
frontend/src/components/AppearanceSettings.tsx
frontend/src/components/RecommendationCard.tsx
frontend/src/components/brand/BrandLogo.tsx
frontend/src/components/ui/button.tsx
frontend/src/config/brand.ts
frontend/src/lib/api/auth.ts
frontend/src/lib/api/client.ts
frontend/src/lib/api/generated.ts
frontend/src/lib/api/response-schema.ts
frontend/src/lib/matching-form.ts
frontend/src/lib/use-matching.ts
frontend/src/lib/utils.ts
frontend/src/main.tsx
frontend/src/styles/account.css
frontend/src/styles/tokens.css
frontend/src/vite-env.d.ts
frontend/tests/artifacts.ts
frontend/tests/auth.spec.ts
frontend/tests/controls.spec.ts
frontend/tests/demonstration.spec.ts
frontend/tests/filters.spec.ts
frontend/tests/prototype.spec.ts
frontend/tests/requirements.spec.ts
frontend/tests/resilience.spec.ts
frontend/tsconfig.json
frontend/vite.config.ts
hack-3c47c506-ai-ana.code-workspace
hackathon dataset anonymized .csv
hackathon dataset preview.html
logo.jpeg
outputs/contractor-agent-brief.zip
outputs/dataset-review-ru.md
outputs/prompt-contractor-matching-agent-ru.md
outputs/stage-1-plan-ru.md
prompt-contractor-matching-agent-ru.md

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

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

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