AAYMA
RU
Войти

помощник покупателя ekt.kz

Чат-помощник покупателя ekt.kz на лендинге Next.js: находит товары и остатки, подбирает аналоги для отсутствующих позиций, разбирает закупочные…

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

О проекте

Команда: TryHackMe
Кейс HackAlem.ai: Торговля — Электрокомплект · ekt.kz
Стадия: Доведённый продукт

Задача кейса: AI-консультант по каталогу ekt.kz: товары, аналоги, цены, остатки, корзина

Суть

Чат-помощник покупателя ekt.kz на лендинге Next.js: находит товары и остатки, подбирает аналоги для отсутствующих позиций, разбирает закупочные спецификации из файлов и добавляет позиции в серверную корзину после отдельного согласия.

Как устроено

FastAPI-бэкенд через OpenAI Responses API со Structured Outputs извлекает намерение, позиции и количества из сообщения или файла. DOCX разбирается python-docx, XLSX python-calamine, PDF и изображения идут в модель как визуальный ввод. Каждая позиция сверяется с каталогом ekt.kz (публичный поиск и Basic Auth API). Корзина вынесена за CartAdapter с циклом prepare → confirm, повторной проверкой цены, остатка и кратности, CSRF и HttpOnly-cookie. Условия оплаты и доставки и контакты менеджера парсятся с официальных страниц сайта.

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

  • OpenAI Structured Outputs
  • мультимодальное извлечение позиций из PDF/JPEG
  • python-docx
  • python-calamine
  • CartAdapter с отдельным подтверждением
  • проверка спецификации по позициям
  • CSRF/Origin защита

Особенности

Сценарий «спецификация → проверка каждой позиции → выбор строк → согласие → корзина»: неоднозначные совпадения, нехватка остатка и неверная кратность видны до добавления. Изменение цены или остатка отменяет всю партию. В репозитории есть готовые входные файлы (docx/xlsx/pdf/jpg/csv) для пяти Must-have сценариев.

Стек

Python · FastAPI · Pydantic · uv · Next.js 16 · React · Tailwind · shadcn/ui · Playwright · Docker Compose · GitHub Actions

Модели

gpt-4o-mini (OpenAI Responses API)


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

README

README.md

HackAlem AI — помощник покупателя ekt.kz

Адаптивный лендинг на Next.js 16, React, Tailwind и shadcn/ui: тёмная тема, сдержанные RGB-линии и одно окно чата. Проверенный чат из index.html переиспользуется внутри Next.js; файлы, карточки, аналоги, подтверждение и корзина сохраняют прежнее поведение. Word ТЗ — первичный источник требований.

Практический результат: спецификация → проверка каждой распознанной позиции → выбор строк → отдельное согласие → корзина. Неизвестные товары, неоднозначные совпадения и нехватка остатка видны до добавления. Польза и развитие продукта, план улучшений и пошаговый пример описывают доступные возможности и следующие шаги.

По уточнению заказчика корзина принадлежит нашему приложению. CartAdapter отделяет её от чата и правил подтверждения, чтобы впоследствии подключить корзину ekt.kz. Заказ, оплата и резерв на стороне магазина сейчас не создаются.

DEMO

Запуск

Для текущего интерфейса нужны Python 3.12+, uv, Node.js ≥ 20.9, интернет и серверные доступы OpenAI/ekt.kz. В чистом клоне, если backend/.env ещё нет, создайте его из backend/.env.example и заполните выданные доступы.

Основной интерфейс запускается в двух терминалах после установки зависимостей (make install) и настройки .env:

make dev           # терминал 1: FastAPI на 8765
make dev-frontend  # терминал 2: Next.js на 3000

Откройте http://localhost:3000/. Запросы /api и корзина /cart остаются на этом адресе; внутренний адрес backend задаётся ASSISTANT_API_URL в frontend/.env.local, по умолчанию http://127.0.0.1:8765. Секреты OpenAI и каталога frontend не получает.

Отдельный запуск FastAPI с исходным HTML:

cd backend
uv sync
# Заполните локальный .env; значения секретов не публикуйте.
uv run uvicorn app.landing:app --host 127.0.0.1 --port 8765

Откройте http://127.0.0.1:8765/. Сервер автоматически читает backend/.env, затем корневой .env; существующие переменные окружения имеют приоритет. Нужны:

