AAYMA
RU
Войти

Pickora

Node.js-приложение подбора event-подрядчиков с жёсткими фильтрами и семантическим ранжированием через эмбеддинги, плюс голосовой ввод заявки…

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

О проекте

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

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

Суть

Node.js-приложение подбора event-подрядчиков с жёсткими фильтрами и семантическим ранжированием через эмбеддинги, плюс голосовой ввод заявки, аккаунты, избранное, переписка, заявки и отзывы.

Как устроено

Бизнес-фильтры (город, категория, формат, занятая дата, бюджет, язык/длительность) применяются до ранжирования; эмбеддинги OpenAI text-embedding-3-small меняют только порядок подходящих профилей, с детерминированным fallback по цене и ID. Голосовой ввод: аудио → gpt-transcribe → structured-output (gpt-4o-mini, JSON Schema) извлекает поля формы для подтверждения пользователем. Данные аккаунтов, отзывов, сообщений хранятся в node:sqlite с миграциями.

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

  • hard filters
  • embedding semantic ranking
  • speech-to-text
  • structured outputs JSON Schema
  • scrypt
  • polling-чат

Особенности

Голосовая заявка с раздельными STT и извлечением полей, где неизвестные значения не подменяются случайными пунктами каталога; эмбеддинги влияют только на порядок уже отфильтрованных кандидатов.

Стек

Node.js · node:sqlite · SQLite · JavaScript · HTML · CSS

Модели

text-embedding-3-small · gpt-transcribe · gpt-4o-mini


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

README

README.md

Pickora - умный подбор подрядчиков

Русскоязычное Node.js-приложение для подбора event-подрядчиков из CSV-каталога из 66 профилей. Обычный просмотр, фильтры и подбор доступны без входа. Пользовательские данные и аккаунты хранятся локально в SQLite.

Требования и запуск

  • Node.js 24 или новее (node:sqlite входит в Node и не требует отдельной базы-сервера).
  • npm install (в приложении нет runtime-пакетов из npm).
  • npm start, затем откройте http://localhost:3000.

При первом запуске сервер создаёт data/hackalem.sqlite и применяет SQL-файлы из migrations/. Миграции выполняются транзакционно и отмечаются в schema_migrations. Файл базы, WAL и секреты .env исключены из Git. Можно задать другой путь через DATABASE_PATH.

Для локального порта в PowerShell: $env:PORT='3100'; npm start. По умолчанию сервер привязывается только к 127.0.0.1. Укажите HOST=0.0.0.0 явно лишь если этого требует хостинг-провайдер.

AI-подбор

Текущие бизнес-фильтры (город, категория, формат, занятая дата, бюджет и необязательные язык/длительность) выполняются до semantic ranking. Embeddings влияют только на порядок подходящих профилей; цена и ID сохраняются как deterministic fallback. Код подборщика остаётся работоспособным без AI.

Создайте локальный .env по образцу .env.example и задайте:

AI_PROVIDER=openai
AI_API_KEY=...
AI_MODEL=text-embedding-3-small
AI_TIMEOUT_MS=4500
AI_CACHE_TIMEOUT_MS=60000

Сервер кэширует описания каталога и извлекает подтверждающие фрагменты из самих профилей. Для работы реального provider API-серверу нужен исходящий доступ к OpenAI API. Ключ не отправляется браузеру.

Голосовой ввод

На странице подбора нажмите «Сказать голосом», разрешите микрофон, проговорите заявку и нажмите остановку. Запись длится максимум 30 секунд и не превышает 10 МБ. Сначала отдельный speech-to-text запрос распознаёт аудио, затем отдельный structured-output запрос извлекает поля. Интерфейс показывает исходный распознанный текст, предложения и неразрешённые/неизвестные значения. Конфликтующие заполненные поля по умолчанию не отмечены. Проверьте и примените выбранные поля; подбор запускается отдельной кнопкой.

Настройки:

AI_TRANSCRIBE_MODEL=gpt-transcribe
AI_VOICE_MODEL=gpt-4o-mini
AI_VOICE_TIMEOUT_MS=30000
APP_TIME_ZONE=Asia/Qyzylorda

gpt-transcribe выбран для загрузки завершённой записи; модель голосового парсера использует constrained JSON Schema. Актуальные API: распознавание аудио и structured outputs. Если AI недоступен, сервер сообщает об ошибке и обычная форма продолжает работать. Аудио передаётся провайдеру только по пользовательскому действию и не записывается в постоянную БД или файл приложения.

Для микрофона используйте localhost в актуальном браузере с поддержкой getUserMedia/MediaRecorder (например, Chrome, Edge или Safari) и разрешите микрофон для сайта. На опубликованном домене требуется HTTPS. Если браузер не поддерживает запись, отказывает в доступе, или speech/AI API недоступен, заполните обычную форму.

Вход и демо-аккаунты

