О проекте
Команда: Олжасхан
Кейс HackAlem.ai: Торговля — Электрокомплект · ekt.kz
Стадия: Рабочий MVP
Задача кейса: AI-консультант по каталогу ekt.kz: товары, аналоги, цены, остатки, корзина
Суть
Чат-консультант по каталогу электротехники ekt.kz. Показывает цены, остатки по складам, характеристики и сертификаты, подбирает объяснимые аналоги и понимает вложения (XLSX, DOCX, PDF, фото этикеток). Товар попадает в корзину только после явного подтверждения.
Как устроено
Консультацию и корзину ведут детерминированные правила в assistant.ts, у LLM нет права записи. Поиск распознаёт SKU и подбирает аналоги: совпадать должны все известные характеристики, категория и источник. Каталог берётся из демо-набора (9 SKU) или из live-API партнёра по Basic Auth с бюджетом 3,5 с на запрос. Gemini опционально уточняет поисковый запрос, но цен и остатков не выдаёт. Вложения разбирает изолированный worker, этикетки читает OCR Tesseract. Подтверждение привязано к SKU, количеству и цене, действует 5 минут и срабатывает один раз.
Ключевые решения
- детерминированный подбор аналогов по характеристикам
- двухшаговое подтверждение корзины
- OCR Tesseract
- изолированные парсеры в worker threads
- HttpOnly-сессии
- виджет для встраивания
Особенности
Строгая защита корзины: простое «да» не принимается, при смене цены нужно новое подтверждение, команды внутри вложений игнорируются. Готов виджет /widget.js для встраивания на сайт.
Стек
Next.js · TypeScript · Node.js · pnpm · Vitest · Playwright · Tesseract
Модели
gemini-3.8-flash
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-9bd2c351-team.
README
README.mdEKT · Консультант по каталогу
Рабочий прототип для кейса HackAlem: подбор электротехнической продукции, остатки по складам, характеристики и документы, объяснимые аналоги и добавление в корзину после отдельного подтверждения.
Ветка: feature/aldiyar. Использует существующий Next.js-проект и адаптер партнёрского API из main. Референс VIVPM/ecommerce-agent использован как ориентир для диалога, карточек, контекста и поиска по вложениям. Его Python-код и инфраструктура Neon/Pinecone не копировались: здесь один сервер Next.js и локальная обработка файлов.
Запуск
Нужен Node.js 22.16+, рекомендуется Node.js 24, и pnpm 11.25.0. При установленном Corepack: corepack enable.
pnpm install --frozen-lockfile
cp .env.example .env.local
pnpm dev
Открыть http://localhost:3000. Демо работает без ключей и внешних сервисов. Сервер использует Node.js worker threads для файлов; Edge runtime и статический экспорт не подходят.
pnpm typecheck
pnpm test
pnpm build
pnpm start
Для браузерных тестов на запущенном сервере:
pnpm exec playwright install chromium
pnpm test:e2e
# Или использовать установленный Chrome на macOS:
CHROME_PATH='/Applications/Google Chrome.app/Contents/MacOS/Google Chrome' pnpm test:e2e
При проверке production-сервера используйте HTTPS (cookie помечается Secure). Chromium разрешает Secure cookie на локальном loopback. Внешний HTTP для production не поддерживается.
Сценарий демонстрации и соответствие ТЗ
| Требование | Проверка |
|---|---|
| Наличие, характеристики, сертификат | Покажи EKT-CB-16A: 2 450 ₸, Алматы 24, Астана 11; 1P, 16 A, 6 kA, C. Документ открывается из карточки. |
| Аналог отсутствующего товара | Есть EKT-CB-20A?: предложен EKT-CB-20A-PRO, объяснены совпадающие параметры. Автомат 16 A не предлагается как эквивалент 20 A. |
| Условия покупки | Оплата, доставка и минимальная партия?: ответ на все три вопроса, явно отмеченный как демо-условия. |
| Только явное подтверждение | Добавь 2 EKT-CB-16A: корзина пуста. да, добавь или кнопка «Да, добавить»: 2 единицы в корзине. Простое да/okay не применяется. |
| Учёт количества и остатков | Добавь 100 EKT-CB-16A отклоняется. Учитывается уже добавленное количество. Кабель продаётся кратно 5 м. |
| Прямая ссылка на актуальную корзину | После подтверждения — /cart. Открытие, обновление и возврат в чат сохраняют сессию. |
| Свободная формулировка и контекст | Нужен автомат 16А, затем Добавь его 3 шт. SKU можно вводить на русском или английском; поиск понимает распространённые названия категорий. |
| Вложения | XLSX, DOCX, PDF с текстом, JPEG/PNG с читаемой этикеткой: поиск по извлечённым артикулам, без выполнения команд из файла. |
| Desktop/mobile и единое окно | Адаптивный интерфейс, встроенная версия /embed, виджет /widget.js. |
Кнопка «Выбрать» готовит предложение, а не добавляет товар. Подтверждение действует 5 минут, привязано к SKU, количеству, цене и источнику, используется один раз. Новый запрос или «Отмена» отменяет старое предложение. При изменении цены нужно новое подтверждение; при изменении остатка/кратности добавление отклоняется. Запросы одной сессии выполняются последовательно, поэтому двойной клик не удваивает добавление.
Архитектура
src/components/chat-workspace.tsx: диалог, восстановление истории, вложения и подтверждение.src/components/product-card.tsx: цены, склады, параметры, документы, обоснования аналогов.src/app/api/chat/route.ts: ограничения запросов, обработка текста/вложений, история.src/lib/assistant.ts: детерминированные правила корзины и консультации; модель не имеет права записи.src/lib/catalog.ts: поиск, точное распознавание SKU, объяснимый подбор, бюджет live-запроса 3,5 с.src/lib/ekt-api.ts: Basic Auth, валидация и отображение партнёрского каталога.src/lib/language.ts: необязательное уточнение поисковых слов через Gemini с таймаутом 4 с. Модель возвращает только запрос, не цены/остатки/условия.src/lib/attachments.ts+scripts/extract-file.mjs: изолированные парсеры и OCR.src/lib/sessions.ts: сессии и блокировки одного процесса, срок неактивности 2 часа, последние 40 сообщений./api/session,/api/cart: чтение состояния по HttpOnly-cookie. КлиентскиеsessionIdне принимаются, в URL токенов нет.
Цены, остатки и сертификаты формируются из данных каталога, а не из текста LLM. Аналоги должны совпадать по всем известным характеристикам и иметь ту же категорию и источник. Условия покупки сейчас только синтетические — реальные политика оплаты, доставка и минимальные партии партнёром не предоставлены.
Данные и live-интеграция
src/data/products.ts содержит 9 синтетических позиций: автоматы, УЗО, кабель, лампы и розетка. У каждой есть артикул, название, категория, цена в тенге, складские остатки и параметры. Два SKU недоступны и имеют совместимые аналоги. Файлы public/certificates/*.txt — демонстрационные образцы, не реальные документы соответствия.
Для реального каталога установите CATALOG_MODE=live и заполните EKT_API_* в .env.local. Секреты используются только сервером; не создавайте NEXT_PUBLIC_* с ключами. Партнёрские credentials не включены в репозиторий.
Адаптер использует GET products?page=N и GET products/detail?id=…. Ожидаемые поля списка: page, per_page, count, items; count — число элементов страницы. Поля товара: article, name, price, quantity, stores, properties, необязательные категория и документы. Несогласованные общие и складские остатки не считаются проверенными. Кратность берётся из packSize, pack_size, minimum_quantity или properties.KRATNOST_MIN.
Live-поиск ограничен бюджетом времени и максимум 100 страницами; ранее увиденные SKU позволяют сразу запросить детали. При полном каталоге партнёра нужен фоновый индекс или поисковый endpoint: текущий прототип может не просмотреть все страницы. Live-аналоги подбираются только из уже доступной части каталога. При сбое допустим явно подписанный fallback только для специально указанных демо-SKU. Добавление, предложенное по live-данным, не может переключиться на демо при подтверждении.
Этот проект пока ведёт локальную корзину прототипа, а не корзину ekt.kz: контракт записи/авторизации корзины сайта не предоставлен. Реальные checkout, заказ, платёж и резервирование не выполняются. Нужно подключить серверный адаптер корзины ekt.kz, сохранив существующие проверки и отдельное подтверждение.
Вложения и приватность
- До 5 МБ; PDF до 20 страниц; изображения до 12 Мп; XLSX до 2000 строк и 50 ячеек на строку.
- DOCX/XLSX: проверка ZIP, до 32 МБ распакованных данных, до 2000 записей. Формулы не исполняются, внешние ссылки не открываются.
- Локальный OCR Tesseract для английских и русских этикеток; языковые модели устанавливаются как зависимости. Фотография без читаемого артикула/подписи может не дать совпадений. Визуального определения модели товара по внешнему виду нет.
- Старые
.xls/.docнужно сохранить как.xlsx/.docx. Для сканированного PDF без текстового слоя загрузите фото этикетки или введите артикул. - Парсинг в отдельном worker с лимитом памяти и дедлайном 20 с, одновременно не более двух файлов. OCR может занять дольше обычного текстового ответа.
- Файлы и извлечённый текст не записываются на диск/в историю. Временная папка OCR содержит только общедоступные языковые модели и удаляется.
- Вложения не подтверждают и не готовят корзину, даже если содержат «да, добавь» или другие инструкции. Количество пользователь сообщает отдельно.
- По умолчанию данные не отправляются AI-провайдеру. Если задать
GEMINI_API_KEY, текст поискового запроса и идентификатор предыдущего товара могут передаваться Google; это показано под полем ввода. Содержимое файлов не передаётся. - Запросы с похожими на платёжные реквизиты числовыми последовательностями отклоняются до записи истории. Не отправляйте персональные/платёжные данные. Такая проверка эвристическая, не система DLP.
Встраивание
Для same-origin размещения через reverse proxy на текущем сайте:
<script src="/widget.js" defer></script>
Виджет открывает /embed. На том же origin должны обслуживаться /embed, /api/*, /cart, /_next/*, /certificates/* и /widget.js. Кросс-доменный iframe с third-party cookies не поддерживается. Для реального ekt.kz нужно согласовать пути, CSP и связать корзину с авторизацией сайта. Виджет проверяется отдельно от дизайна и CSP действующего сайта, доступа к которым здесь нет.
Границы прототипа
Один долгоживущий Node-процесс. При перезапуске теряются история, pending-запросы и корзины. Несколько инстансов/serverless требуют общего хранилища и распределённых блокировок. Ограничение 30 сообщений/минуту на сессию — базовая защита; публичному серверу нужен также лимит по IP на reverse proxy. Production требует HTTPS; за TLS reverse proxy задайте APP_ORIGIN=https://ваш-домен для проверки источника запросов.
Казахский интерфейс, история авторизованных пользователей, реальная передача менеджеру, резервирование и оформление заказа не реализованы. Базовые казахские подтверждения поддерживаются, но полноценное двуязычие не заявляется. Содержимое демо не подтверждает пригодность оборудования для конкретного электрощита.
Документация дополнительного AI-поиска: структурированный вывод Gemini, доступные модели. По умолчанию выбран gemini-3.8-flash; модель можно заменить через окружение.
Авторы коммитов
История коммитов
- 2026-09-23 17:57Merge pull request #2 from BAITC-Hacks/feature/aldiyaraldiyarww
- 2026-09-23 17:54Implement EKT catalog assistant with confirmed cart updatesAldiyar Meirambek
- 2026-09-23 17:54Document prototype setup and specification coverageAldiyar Meirambek
- 2026-09-23 16:55Merge pull request #1 from BAITC-Hacks/feature/sultanbryanDecs
- 2026-09-23 16:54Add EKT partner catalog adapterbryanDecs
- 2026-09-23 15:23Just skeleton of projectMAKLUBE
- 2026-09-23 14:36Where is my codex proMAKLUBE
Структура репозитория (45 файлов)
.env.example .gitignore .node-version AGENTS.md CLAUDE.md README.md next-env.d.ts next.config.ts package-lock.json package.json playwright.config.ts pnpm-lock.yaml pnpm-workspace.yaml public/certificates/EKT-CB-16A.txt public/certificates/EKT-CB-20A-PRO.txt public/widget.js scripts/extract-file.mjs src/app/api/cart/route.ts src/app/api/chat/route.ts src/app/api/session/route.ts src/app/cart/page.tsx src/app/embed/page.tsx src/app/globals.css src/app/layout.tsx src/app/page.tsx src/components/chat-workspace.tsx src/components/icons.tsx src/components/product-card.tsx src/data/products.ts src/lib/assistant.test.ts src/lib/assistant.ts src/lib/attachments.test.ts src/lib/attachments.ts src/lib/catalog.ts src/lib/ekt-api.test.ts src/lib/ekt-api.ts src/lib/http.test.ts src/lib/http.ts src/lib/language.test.ts src/lib/language.ts src/lib/sessions.ts src/lib/types.ts tests/chat.spec.ts tsconfig.json vitest.config.ts
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.