OPENAI_API_KEY=ваш_ключ
AI_MODEL=gpt-4o-mini
EKT_API_USER=логин_партнёра
EKT_API_PASSWORD=пароль_партнёра

Модель берётся из вашего .env; она должна поддерживать Responses API, Structured Outputs и изображения. Ключ OpenAI и Basic Auth остаются на сервере. Используйте один worker: сессии и корзины хранятся в памяти процесса, переживают обновление страницы, но очищаются после часа бездействия или перезапуска сервера.

Открытие index.html без backend включает явно обозначенный деморежим: встроенные 40 товаров и одна детальная карточка из JSON партнёра, локальная корзина вкладки, без OpenAI. Это отдельный режим и не доказательство живой интеграции.

Готовые сценарии и файлы для демонстрации

Начните с out/README.md: там находятся пять сценариев строго по Must have Word ТЗ, готовые сообщения, закупочные Word/Excel/PDF/JPEG и дополнительные TXT/CSV, ожидаемые ответы и последовательность подтверждения корзины. Формы и количества смоделированы, коды взяты из каталога; фото товара сохранено с сайта без изменений. Реальные ответы проверяются по текущему каталогу. out/ документирует использование приложения и не служит источником готовых ответов для LLM.

На лендинге раздел «Начните со своей задачи» открывает тот же чат: пример артикула, выбор файла или запрос отсутствующего товара. Прикрепление только готовит вложение; пользователь отправляет запрос, проверяет результат и отдельно подтверждает корзину. Для знакомства с приложением используйте одну историю из demo-day.md, а не семь копий спецификации в одном заказе.

Что работает

СценарийРеальная обработка и результат
UC-01: товарOpenAI извлекает запрос/артикул → поиск по известным товарам и публичному поиску ekt.kz → детальная карточка через Basic Auth API. Показываются характеристики, склады, цена источника, время чтения и документы, когда есть ссылка.
UC-02: отсутствиеДля каждой найденной позиции с нулевым остатком автоматически ищутся аналоги, в том числе в запросе на добавление. При пустом узком поиске проверяются тип товара и раздел каталога. Кандидаты имеют проверенный остаток, объяснение совпадений и отличий; аксессуары и известные несовместимые электрические параметры отсеиваются.
UC-03: покупкаОплата и доставка читаются с опубликованной страницы магазина и отображаются разделами, абзацами и списками. Повторные блоки и навигация сайта не попадают в ответ; разные версии условий отмечаются. Кратность выбранного товара показывается в разделе минимальной партии.
UC-04: подтверждениеСначала предложение с артикулами, количеством и ценой. Кнопка «Да, добавить» либо отдельное «да, добавь» запускают повторную проверку цены, остатка и кратности. «Ок», вложения и ответы модели не разрешают изменение.
UC-05: корзинаПосле добавления выдаётся /cart. Ссылка открывает актуальную серверную корзину текущего посетителя в том же окне чата, в том числе после обновления страницы. Удаление тоже требует подтверждения.

Кнопка корзины повторно загружает её состав и показывает позиции в видимой части чата без фокуса на поле ввода. Запрос «Покажи корзину» выводит состав сразу. Товары из спецификации появляются здесь после отдельного подтверждения; исходные файлы в корзине не хранятся.

Примеры: Есть ли 027005? Покажи характеристики и сертификат, Есть ли ярп4520? Если нет, предложи аналог, Как оплатить и получить доставку?, Добавь 2 шт 027005, затем да, добавь.

Проверка спецификации перед корзиной

В серверном режиме у каждой распознанной позиции есть запрос, количество, соответствие каталогу и статус. Отдельно отмечаются неоднозначность, неизвестный код, отсутствие количества, нехватка остатка и неверная кратность. Повторы учитываются суммарно вместе с уже добавленным количеством. Модель может пропустить строку: разбор нужно сверить с исходным файлом.

Готовые строки можно снять с выбора; счётчик показывает выбранные и пропущенные. «Проверить выбранное» повторно читает каталог и готовит предложение. Только «Да, добавить» изменяет корзину. Изменившийся остаток или цена отменяют добавление всей партии. Неизвестные строки и аналоги автоматически в партию не попадают.