Регистрация заказчика находится в Аккаунт. Пароли хешируются scrypt; браузер получает случайную HttpOnly, SameSite=Strict сессию, хеш которой хранится в SQLite. Маршруты проверяют аккаунт, роль и участника ресурса на сервере. Регистрация подрядчика требует одноразового приглашения, связанного на сервере с конкретным ID CSV-профиля: аккаунт нельзя привязать к произвольному профилю из браузера.

Для локального демо однократно выполните:

npm run seed:demo

Команда идемпотентна; повторный запуск не дублирует аккаунты, заявки, отзывы, избранное, дружбу или сообщения. Демо-аккаунты:

Имя пользователяРольПароль
demo.customerзаказчикHackAlemDemo2026!
demo.friendзаказчик и подтверждённый другHackAlemDemo2026!
demo.outsiderзаказчик без дружбыHackAlemDemo2026!
demo.vendorподрядчик, профиль HK-44733HackAlemDemo2026!

Пароль фиксирован только для локальной демонстрации. Не создавайте демо-аккаунты в production. seed:demo блокируется при NODE_ENV=production, пока явно не задано ALLOW_DEMO_DATA=YES. Созданные отзывы, завершённые заявки, переписка и счётчики помечены как демонстрационные и не являются настоящими отзывами/заказами.

Чтобы подключить реального владельца профиля, выпустите персональное одноразовое приглашение:

npm run invite:contractor -- HK-12345

Передайте код конкретному владельцу вне приложения. Он зарегистрирует роль подрядчика с username/password и этим кодом. В БД сохраняется хеш кода, профиль привязывается один раз. Пока профиль не подключён, кнопка переписки показывает это явно; CSV остаётся каталогом и не создаёт фиктивные аккаунты подрядчикам.

Пять пользовательских функций

  1. Рейтинг и выполненные услуги. В карточке, сравнении и подробном профиле отображаются средняя оценка по опубликованным отзывам, количество отзывов и количество заказов, которые завершил подрядчик и подтвердил заказчик. Метрики независимы. До первого отзыва показывается «Пока нет отзывов», без фиктивной оценки. Отзыв доступен только заказчику по завершённой им заявке; одна заявка — один редактируемый отзыв. Итоговые числа напрямую не редактируются.
  2. Голосовой ввод. Раздельное распознавание речи и извлечение параметров; русский/казахский; неизвестные значения не подменяются случайными пунктами каталога; даты сверяются с календарём 2026-09-23–2026-12-31 в APP_TIME_ZONE.
  3. Избранное. Серверный список на аккаунт с уникальностью по паре пользователь/профиль. На странице избранного профиль можно открыть, удалить или написать в подключённый аккаунт. Кнопка проверки применяет существующие обязательные фильтры к текущей форме; сохранение профиля само по себе не означает доступность.
  4. Переписка. Текстовые диалоги заказчик–подрядчик хранятся в SQLite. Один диалог на пару аккаунтов. Новые сообщения проверяются каждые 3,5 секунды (polling): для небольшого проекта с короткой задержкой это проще поддерживать, чем WebSocket/SSE, и не требует отдельного постоянного соединения. API допускает сообщения до 4000 символов, ограничивает частоту, проверяет участников, определяет автора по сессии и поддерживает idempotency nonce для повтора запроса.
  5. Отзывы друзей. Найдите username точным совпадением, отправьте запрос и дождитесь принятия. Отзыв друга получает мягкий цвет, рамку и подпись «Отзыв вашего друга». Это персональное представление; сервер рассчитывает его из принятых связей. Есть явный переключатель «Сначала отзывы друзей», обычный порядок иначе не меняется. Удаление дружбы снимает акцент.

Для услуги предусмотрен короткий процесс заявки без оплаты: заказчик создаёт заявку со сводкой события → подрядчик принимает/отклоняет → при принятии отмечает выполнение → заказчик подтверждает завершение. Только последний переход увеличивает счётчик. Заявки доступны в аккаунте и соответствующей переписке.

Демонстрационные сценарии

  • Войдите как demo.customer, откройте профиль HK-44733: увидите рейтинг 4.5, два отзыва и две демо завершённые услуги. Один отзыв будет подписан как отзыв друга.
  • Выйдите и войдите как demo.outsider: те же отзывы видны в обычном оформлении, без подписи друга.
  • Откройте Сообщения у заказчика и demo.vendor: один и тот же диалог и история доступны обеим сессиям.
  • У заказчика уже есть избранный HK-44733; он сохраняется после logout/login.
  • Для проверки заявок войдите в customer/vendor, создайте заявку в диалоге, затем выполните переходы ролями. Демо-отзывы/история уже помечены, новые заявки создаются как обычные строки в локальной БД.

