AAYMA
RU
Войти

Умный подбор подрядчиков (Firebird MVP)

Двуязычный (каз./рус./англ.) сервис подбора до 3 подрядчиков для мероприятия с фактическими объяснениями, тремя состояниями выдачи (found…

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

О проекте

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

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

Суть

Двуязычный (каз./рус./англ.) сервис подбора до 3 подрядчиков для мероприятия с фактическими объяснениями, тремя состояниями выдачи (found, no_category, no_match) и списком причин, почему остальные не показаны.

Как устроено

Express API читает CSV-каталог. Жёсткие фильтры в коде: город, категория, дата, бюджет (цена не выше бюджета), формат, язык, длительность. Прошедшие фильтр сортируются по форматным словам в описании, при равенстве по ID. AI получает один batch-запрос на выбранные карточки и выбирает только индексы заранее подготовленных фрагментов и фактов, текст собирает сервер. Цепочка провайдеров: OpenAI, затем NVIDIA NIM, затем фактический шаблон, с таймаутами 1,8 с и 3,6 с и JSON-кэшем.

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

  • детерминированный matcher
  • LLM выбирает индексы фактов
  • каскад провайдеров с таймаутами
  • кэш AI-ответов
  • OpenAI tool agent вокруг matcher
  • i18n kk/ru/en

Особенности

Защита от галлюцинаций: модель не пишет текст, а только выбирает индексы заранее подготовленных фрагментов профиля и фактов. Сервер проверяет ID и индексы, так что новые цены или даты модель добавить не может. Строгие бюджеты времени на провайдера и безключевой fallback. README основной частью на казахском.

Стек

React · TypeScript · Vite · Tailwind CSS · Three.js · Node.js · Express · Zod · csv-parse

Модели

OpenAI · NVIDIA NIM


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

README

README.md

Мердігерлерді ақылды іріктеу / Умный подбор подрядчиков

HackAlem AI · #79-lite · Қазақстан / Казахстан

Іс-шараға сай 3-ке дейін мердігер және әрқайсысына нақты дерекке негізделген түсіндірме. До 3 подрядчиков для мероприятия с объяснением выбора на основе данных каждого профиля.

Қазақша · Русский

Қазақша

Бізге берілген тапсырма

Хакатон тапсырмасы — «мердігерлерді ақылды іріктеу» сервисін жасау. Клиент іс-шараның қаласын, күнін, түрін, мердігер санатын және теңгемен бюджетін енгізеді. Қажет болса, ұзақтығын және мердігердің жұмыс тілін көрсетеді. Сервис осы шарттарға сай ең көбі 3 карточка қайтарады.

Негізгі мақсат — каталогты жай көрсету емес, «неге осы мердігер менің іс-шарама сәйкес?» деген сұраққа жауап беру. Әр карточкада атау, санат, қала, «бастап» бағасы және профиль фактілеріне сүйенген 1–2 сөйлемдік түсіндірме болуы керек. Жалпылама мақтау, жұлдызша, ойдан шығарылған рейтинг немесе сәйкестік пайызы қолданылмайды. Сирек санат пен бос нәтиже де адал түсіндірілуі тиіс.

Тапсырманы қалай орындадық

Жобаны екі адамнан тұратын команда болып әзірледік. Жұмысты frontend және backend бағыттарына бөліп, ортақ TypeScript контракт арқылы біріктірдік.

БағытОрындалған жұмыс
FrontendІздеу формасы, карточкалар, үш нәтиже күйі, сұраныс қорытындысы, күн мен бюджетті жылдам өзгерту, сүзгілерді алып тастау, қазақша/орысша/ағылшынша интерфейс, қателерді көрсету және қайта іздеу
BackendCSV каталогын оқу және тексеру, API, бос күн/бюджет/формат/тіл/ұзақтық сүзгілері, тұрақты сұрыптау, фактілік түсіндірмелер, AI провайдерлері мен резервтік жауап, автоматты тесттер
Бірлескен жұмысshared/types.ts және shared/api.md келісімі, моктардан нақты API-ге өту, интеграцияны тексеру, күн ауыстыру және бос нәтиже демосын дайындау

Алдымен толық MVP жолын жасадық: форма → сұраныс → іріктеу → түсіндірмесі бар нәтиже. Backend дайын болғанға дейін frontend синтетикалық моктармен жұмыс істеді. Кейін нақты каталогқа қостық, үш тілдегі мәтіндерді, күн мен баға пішімдерін түзеттік және интерфейсті жаңарттық. Әзірлеу мен тексеруде Codex көмекшісі пайдаланылды.

Қазір не жұмыс істейді

  • Міндетті өрістер: қала, іс-шара күні, формат, мердігер санаты, бюджет. Қосымша өрістер: сағат саны және жұмыс тілі.
  • Каталог мәндері backend-тен алынады. Экрандағы аударма API-ге жіберілетін бастапқы мәнді өзгертпейді.
  • Карточкаларда нақты профиль үзіндісі, баға мен бюджет арақатынасы, күннің бос болуы және берілген қосымша шарттар көрсетіледі.
  • Нәтиже үстінде сұраныс қорытындысы бар: күнді немесе бюджетті өзгертіп, қайта іздеуге болады.
  • Карточкаға кірмеген мердігерлердің себептері бөлек ашылатын тізімде көрсетіледі: бос емес, бюджеттен жоғары, формат/тіл/ұзақтық сәйкес емес немесе алғашқы үштікке кірмеді.
  • Интерфейс пен түсіндірмелер қазақша, орысша және ағылшынша. Тіл таңдауы браузерде сақталады; профильден алынған үзінді орысша түпнұсқада қалады.
  • dataFlags бар карточкада синтетикалық профиль, толықтырылған қала немесе толықтырылған баға белгісі көрінеді.
API күйіПайдаланушы не көреді
foundШарттардан өткен 1–3 карточка және нәтиже туралы түсіндірме
no_categoryТаңдалған қалада осы санаттағы профиль жоқ деген хабар
no_matchҚала мен санатта профильдер бар, бірақ шарттарға сәйкес келмегені және себептері туралы хабар

candidatesBeforeCut — барлық сүзгіден өткен, алғашқы үштікке дейінгі кандидаттар саны. Бұл қаладағы барлық мердігер саны емес. no_match кезінде ол 0 болса, интерфейс артық нөлдік жолды көрсетпейді. Бір профиль бірнеше себеппен өтпеуі мүмкін, сондықтан себептердегі сандарды қосып, адам саны деп есептеуге болмайды.

Іріктеу және AI қалай жұмыс істейді

React интерфейсі :5173
        │ /api сұранысы, Vite proxy
        ▼
Express API :8787
        │
        ├─ CSV каталогы → сұранысты тексеру
        ├─ Қала + санат → күн, бюджет, формат, тіл, ұзақтық сүзгілері
        ├─ Сипаттаманың форматқа қатыстылығы → тең болса ID → ең көбі 3 карточка
        └─ Профиль үзіндісі + тексерілген фактілер → түсіндірме → нәтиже

Қатаң сүзгілерді код орындайды. Баға тек шек ретінде қолданылады: priceFromKzt <= budgetKzt; арзан не қымбат болуы сапа бағасы емес. Өткен профильдер сипаттамадағы форматқа қатысты сөз топтары бойынша реттеледі, тең болса ID шешеді. Бірдей сұраныс бірдей карточкаларды бірдей ретпен береді. Интерфейс тілі іріктеуге әсер етпейді.

AI қосылса, таңдалған карточкалардың бәріне бір batch-сұраныс жіберіледі. Модель тек алдын ала дайындалған профиль үзіндісі мен факті сөйлемінің индексін таңдайды. Сервер ID мен индекстерді тексеріп, мәтінді өзі құрастырады: модель жаңа баға, бос күн немесе тәжірибе қоса алмайды.

Тізбек: OpenAI → NVIDIA → фактілік шаблон. Әр провайдерге 1,8 секундқа дейін, жалпы AI бөлігіне 3,6 секунд уақыт шегі берілген. Жарамды AI таңдаулары жергілікті кэшке сақталады. Кілт жоқ болса немесе провайдер істемесе, іріктеу мен түсіндірме шаблон арқылы жұмысын жалғастырады. Қазылар демосына API кілті міндетті емес.

Технологиялар және деректер

БөлікТехнология
FrontendReact, TypeScript, Vite, Tailwind CSS
Интерфейс элементтеріMotion, Lucide, Three.js, DiceBear Avataaars
BackendNode.js, Express, TypeScript, Zod, csv-parse
AI интеграциясыOpenAI SDK, NVIDIA-ның OpenAI-үйлесімді API-і
Ортақ келісімshared/types.ts, shared/api.md
Дерек және сақтауРепозиторийдегі CSV; AI таңдауларына жергілікті JSON кэш; бөлек дерекқор қажет емес

Каталог: 66 профиль, 3 қала мәні, 17 санат, 6 іс-шара форматы. Қалалар: Алматы, Астана, Шетел (Зарубежье). Файл — backend/data/contractors.csv; оны бөлек жүктеу қажет емес. Деректе 13 синтетикалық профиль, 8 толықтырылған қала және 18 толықтырылған баға бар; белгілер бір профильде қатар болуы мүмкін. Қазіргі күнтізбе 2026-09-23–2026-12-31 аралығымен шектелген.

Қазыларға: жобаны іске қосу

Керек: Node.js 24, npm және репозиторий көшірмесі. Тексерілген орта: Node.js 24.21.0, npm 11.19.0. Алғашқы npm ci үшін интернет керек. Тәуелділіктер орнатылғаннан кейін кілтсіз негізгі демо жергілікті жұмыс істейді.

1. Репозиторийді алу — терминалда:

git clone https://github.com/BAITC-Hacks/hack-96ac4fd9-7-s-ai.git
cd hack-96ac4fd9-7-s-ai

ZIP жүктелсе, оны ашып, терминалды frontend/, backend/, shared/ орналасқан түбірлік папкадан бастаңыз. Келесі екі терминал да осы түбірден басталады. Түбірде package.json жоқ: тәуелділіктер әр бөлікте бөлек орнатылады.

2. Бірінші терминал — backend:

cd backend
npm ci
npm start

Терминалды ашық қалдырыңыз. Backend: http://localhost:8787/api/health. Дұрыс жауап:

{"ok":true,"data":{"status":"up"}}

3. Екінші терминал — frontend, Windows PowerShell:

cd frontend
npm ci
$env:VITE_USE_MOCKS = 'false'
npm run dev

macOS/Linux үшін сол қадам:

cd frontend
npm ci
VITE_USE_MOCKS=false npm run dev

4. Браузерде http://localhost:5173 ашыңыз. index.html файлын екі рет басып ашудың қажеті жоқ: интерфейс Vite сервері арқылы жұмыс істейді. Екі сервер де бір компьютерде іске қосылуы керек. Тоқтату үшін әр терминалда Ctrl+C басыңыз.

VITE_USE_MOCKS=false нақты backend пен CSV каталогын қосады. Бұл мән берілмесе, dev режимі әдепкіде синтетикалық моктарды қолданады; бетте демо белгісі көрінеді. Режимді өзгерткеннен кейін Vite-ті қайта іске қосыңыз. Кілттер мен .env жасау негізгі іске қосуға қажет емес.

Қазыларға: 3 минуттық демо

Төмендегі сценарийлер нақты backend және репозиторийдегі CSV үшін, моктарға арналмаған. Негізгі сұраныс: Алматы · 2026-10-06 · Корпоратив · Жүргізуші (Ведущий) · 1 000 000 ₸ · 5 сағат · жұмыс тілі Орысша (ru). Интерфейс тілін Қазақша етіп қалдыруға болады.

ҚадамӘрекетКүтілетін нәтиже
1Негізгі сұранысты жіберуfound: 6 кандидат өтті, 3 карточка көрсетілді: HK-44733, HK-88430, HK-35215
2Қорытындыда тек күнді 2026-10-01 ету2 карточка: HK-88430, HK-44923; хабарда сол күні бос емес 6 профиль көрсетіледі
3Қазақша → Русский → English ауыстыруКарточкалардың ID-і мен реті сақталады; интерфейс пен түсіндірме тілі өзгереді
4Алматы · 2026-10-04 · Корпоратив · Флорист · 300 000 ₸ · 24 сағат; жұмыс тілін бос қалдыруСирек нәтиже: 1 карточка, HK-39372
5Негізгі сұранысқа оралып, Астана + Безендіруші (Декоратор) таңдауno_category: осы қалада бұл санат жоқ
6Негізгі сұранысқа оралып, тек бюджетті 1 000 ₸ етуno_match: қала мен санатта 10 профиль бар, бірақ ешқайсысы бюджетке сай емес

Басты сәт — 1 → 2: күн өзгергенде карточкалардың құрамы өзгереді, ал хабар мен ашылатын тізім кімнің неге көрсетілмегенін түсіндіреді. Тек жоғалған карточкаға қарап себеп болжамаймыз — себеп backend-тен келеді.

API қысқаша

Әдіс және жолМақсаты
GET /api/healthСервердің жұмысын тексеру
GET /api/catalog-optionsҚала, санат, формат және жұмыс тілі нұсқалары
POST /api/matchМердігерлерді іріктеу

Негізгі демоға арналған JSON body:

{
  "city": "Алматы",
  "eventDate": "2026-10-06",
  "eventType": "корпоратив",
  "category": "Ведущий",
  "budgetKzt": 1000000,
  "durationHours": 5,
  "language": "ru",
  "locale": "kz"
}

language — мердігердің жұмыс тіліне сүзгі; locale — жауап мәтінінің тілі. API қазақша үшін kz, ал frontend ішкі локалі kk қолданады. Күн YYYY-MM-DD, бюджет оң бүтін сан болуы керек. Сәтті жауап {ok:true,data:...}, қате {ok:false,error:{code,message}} түрінде беріледі. Бос іріктеу — HTTP 200; жарамсыз сұраныс — HTTP 400. Толық келісім: shared/api.md, типтер: shared/types.ts.

Тексеру және жинақтау

Репозиторий түбірінен:

npm --prefix backend run typecheck
npm --prefix backend test
npm --prefix frontend run typecheck
npm --prefix frontend run check
npm --prefix frontend run build

Нақты backend 8787 портында жұмыс істеп тұрғанда, түбірден бөлек терминалда:

cd frontend
node -e "require('node:fs').mkdirSync('.status', { recursive: true })"
node scripts/checkLiveBackend.mjs

Backend-тің 21 автоматты тесті сүзгілерді, тұрақты ретті, күн ауысуын, бос нәтижелерді, валидацияны, тілдерді және AI қатесі кезіндегі резервтік жауапты қамтиды. Frontend тексерістері моктарды, React SSR нәтижелерін, клиент келісімін және аудармаларды тексереді. checkLiveBackend.mjs нақты Vite /api проксиі арқылы API-ге қосылады; есебі frontend/.status/t14-backend-check.json файлына жазылады. Бұл тексеріс браузердегі басу/енгізу әрекеттерін алмастырмайды.

Соңғы тексеруде typecheck, frontend check/build, backend-тің 21 тесті және нақты API интеграциясы өтті. Жинақтау кезінде ірі JS-чанк туралы ескерту бар, build сәтті аяқталады. Қазіргі README-дегі бес негізгі API сценарийі қайта тексерілді.

Production файлдары frontend/dist/ ішіне жиналады. Серверге орналастырғанда /api маршрутын backend-ке бағыттау қажет; бір dist папкасын жариялау API-ді іске қоспайды. Хакатон демосы үшін жоғарыдағы екі терминалды тәсіл жеткілікті.

Deploy: қысқаша нұсқаулық

Бір Linux VPS-та, бір доменмен орналастыру жоспары:

  1. Серверге Node.js 24 және Nginx орнатып, репозиторийді толық көшіріңіз. shared/ және backend/data/contractors.csv файлдары да қажет.
  2. backend/ ішінде npm ci --include=dev орындап, npm start процесін systemd қызметі арқылы тұрақты іске қосыңыз. tsx dev dependency болғандықтан, оны орнатудан алып тастамаңыз. Backend осы сервердің 127.0.0.1:8787 адресінде жұмыс істейді.
  3. frontend/ ішінде npm ci --include=dev, содан кейін VITE_USE_MOCKS=false npm run build орындаңыз. Жарияланатын файлдар — frontend/dist/.
  4. Nginx-тің root мәнін frontend/dist/ абсолют жолына бағыттаңыз; / үшін try_files $uri $uri/ /index.html;, /api/ үшін proxy_pass http://127.0.0.1:8787; қолданыңыз. proxy_pass адресінің соңына / қоспаңыз: backend-ке /api/... жолы толық жетуі керек. Vite dev proxy production-да жұмыс істемейді.
  5. Доменді сервер IP-іне бағыттап, HTTPS сертификатын қосыңыз. Сыртқа 80/443 порттарын ашу жеткілікті; 8787 ішкі болып қалады. AI кілттері қажет болса, оларды тек backend ортасында сақтаңыз.
  6. Деплойдан кейін https://<домен>/api/health, басты бет және күн ауыстыру демосын тексеріңіз. Жаңартқанда frontend-ті қайта жинап, backend қызметін қайта іске қосыңыз.

Бұл — орналастыру нұсқаулығы; дайын жарияланған сайттың адресі емес.

Қосымша AI баптауы

Нақты модельді пайдаланғыңыз келсе, backend ортасына OPENAI_API_KEY және/немесе NVIDIA_API_KEY беріңіз. NVIDIA_BASE_URL әдепкіде https://integrate.api.nvidia.com/v1. Айнымалылар үлгісі — .env.example. Backend алдымен backend/.env, кейін түбірдегі .env файлын оқиды; процесс ортасындағы мәндер басым. Кілттерді frontend-ке немесе Git-ке қоспаңыз.

Кодтағы модельдер: gpt-4.1-mini-2025-04-14 және meta/llama-3.3-70b-instruct. Тіркелгінің қолжетімділігі мен квотасына қарай нақты модель шақыруын бөлек тексеру керек; автоматты тесттер провайдер адаптерлерін алмастырылған transport арқылы тексереді. Негізгі демо бұл сыртқы сервистерге тәуелді емес.

Егер қосылмаса

МәселеШешім
npm немесе node табылмайдыNode.js 24 орнатылғанын тексеріп, терминалды қайта ашыңыз
PowerShell npm.ps1 іске қоспайдыКомандаларда npm орнына npm.cmd қолданыңыз
package.json табылмайдыnpm ci командасын backend/ немесе frontend/ ішінде орындаңыз
Каталог жүктелмейді / API-ге қосылмайдыBackend терминалы мен /api/health жауабын тексеріңіз; порт 8787 болуы керек
Бетте синтетикалық демо белгісі барVITE_USE_MOCKS=false беріп, frontend серверін қайта іске қосыңыз
5173 немесе 8787 порты бос емесСол порттағы алдыңғы жоба серверін өз терминалында Ctrl+C арқылы тоқтатыңыз
Күн немесе сұраныс қабылданбайды2026-09-23–2026-12-31 аралығын, оң бүтін бюджетті және каталогтағы мәндерді қолданыңыз

Шектеулер

Бұл — берілген CSV негізіндегі хакатон прототипі. Күнтізбе нақты уақытта жаңармайды; деректегі бос күн нақты бронь кепілдігі емес. maxHours=null болса, ұзақтық сүзгісі қолданылмайды: бұл шексіз қызмет туралы уәде емес. Сипаттама — профильдегі мәлімдеме, тәуелсіз расталған ақпарат емес.

Көрсетілетін есімдер мен иллюстрациялық аватарлар ойдан шығарылған. Есімдер ID бойынша тұрақты түрде ауыстырылады; портреттер нақты адамдардың фотосы емес. Сұрыптау сөздік эвристикаға сүйенеді және мағынаны толық түсінбейді. Брондау, төлем, жеке кабинет және мердігерге хабарлама жіберу іске асырылмаған.

Русский

Какую задачу мы получили

Задание хакатона — создать сервис «умного подбора подрядчиков». Клиент указывает город, дату, тип мероприятия, категорию подрядчика и бюджет в тенге. Дополнительно можно задать длительность и рабочий язык подрядчика. Сервис возвращает не более 3 карточек.

Главная ценность — объяснить «почему этот подрядчик подходит именно для моего мероприятия?». Карточка должна содержать название, категорию, город, цену «от» и 1–2 предложения на основе фактов профиля. Общая похвала, звёзды, выдуманный рейтинг и процент совпадения не используются. Редкие категории и отсутствие результатов должны обрабатываться честно и понятно.

Как мы выполнили задачу

Мы разработали проект командой из двух человек, разделив работу на frontend и backend и объединив их через общий TypeScript-контракт.

НаправлениеЧто реализовано
FrontendФорма поиска, карточки, три состояния результата, сводка запроса, быстрое изменение даты и бюджета, удаление фильтров, интерфейс на трёх языках, ошибки и повтор поиска
BackendЧтение и проверка CSV, API, фильтры доступности/бюджета/формата/языка/длительности, стабильная сортировка, фактические объяснения, AI-провайдеры и резервный ответ, автоматические тесты
Совместная работаКонтракт shared/types.ts и shared/api.md, переход от моков к реальному API, проверка интеграции, подготовка демо со сменой даты и пустыми результатами

Сначала собрали полный MVP-путь: форма → запрос → подбор → результат с объяснениями. Пока backend разрабатывался, frontend работал на синтетических моках. Затем подключили реальный каталог, исправили локализацию, даты и суммы, обновили интерфейс. При разработке и проверке использовали помощника Codex.

Что умеет приложение

  • Принимает обязательные параметры: город, дату, формат, категорию и бюджет; дополнительные — количество часов и рабочий язык.
  • Получает варианты формы из backend. Перевод подписи на экране не меняет исходное значение, отправляемое в API.
  • Показывает выдержку из профиля и проверенные факты: свободную дату по данным каталога, отношение цены к бюджету и дополнительные условия запроса.
  • Позволяет менять дату и бюджет прямо в сводке над результатами и повторять поиск.
  • В раскрываемом списке объясняет, почему остальные кандидаты не показаны: заняты, дороже бюджета, не подходят по формату/языку/длительности или не вошли в первые три.
  • Поддерживает казахский, русский и английский языки интерфейса и объяснений. Выбор языка сохраняется в браузере; цитата из профиля остаётся на русском языке оригинала.
  • Показывает dataFlags: синтетический профиль, восстановленный город или восстановленная цена.
Статус APIЧто видит пользователь
foundОт 1 до 3 подходящих карточек и пояснение результата
no_categoryВ выбранном городе нет профилей этой категории
no_matchПрофили в городе и категории есть, но условия не прошёл никто; указаны причины

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

Алгоритм и роль AI

React :5173 → /api через Vite proxy → Express :8787
                                          │
                         CSV → валидация → строгие фильтры
                                          │
                         релевантность описания → ID → до 3 карточек
                                          │
                         выдержка из профиля + факты → объяснение

Строгие фильтры выполняет код: город, категория, занятость на дату, формат, priceFromKzt <= budgetKzt, а при наличии — рабочий язык и длительность. Цена является порогом, а не оценкой качества. Прошедшие профили сортируются по группам слов в описании, связанным с форматом, при равенстве — по ID. Одинаковый запрос даёт одинаковые карточки в одинаковом порядке; язык интерфейса на отбор не влияет.

Если AI подключён, все выбранные профили отправляются одним batch-запросом. Модель выбирает только индексы готовой выдержки и предложения с проверенными фактами. Сервер проверяет ID и индексы, затем собирает текст сам. Новую цену, доступность или опыт модель добавить не может.

Цепочка: OpenAI → NVIDIA → фактический шаблон. Лимит каждого провайдера — до 1,8 секунды, всей AI-части — 3,6 секунды. Валидные выборы сохраняются в локальном кэше. Без ключей или при ошибке провайдера подбор продолжает работать с шаблонным объяснением. Для демонстрации жюри API-ключи не обязательны.

Стек и данные

ЧастьТехнология
FrontendReact, TypeScript, Vite, Tailwind CSS
Элементы интерфейсаMotion, Lucide, Three.js, DiceBear Avataaars
BackendNode.js, Express, TypeScript, Zod, csv-parse
AIOpenAI SDK и совместимый API NVIDIA
Общий контрактshared/types.ts, shared/api.md
ХранениеCSV в репозитории и локальный JSON-кэш AI; отдельная база данных не нужна

Каталог содержит 66 профилей, 3 значения города, 17 категорий и 6 форматов мероприятий. Города: Алматы, Астана, Зарубежье. Датасет уже включён в backend/data/contractors.csv, отдельно скачивать его не нужно. В нём 13 синтетических профилей, 8 восстановленных городов и 18 восстановленных цен; признаки могут пересекаться. Доступный диапазон дат: 2026-09-23–2026-12-31.

Для жюри: запуск проекта

Требуется: Node.js 24, npm и копия репозитория. Проверенная среда: Node.js 24.21.0, npm 11.19.0. Первое выполнение npm ci требует интернета. После установки зависимостей основной режим без ключей работает локально.

1. Скачайте репозиторий:

git clone https://github.com/BAITC-Hacks/hack-96ac4fd9-7-s-ai.git
cd hack-96ac4fd9-7-s-ai

Можно скачать ZIP, распаковать и открыть терминал в корневой папке с frontend/, backend/, shared/. Каждый из следующих двух терминалов начинает работу из этой папки. В корне нет package.json: зависимости устанавливаются отдельно для frontend и backend.

2. Первый терминал — backend:

cd backend
npm ci
npm start

Оставьте терминал открытым. Проверьте http://localhost:8787/api/health. Ожидается:

{"ok":true,"data":{"status":"up"}}

3. Второй терминал — frontend, Windows PowerShell:

cd frontend
npm ci
$env:VITE_USE_MOCKS = 'false'
npm run dev

Для macOS/Linux:

cd frontend
npm ci
VITE_USE_MOCKS=false npm run dev

4. Откройте http://localhost:5173 в браузере. Не запускайте index.html двойным щелчком: приложению нужен сервер Vite. Оба сервера должны работать на одном компьютере. Для остановки нажмите Ctrl+C в каждом терминале.

VITE_USE_MOCKS=false включает настоящий backend и CSV-каталог. Без этой настройки dev-режим по умолчанию использует синтетические моки, о чём сообщает плашка на странице. После изменения режима перезапустите Vite. Для основного запуска создавать .env и получать ключи не требуется.

Для жюри: демо за 3 минуты

Сценарии относятся к реальному backend и CSV из репозитория, не к мокам. Базовый запрос: Алматы · 2026-10-06 · корпоратив · Ведущий · 1 000 000 ₸ · 5 часов · рабочий язык Русский (ru). Язык интерфейса можно выбрать любой.

ШагДействиеОжидаемый результат
1Отправить базовый запросfound: прошли 6 кандидатов, показаны 3: HK-44733, HK-88430, HK-35215
2В сводке изменить только дату на 2026-10-012 карточки: HK-88430, HK-44923; сообщение о 6 занятых профилях на эту дату
3Переключить Қазақша → Русский → EnglishID и порядок карточек сохраняются, меняется язык интерфейса и объяснений
4Алматы · 2026-10-04 · корпоратив · Флорист · 300 000 ₸ · 24 часа; рабочий язык оставить пустымРедкая категория: 1 карточка, HK-39372
5Вернуться к базовому запросу, выбрать Астана + Декораторno_category: в городе нет этой категории
6Вернуться к базовому запросу, изменить только бюджет на 1 000 ₸no_match: в городе и категории есть 10 профилей, но ни один не проходит бюджет

Ключевой момент — переход 1 → 2: состав выдачи меняется, а сообщение и раскрываемый список объясняют, кто и почему не показан. Причину возвращает backend; frontend не угадывает занятость по исчезновению карточки.

Кратко об API

Метод и путьНазначение
GET /api/healthПроверка сервера
GET /api/catalog-optionsГорода, категории, форматы и рабочие языки
POST /api/matchПодбор подрядчиков

JSON body базового демо:

{
  "city": "Алматы",
  "eventDate": "2026-10-06",
  "eventType": "корпоратив",
  "category": "Ведущий",
  "budgetKzt": 1000000,
  "durationHours": 5,
  "language": "ru",
  "locale": "ru"
}

language фильтрует по рабочему языку подрядчика, locale задаёт язык текста ответа. Казахский язык в API обозначается kz, внутренний locale frontend — kk. Дата передаётся как YYYY-MM-DD, бюджет — положительным целым числом. Ответ: {ok:true,data:...} или {ok:false,error:{code,message}}. Пустой подбор возвращает HTTP 200, невалидный запрос — HTTP 400. Полный контракт: shared/api.md, типы: shared/types.ts.

Проверки и сборка

Из корня репозитория:

npm --prefix backend run typecheck
npm --prefix backend test
npm --prefix frontend run typecheck
npm --prefix frontend run check
npm --prefix frontend run build

При работающем backend на порту 8787, из корня в отдельном терминале:

cd frontend
node -e "require('node:fs').mkdirSync('.status', { recursive: true })"
node scripts/checkLiveBackend.mjs

21 автоматический тест backend проверяет фильтры, порядок, смену даты, пустые результаты, валидацию, языки и резервный ответ при сбоях AI. Frontend проверяет моки, React SSR, контракт клиента и переводы. checkLiveBackend.mjs обращается к реальному API через временный Vite /api proxy и сохраняет отчёт в frontend/.status/t14-backend-check.json. Проверка не заменяет интерактивные действия в браузере.

Последняя проверка: typecheck, frontend check/build, 21 тест backend и интеграция с настоящим API прошли. Сборка сообщает о крупных JS-чанках, но завершается успешно. Пять основных API-сценариев из этого README проверены повторно.

Production-сборка создаётся в frontend/dist/. При размещении на сервере маршрут /api нужно направить на backend; публикация одного dist не запускает API. Для хакатонного демо достаточно двух терминалов по инструкции выше.

Deploy: краткая инструкция

Схема размещения на одном Linux VPS с одним доменом:

  1. Установите Node.js 24 и Nginx, скопируйте весь репозиторий. Сохраните shared/ и backend/data/contractors.csv.
  2. В backend/ выполните npm ci --include=dev, затем настройте постоянный запуск npm start через службу systemd. Не исключайте dev dependencies: для запуска нужен tsx. Backend слушает 127.0.0.1:8787 на этом же сервере.
  3. В frontend/ выполните npm ci --include=dev, затем VITE_USE_MOCKS=false npm run build. Публикуемый каталог — frontend/dist/.
  4. В Nginx укажите абсолютный путь к frontend/dist/ как root; для / задайте try_files $uri $uri/ /index.html;, для /api/ — proxy_pass http://127.0.0.1:8787;. Не добавляйте / в конце адреса proxy_pass: backend должен получить полный путь /api/.... Dev proxy Vite в production не работает.
  5. Направьте домен на IP сервера и подключите HTTPS-сертификат. Снаружи достаточно портов 80/443; 8787 остаётся внутренним. Если нужны AI-ключи, храните их только в окружении backend.
  6. После размещения проверьте https://<домен>/api/health, главную страницу и демо со сменой даты. При обновлении пересоберите frontend и перезапустите службу backend.

Это инструкция по размещению, а не адрес уже опубликованного приложения.

Необязательное подключение AI

Передайте в окружение backend OPENAI_API_KEY и/или NVIDIA_API_KEY. Значение NVIDIA_BASE_URL по умолчанию — https://integrate.api.nvidia.com/v1. Шаблон переменных: .env.example. Backend читает backend/.env, затем корневой .env; переменные процесса имеют приоритет. Не добавляйте ключи во frontend или Git.

В коде заданы модели gpt-4.1-mini-2025-04-14 и meta/llama-3.3-70b-instruct. Доступность и квоты конкретного аккаунта требуют отдельной проверки реального вызова; автоматические тесты используют подменённый transport провайдеров. Основное демо не зависит от этих внешних сервисов.

Если что-то не запускается

ПроблемаЧто сделать
Не найдены npm или nodeПроверьте установку Node.js 24 и заново откройте терминал
PowerShell блокирует npm.ps1Используйте npm.cmd вместо npm
Не найден package.jsonВыполняйте npm ci внутри backend/ или frontend/
Не загружается каталог / недоступен APIПроверьте терминал backend и /api/health; сервер должен работать на 8787
На странице показана плашка синтетического демоЗадайте VITE_USE_MOCKS=false и перезапустите frontend
Занят порт 5173 или 8787Остановите предыдущий сервер этого проекта через Ctrl+C в его терминале
Не принимается дата или запросИспользуйте даты 2026-09-23–2026-12-31, положительный целый бюджет и значения каталога

Ограничения

Это хакатонный прототип на предоставленном CSV. Календарь не обновляется в реальном времени; свободная дата в датасете не гарантирует реальную бронь. При maxHours=null фильтр длительности не применяется: это не обещание неограниченного времени работы. Описание — утверждение из профиля, а не независимо проверенный факт.

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

Репозиторий құрылымы / Структура репозитория

frontend/
  src/api/             API клиенті, моктар / API-клиент, моки
  src/components/      Форма, карточкалар, қорытынды / форма, карточки, сводка
  src/lib/             Аудармалар, пішімдеу / переводы, форматирование
  scripts/             Frontend және интеграция тексерістері / проверки
backend/
  src/app.ts           Express маршруттары / маршруты Express
  src/lib/             Каталог, іріктеу, түсіндірме / каталог, подбор, объяснения
  src/ai/              Провайдерлер, валидация, кэш / провайдеры, валидация, кэш
  data/contractors.csv  Бастапқы дерек / исходный датасет
  tests/               Автоматты тесттер / автоматические тесты
shared/
  api.md               API келісімі / контракт API
  types.ts             Ортақ TypeScript типтері / общие TypeScript-типы
scripts/               Командалық құралдар / командные инструменты
README.md              Қазыларға нұсқаулық / инструкция для жюри

Дерек туралы / О данных: backend/data/README.md. Жергілікті .env, node_modules/, dist/ және runtime кэш Git-ке кірмейді / локальные .env, node_modules/, dist/ и runtime-кэш не включаются в Git.

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

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

tasimbetovatabek-byte 13Мейрамбек 9

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

  • 2026-09-23 17:52Add brief bilingual deployment guideМейрамбек
  • 2026-09-23 17:49Document project and jury demo in Kazakh and RussianМейрамбек
  • 2026-09-23 17:39Polish contractor search interface and editable request summaryМейрамбек
  • 2026-09-23 17:15Record verified live OpenAI agent end-to-end resulttasimbetovatabek-byte
  • 2026-09-23 17:14Merge main: keep the React/Node app as the submission, drop the Firebird prototypeМейрамбек
  • 2026-09-23 17:11Localize explanations, add not-shown reasons, Kazakh names and redesigned UIМейрамбек
  • 2026-09-23 17:04Add NVIDIA NIM transport with deterministic agent fallbacktasimbetovatabek-byte
  • 2026-09-23 16:48Handle OpenAI quota errors and retain shared agent deadlinetasimbetovatabek-byte
Все коммиты (22)
  • 2026-09-23 16:37Add grounded OpenAI tool agent around deterministic matchertasimbetovatabek-byte
  • 2026-09-23 16:21Merge preserved team code with Firebird as the primary submissiontasimbetovatabek-byte
  • 2026-09-23 16:21Add researched event planning workspace, team budgets and date radartasimbetovatabek-byte
  • 2026-09-23 16:03Expand Firebird with catalog, profiles, comparison and saved searchestasimbetovatabek-byte
  • 2026-09-23 15:48Build Firebird contractor matching MVP with grounded explanationstasimbetovatabek-byte
  • 2026-09-23 14:46Localize frontend UI into Kazakh, Russian and EnglishМейрамбек
  • 2026-09-23 14:37Add frontend MVP: request form, three result states and contractor cardsМейрамбек
  • 2026-09-23 14:37Merge remote-tracking branch 'origin/main' into frontМейрамбек
  • 2026-09-23 14:34feat(backend): add deterministic matching with batch AI fallbacktasimbetovatabek-byte
  • 2026-09-23 14:05Add hackathon kit with contractor-matching contract and team rolesМейрамбек
  • 2026-09-23 13:05docs: remove local push verification filetasimbetovatabek-byte
  • 2026-09-23 13:04docs: verify push from local computertasimbetovatabek-byte
  • 2026-09-23 13:02docs: remove push verification filetasimbetovatabek-byte
  • 2026-09-23 13:01docs: add push verification filetasimbetovatabek-byte
Структура репозитория (98 файлов)
.env.example
.gitattributes
.gitignore
AGENTS.md
CONVENTIONS.md
HOW-TO.md
README.md
backend/.gitignore
backend/AGENTS.md
backend/CONTRACT_REQUEST.md
backend/README.md
backend/TASKS.md
backend/data/README.md
backend/data/contractors.csv
backend/package-lock.json
backend/package.json
backend/scripts/smoke.ps1
backend/src/ai/cache.ts
backend/src/ai/index.ts
backend/src/ai/nvidia.ts
backend/src/ai/openai.ts
backend/src/ai/provider.ts
backend/src/app.ts
backend/src/index.ts
backend/src/lib/catalog.ts
backend/src/lib/displayNames.ts
backend/src/lib/explanations.ts
backend/src/lib/gender.ts
backend/src/lib/i18n.ts
backend/src/lib/matching.ts
backend/src/lib/validation.ts
backend/tests/ai.test.ts
backend/tests/api.test.ts
backend/tests/catalog.test.ts
backend/tests/matching.test.ts
backend/tsconfig.json
docs/KIT.md
frontend/.gitignore
frontend/AGENTS.md
frontend/CONTRACT_REQUEST.md
frontend/DESIGN.md
frontend/DESIGN_REFERENCES.md
frontend/README.md
frontend/TASKS.md
frontend/index.html
frontend/package-lock.json
frontend/package.json
frontend/scripts/checkClient.mjs
frontend/scripts/checkLiveBackend.mjs
frontend/scripts/checkLocales.mjs
frontend/scripts/checkMocks.mjs
frontend/scripts/checkUi.mjs
frontend/scripts/fixtures/catalogLabels.mjs
frontend/src/App.tsx
frontend/src/api/client.ts
frontend/src/api/mockCopy.ts
frontend/src/api/mocks.ts
frontend/src/api/validation.ts
frontend/src/components/AnimatedBackground.tsx
frontend/src/components/ContractorCard.tsx
frontend/src/components/DatePicker.tsx
frontend/src/components/Dropdown.tsx
frontend/src/components/FlipCard.css
frontend/src/components/FlipCard.tsx
frontend/src/components/GlassAvatar.tsx
frontend/src/components/Hero.tsx
frontend/src/components/HowItWorks.tsx
frontend/src/components/JellyRadio.css
frontend/src/components/JellyRadio.tsx
frontend/src/components/LanguageSwitcher.tsx
frontend/src/components/LiquidEther.css
frontend/src/components/LiquidEther.d.ts
frontend/src/components/LiquidEther.jsx
frontend/src/components/LocaleProvider.tsx
frontend/src/components/MatchResults.tsx
frontend/src/components/NotShownList.tsx
frontend/src/components/ProfileCard.css
frontend/src/components/ProfileCard.tsx
frontend/src/components/RequestForm.tsx
frontend/src/components/RequestSummary.tsx
frontend/src/lib/avatar.ts
frontend/src/lib/catalogLabels.ts
frontend/src/lib/format.ts
frontend/src/lib/locale.ts
frontend/src/lib/messages.ts
frontend/src/lib/validation.ts
frontend/src/main.tsx
frontend/src/styles.css
frontend/tsconfig.json
frontend/vite.config.ts
roles/DEV.md
roles/TESTER.md
scripts/check.ps1
scripts/hooks/pre-push
scripts/setup.ps1
scripts/start.ps1
shared/api.md
shared/types.ts
Ветки (4)

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

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

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