Как обрабатываются файлы

  1. Прикрепление подготавливает файл в браузере; передача начинается при отправке сообщения. До отправки вложение можно удалить.
  2. Backend проверяет размер, формат и структуру. DOCX разбирается через python-docx (абзацы и таблицы), XLS/XLSX — через python-calamine (все листы, заголовки и значения, включая сохранённый результат формулы). CSV разбирается по столбцам с учётом разделителя, TXT — как UTF-8. Модель получает текст и структуру строк; внутренние ERP-коды не подменяют код производителя.
  3. JPEG/PNG передаются в OpenAI как изображение, PDF — как файл с визуальным содержимым, включая сканы. Модель извлекает коды, признаки и количества. Старый бинарный DOC направляется как документ OpenAI; отдельный живой прогон этого формата пока не зафиксирован, предпочтителен DOCX.
  4. Распознанные позиции показываются в чате; цена, остаток и характеристики всё равно проверяются по каталогу. Если точного соответствия нет, требуется уточнение. Ни текст файла, ни распознанная инструкция не подтверждают корзину.

Лимиты: 5 файлов по 8 МБ; общий base64-пакет до 24 млн символов; PDF до 10 страниц без пароля; фото до 20 Мп; Excel до 5 листов, 300 строк и 80 столбцов на лист; извлечённый текст документа до 24 000 символов; до 20 товарных позиций за запрос. Превышение и повреждённые файлы возвращают объяснимую ошибку.

Файлы не записываются на диск или в БД приложения. В памяти сессии остаются последние 12 сообщений с названиями файлов, без их содержимого. Запросы OpenAI используют store=False; это не обещание нулевого хранения на стороне провайдера. Не отправляйте платёжные данные. Известные шаблоны номера карты/CVV блокируются в сообщениях и извлечённом офисном тексте; это не универсальный детектор содержимого изображений. Подробности — в docs/file-processing.md.

Помощь и связь с менеджером

«Как заказать?» (в том числе с опечатками) возвращает конкретные шаги, «С чем можешь помочь?» — возможности и следующий шаг. При обработке вложений чат показывает, что читает файлы и сопоставляет позиции; при долгом запросе сообщает об ожидании.

«Связаться с менеджером» доступно в том же окне и без ответа языковой модели. Сервер читает телефон/WhatsApp с официальной страницы контактов и готовит редактируемый текст обращения. Покупатель копирует его и сам открывает канал магазина. Сообщения, история и документы автоматически не отправляются. Это контакт с магазином, а не подключение оператора к нашей сессии или создание CRM-заявки.

Кнопка также предлагается при сбое источника/модели, повторно неудачном поиске, отсутствии нужного сертификата, конфликте характеристик или невозможности подтвердить замену. Если контакты получить не удалось, остаётся ссылка на исходный сайт. Корзина меняется только после своего отдельного подтверждения.

Архитектура и смена корзины

Next.js / React / Tailwind / shadcn — лендинг
    → общий DOM/JS чат из index.html, подключённый внутри страницы
    → Next.js /api → app.landing /api/chat
        → OpenAI Responses: намерение, позиции, количества
        → ekt.kz: поиск + проверенный каталог + условия
    → /api/cart/prepare → отдельное согласие → /api/cart/confirm
        → CartAdapter → ServerCart
    → /cart → та же Next.js страница + актуальная сессия

backend/app/landing.py содержит Pydantic-контракты, обработку файлов, поиск, проверки и адаптер. LLM не имеет инструмента прямого изменения корзины. HttpOnly/SameSite cookie, CSRF-токен и проверка Origin защищают сессию; предложение связано с её версией, одноразовое и действует 3 минуты. Проверяется весь набор позиций до изменения; повторное подтверждение отклоняется.

Для будущего переключения реализуется CartAdapter с авторизованной сессией покупателя ekt.kz и заменяется cart_adapter = ServerCart(). Логика явного согласия остаётся перед адаптером. Нужен контракт магазина для сопоставления пользователя и идемпотентного применения операции; передача наших cookie или Basic Auth каталога вместо авторизации корзины недопустима. Для размещения виджета на другом сайте предусмотрите маршрутизацию /api и /cart на этот backend через тот же origin.

contracts/landing.openapi.json генерируется из Pydantic. В index.html обычный JavaScript, отдельной ручной копии типов нет. Генератор проверяется локально и в CI.

Проверки