API (основные маршруты)

  • GET /api/health, GET /api/meta, POST /api/recommendations — существующий подбор плюс сводная статистика аккаунта профиля.
  • POST /api/auth/register, /api/auth/login, /api/auth/logout, GET /api/auth/me.
  • GET /api/profiles/:id, GET /api/favorites, PUT /api/favorites/:id, POST /api/favorites/check.
  • GET /api/dialogs, POST /api/dialogs/open, GET/POST /api/dialogs/:id/messages, POST /api/dialogs/:id/requests.
  • GET /api/requests, POST /api/requests, PATCH /api/requests/:id/status, PUT /api/requests/:id/review.
  • GET /api/friends, POST /api/friends/requests, PATCH /api/friends/requests/:id, DELETE /api/friends/:id.
  • POST /api/voice/transcribe (multipart audio), POST /api/voice/parse (recognized text).

Закрытые маршруты требуют cookie-сессию и проверяют роль/участника. API-ошибки не раскрывают трассировки и SQL. Текст интерфейса вставляется через textContent, а не как HTML.

Тесты и двухсессионная проверка

npm test
npm run seed:demo
npm start

npm test покрывает прежние рекомендации/hard filters/fallback, SQLite миграции, разделение рейтинга и завершений, права на отзывы, уникальность избранного, доступ и идемпотентность сообщений, персональную подсветку друзей/её снятие и проверку структурированных AI-полей тестовым provider. Для HTTP-сценария с двумя независимыми cookie-клиентами после seed:demo запустите:

npm run smoke:demo

Он использует только локальный тестовый сервер и демо-аккаунты; не вызывает внешний AI. Аудио/API и реальные embeddings требуют действующего ключа и исходящего доступа в интернет. Если этого доступа нет, AI интеграция проверяется через тестовый provider; это не является проверкой качества живой транскрипции.

Дополнительная живая проверка конфигурации: npm run check:ai делает по одному embeddings-запросу и извлечению полей по синтетической фразе. Аудио не загружается, ключ не печатается; провайдер может тарифицировать эти запросы.

Ограничения и развёртывание

  • Каталог анонимизирован, содержит исходные флаги synthetic/imputed и не представляет подключённых продавцов. Сейчас диалог получает только профиль с зарегистрированным связанным аккаунтом; остальные владельцы должны подключиться по приглашению.
  • Это минимальный локальный продукт без платежей, вложений, реального календаря/бронирования или внешней доставки email/SMS.
  • Для постоянного хранения на хостинге нужен сохраняемый диск/том для data/hackalem.sqlite; эфемерная файловая система теряет базу при пересоздании приложения.
  • Голосовые запросы и тексты отправляются AI-провайдеру только когда вызван соответствующий endpoint и настроен ключ. Не размещайте ключ в frontend, CSV или Git.
Звёзды
⭐ 0
Форки
0
Язык
JavaScript
Последний push
в прошлом месяце
Коммиты
18
Авторы
4
Файлы
25
Ветки
1

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

Nurasyl Zhumagul 12sacenovaldiyar 3zhnuraa 2magzhanseitkapar 1

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

  • 2026-09-23 17:52Fix heading formatting in README.mdzhnuraa
  • 2026-09-23 17:52Update README.mdzhnuraa
  • 2026-09-23 17:33get rid off nonsense codeNurasyl Zhumagul
  • 2026-09-23 17:16asdasdNurasyl Zhumagul
  • 2026-09-23 17:14asdadsNurasyl Zhumagul
  • 2026-09-23 16:31improvementsNurasyl Zhumagul
  • 2026-09-23 15:37attemps of implementing aiNurasyl Zhumagul
  • 2026-09-23 15:02UPDATING THE INTERFACENurasyl Zhumagul
Все коммиты (18)
  • 2026-09-23 14:23main workNurasyl Zhumagul
  • 2026-09-23 13:59Resolve merge conflictsNurasyl Zhumagul
  • 2026-09-23 13:50Update project filesNurasyl Zhumagul
  • 2026-09-23 12:57deleted uncNurasyl Zhumagul
  • 2026-09-23 12:55smthNurasyl Zhumagul
  • 2026-09-23 12:48created folderssacenovaldiyar
  • 2026-09-23 12:37upNurasyl Zhumagul
  • 2026-09-23 12:36uppmagzhanseitkapar
  • 2026-09-23 12:30Add prediction modelsacenovaldiyar
  • 2026-09-23 12:27testsacenovaldiyar
Структура репозитория (25 файлов)
.env.example
.gitattributes
.gitignore
README.md
data/contractors.csv
migrations/001_social_features.sql
migrations/002_demo_record_markers.sql
package-lock.json
package.json
public/app.js
public/index.html
public/styles.css
scripts/check-ai-config.js
scripts/seed-demo.js
scripts/smoke-demo-sessions.js
server.js
src/catalog.js
src/db.js
src/identity.js
src/recommend.js
src/semantic.js
src/social.js
src/voice.js
test/recommend.test.js
test/social.test.js

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

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

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