# Из backend/
uv run python generate_landing_openapi.py --check
uv run python generate_ekt_catalog_openapi.py --check
uv run ruff check app tests generate_ekt_catalog_openapi.py generate_landing_openapi.py
uv run pytest

# Из frontend/; нужны установленный Chromium и его системные библиотеки
npm run test:landing       # автономный режим, без внешнего AI
npm run test:landing:live  # живые API и расходы OpenAI из .env
# Новый интерфейс: сначала npm run build
npm run test:next           # production Next.js, без внешнего AI
npm run test:next:live      # Next.js → FastAPI → OpenAI / ekt.kz

После переноса в Next.js: production build, ESLint, TypeScript и 24 автономных браузерных проверки прошли. В отдельных живых запусках прошли 9 из 10 сценариев; полный desktop UC дважды остановился на сообщении о сбое обработки модели без товаров. Файлы, помощь и защита прокси проверены на обоих экранах, полная живая цепочка UC-01…05 — на mobile. Подробности и оставшаяся проверка — ROADMAP, M13.

Предыдущий этап M12: 41 backend-тест, 22 автономных браузерных и 8 живых сценариев; это отдельные результаты до переноса. Команда из корня make test-files-live загружает все восемь входов с реальными OpenAI/ekt.kz, сравнивает позиции/количества и факты, проверяет объяснённый аналог и подтверждённую корзину из 6 единиц. Результаты, модель, длительности, хеши и неуспешные предыдущие попытки — out/verification.json. JPEG накладной и фото аппарата проверяются отдельно: мелкая маркировка требует уточнения кода, это не успешное автоматическое распознавание модели аппарата.

Границы данных и прототипа

  • В API нет валюты; сумма показывается без придуманного обозначения. Цена API может отличаться от цены интернет-магазина, окончательная цена заказа здесь не обещается.
  • Для 027228 обнаружено расхождение: название 160 А, свойство 250 А. Оно показано пользователю и переносится в пояснение к кандидатам; полная взаимозаменяемость не утверждается. Замены не добавляются автоматически.
  • В проверенных карточках нет сертификата: отображается отсутствие данных. Положительный путь показа ссылки реализован, но живой товар с доступным сертификатом пока не найден.
  • Поиск использует выборку и публичный поиск магазина, а не гарантированно полный локальный индекс. Его HTML и опубликованные условия могут измениться; при сбое факты не подменяются выдуманными.
  • Критерий UC-02 — минимум один релевантный доступный аналог. Он проверяется на реальных отсутствующих позициях, но для всего каталога ещё не доказан: при отсутствии подходящего кандидата после расширенного поиска показывается ограничение. Нерелевантный товар не подставляется ради количества ответов.
  • Перед публичной эксплуатацией нужны постоянное хранилище сессий/корзин, HTTPS и ограничения нагрузки. Размещение на действующем домене ekt.kz не выполнялось.

Перенос frontend без переписывания чата

React-лендинг живёт в frontend/src/app/page.tsx. Сборка автоматически извлекает CSS/HTML/JS чата из index.html: это один источник поведения, изолированный от React и оформленный в общей теме. Так сохраняются проверенные сценарии, а полный перенос сообщений на React можно выполнить отдельно. Для изменений чата после редактирования HTML перезапустите frontend. Подробнее — frontend/README.md.

make test-next-ui проверяет production Next.js без внешних API; make test-next-live — живую цепочку через прокси, файлы и корзину на desktop/mobile. Старые команды HTML-набора сохранены. Новых зависимостей этот перенос не добавляет.

Команды проекта и Docker

Из корня: make install, make dev, make check, make test, make test-ui, make test-live. Команды, архитектура и проверки описаны в docs/architecture.md и docs/testing.md.

Для контейнера: docker compose up --build. Используется один backend на http://127.0.0.1:8765/, секреты из локального backend/.env; дополнительные сервисы не запускаются. Файлы .env и документы с доступом не включаются в образ. Сессии остаются в памяти процесса.

Существующий Docker-образ обслуживает самостоятельный HTML на FastAPI. Новый Next.js запускается отдельно; production-сборка проверяется командой make build-frontend.

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

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

mm 19

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

  • 2026-09-23 18:04docs: add img demomm
  • 2026-09-23 18:03fix(frontend): chat colors in dark mode and testsmm
  • 2026-09-23 18:00fix(frontend): chat colors in dark modemm
  • 2026-09-23 17:55feat(frontend): migrate landing to Next.js with neon UImm
  • 2026-09-23 17:34feat(assistant): add procurement inputs and manager contact flowmm
  • 2026-09-23 17:07feat(assistant): add specification review before cart confirmationmm
  • 2026-09-23 16:37fix(assistant): repair cart navigation, terms and analog searchmm
  • 2026-09-23 16:13refactor(repo): trim template to HackAlem assistant scopemm
Все коммиты (19)
  • 2026-09-23 15:58docs(demo): add HackAlem use cases and input examplesmm
  • 2026-09-23 15:57feat(landing): connect OpenAI assistant and protected session cartmm
  • 2026-09-23 15:26feat: update landingmm
  • 2026-09-23 14:22feat: landing htmlmm
  • 2026-09-23 14:17docs(contracts): move catalog API contracts into dedicated directorymm
  • 2026-09-23 14:11docs: align use cases with HackAlem Word source of truthmm
  • 2026-09-23 14:01docs: openapi generatedmm
  • 2026-09-23 13:39clean docsmm
  • 2026-09-23 13:38docs: formalize ekt.kz requirements and use-case roadmapmm
  • 2026-09-23 13:12docs: add HackAlem AI hackathon materials and update agent guidelinesmm
  • 2026-09-23 11:08chore: initialize bootstrap scaffoldmm
Структура репозитория (87 файлов)
.dockerignore
.github/workflows/ci.yml
.gitignore
AGENTS.md
CHANGELOG.md
Makefile
README.md
ROADMAP.md
TMP_real_out_files.md
backend/.env.example
backend/Dockerfile
backend/app/__init__.py
backend/app/landing.py
backend/app/schemas/__init__.py
backend/app/schemas/ekt_catalog.py
backend/generate_ekt_catalog_openapi.py
backend/generate_landing_openapi.py
backend/pyproject.toml
backend/tests/test_landing.py
backend/uv.lock
contracts/README.md
contracts/ekt-catalog-api.md
contracts/ekt-catalog.openapi.json
contracts/landing.openapi.json
docker-compose.yml
docs/architecture.md
docs/file-processing.md
docs/product-value.md
docs/source of truth/PRD.md
docs/source of truth/assets/HackAlem AI_ ИИ-ассистент для чата на сайте ekt.kz.docx
docs/source of truth/assets/detail.json
docs/source of truth/assets/products _page_2.json
docs/source of truth/assets/products.json
docs/source of truth/assets/ИИ-ассистент для чата на сайте ekt.kz ДАННЫЕ.docx
docs/source of truth/plan.md
docs/source of truth/source of truth.md
docs/source of truth/use-cases.md
docs/testing.md
frontend/.editorconfig
frontend/.env.example
frontend/.gitignore
frontend/.prettierignore
frontend/.prettierrc
frontend/README.md
frontend/components.json
frontend/e2e/landing.live.spec.ts
frontend/e2e/landing.spec.ts
frontend/e2e/next.live.spec.ts
frontend/e2e/next.spec.ts
frontend/eslint.config.mjs
frontend/next.config.ts
frontend/package-lock.json
frontend/package.json
frontend/playwright.landing.config.ts
frontend/playwright.next.config.ts
frontend/postcss.config.mjs
frontend/scripts/sync-assistant.mjs
frontend/src/app/api/[...path]/route.ts
frontend/src/app/cart/page.tsx
frontend/src/app/globals.css
frontend/src/app/layout.tsx
frontend/src/app/page.tsx
frontend/src/components/assistant/action.tsx
frontend/src/components/assistant/widget-theme.css
frontend/src/components/assistant/widget.tsx
frontend/src/components/ui/button.tsx
frontend/src/lib/utils.ts
frontend/tsconfig.json
image.png
index.html
out/README.md
out/demo-day.md
out/expected-results.md
out/generate_examples.py
out/inputs/delivery_note_image.jpg
out/inputs/delivery_note_scan.pdf
out/inputs/electrician_request.txt
out/inputs/erp_export.csv
out/inputs/procurement_request.docx
out/inputs/product_photo.jpg
out/inputs/project_specification.pdf
out/inputs/purchase_request.xlsx
out/manifest.json
out/observed-session.json
out/use-cases.md
out/verification.json
out/verify_live.py

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

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

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