AAYMA
RU
Войти

EKT Assistant

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

Рабочий MVP
Команда «'til 3am» 17💬 0 комментариевс 23 сентября 2026 г.

О проекте

Команда: 'til 3am
Кейс HackAlem.ai: Торговля — Электрокомплект · ekt.kz
Стадия: Рабочий MVP

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

Суть

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

Как устроено

Сообщение сначала разбирается детерминированно: артикул, синонимы, количество (в том числе словами, «екеуін»), город и намерение. LLM (Gemini через OpenAI-совместимый API) подключается, только если разбор ничего не нашёл, и для естественного казахского ответа; guard на числа следит, чтобы модель не меняла цифры. Ответ собирается из выгрузки ekt.kz (dataset/*.json). Агент возвращает только намерение «добавить N шт.», backend создаёт одноразовое предложение на 5 минут и повторно проверяет остаток перед записью.

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

  • детерминированный разбор намерений и сущностей
  • LLM-fallback для свободного текста
  • number guard
  • подбор аналогов с обоснованием
  • одноразовое подтверждение корзины с TTL
  • Playwright e2e-тесты

Особенности

Гибрид «сначала правила, LLM только при необходимости» с защитой цифр от галлюцинаций. Противоречие в данных каталога (160 А в названии и 250 А в свойствах) показывается пользователю. README содержит пошаговую проверку пяти требований кейса и попытки обойти подтверждение.

Стек

Python · FastAPI · React · TypeScript · Vite · Playwright

Модели

Google Gemini · NVIDIA NIM (адаптер)


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

README

README.md

EKT Assistant — ИИ-консультант для ekt.kz

HackAlem AI · кейс ТОО «Электрокомплект» · команда ’til 3am

Чат-ассистент для интернет-магазина электротехники. Покупатель пишет свободным текстом на русском или казахском и сразу получает:

  • наличие по городу, цену и характеристики;
  • честный ответ про сертификат;
  • аналоги с объяснением, если товара нет;
  • условия покупки;
  • добавление в корзину, но только после явного подтверждения и не больше остатка.

Главный экран


Содержание

  1. Быстрый запуск
  2. Как проверить: 5 обязательных требований
  3. Казахский язык и мобильная версия
  4. Архитектура
  5. Безопасность и достоверность
  6. Данные
  7. Тесты
  8. Ограничения
  9. Структура репозитория

Быстрый запуск

Нужны Python 3.10+ и Node.js 18+. Запуск занимает около 3 минут. Команды даны для Windows PowerShell, для macOS и Linux они в комментариях.

1. Backend (терминал 1)

git clone https://github.com/BAITC-Hacks/hack-682bab9a-til-3am.git
cd hack-682bab9a-til-3am

python -m venv .venv
.venv\Scripts\activate                      # macOS/Linux: source .venv/bin/activate
python -m pip install -r backend/requirements.txt

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

Проверка: откройте http://127.0.0.1:8000/api/v1/health. Должно быть {"status":"ok","products_loaded":40}.

2. Frontend (терминал 2)

cd frontend
npm install
Copy-Item .env.example .env.local      # macOS/Linux: cp .env.example .env.local
npm run dev

Откройте http://127.0.0.1:5173. Vite сам перенаправляет запросы /api на backend.

3. LLM (необязательно, нужен для казахского)

Без ключа ассистент полностью работает на русском в детерминированном режиме. Чтобы включить понимание казахского и живой речи:

  1. Получите бесплатный ключ Google Gemini: https://aistudio.google.com/apikey
  2. Создайте файл backend/.env:
    LLM_BASE_URL=https://generativelanguage.googleapis.com/v1beta/openai
    LLM_API_KEY=ваш_ключ
    LLM_TIMEOUT=12
    
  3. Перезапустите backend.

Подходит любой OpenAI-совместимый провайдер (Groq, OpenRouter, NVIDIA), примеры в backend/.env.example.

Почему Gemini, а не NVIDIA. Сначала мы подключали LLM через NVIDIA NIM: адаптер и логирование ошибок сохранились в истории коммитов. Но ключ NVIDIA, который был у команды, во время хакатона так и не заработал, и ответов от модели мы не получили. Поэтому переключились на Google Gemini:

  • бесплатный ключ получается за минуту;
  • модель хорошо понимает казахский и небрежный живой текст;
  • у Gemini есть OpenAI-совместимый API, поэтому менять код клиента не пришлось.

Клиент по-прежнему провайдер-независимый. Чтобы вернуть NVIDIA, достаточно указать в backend/.env значения LLM_BASE_URL=https://integrate.api.nvidia.com/v1, LLM_MODEL и LLM_API_KEY.

Если что-то не работает

СимптомРешение
Чат отвечает про «DEMO-BREAKER», а не про реальные товарыНет frontend/.env.local: выполните Copy-Item .env.example .env.local в папке frontend и перезапустите npm run dev
«Сервер недоступен (404/502)»Backend не запущен на порту 8000 (шаг 1)
Порт 8000 занятЗакройте предыдущий запуск uvicorn или используйте другой порт и поменяйте его в frontend/vite.config.ts
Казахский ответ пришёл на русскомLLM не настроена или бесплатный тариф Gemini перегружен. Ответ всё равно верный: он из данных

Как проверить: 5 обязательных требований

Вводите запросы в чат по порядку, в одной вкладке. Чтобы начать заново с пустой корзиной, обновите страницу.

1. Наличие, характеристики, сертификат

Ввести: Есть 027228 в Алматы?

Ожидается: цена 64 920 ₸, 5 шт. в Алматы, характеристики. В данных каталога номинальный ток указан по-разному: 160 А в названии и 250 А в свойствах. Ассистент не скрывает противоречие, а показывает предупреждение.

Наличие и характеристики

Дальше:

  • а в Нур-Султане? → 8 шт. Ассистент помнит товар из контекста диалога.
  • покажи сертификат → «файл сертификата в данных каталога не приложен» и ссылка на карточку. Ассистент ничего не выдумывает.

2. Аналоги, если товара нет

Ввести: Нужен 027230

Ожидается: «нет в наличии» и 2 аналога. На каждой карточке обоснование: что совпадает (тип, ток, серия, полюса), что отличается (отключающая способность), сколько в наличии и напоминание проверить совместимость.

Аналоги с обоснованием

3. Условия покупки

Ввести: Условия доставки и оплаты? или минимальная партия?

Ожидается: оплата, доставка и минимальная партия. В выгрузке ekt.kz этих условий нет, поэтому это условия демомагазина, и ответ помечает это явно.

Условия покупки

4. Корзина только после явного подтверждения и в пределах остатка

Ввести: Есть 027228 в Алматы?, затем добавь 2 штуки

Ожидается: предложение на 2 шт. и 129 840 ₸ с кнопкой «Да, добавить». Корзина пока не изменилась.

Предложение добавить

Подтвердить: нажать «Да, добавить» или написать да, добавь. Только теперь товар попадает в корзину.

Проверка остатка: добавь 4 штуки → отказ: «доступно 5 шт., в корзине уже 2 шт.»

Отказ сверх остатка

Попробуйте обойти:

  • да без предложения → «нечего подтверждать»;
  • повторное «да» → количество не удваивается;
  • два предложения подряд → подтверждается только последнее.

5. Ссылка на корзину

После подтверждения появляется панель корзины: позиции, сумма и ссылка «Перейти к корзине».

Корзина

Сводка

#Требование кейсаЗапрос для проверкиСтатус
1Наличие, характеристики, сертификатЕсть 027228 в Алматы?, покажи сертификат✅
2Аналог с обоснованием при нулевом остаткеНужен 027230✅
3Оплата, доставка, минимальная партияУсловия доставки и оплаты?✅ (демо-условия)
4Корзина только после «да, добавь», не больше остаткадобавь 2 штуки → да, добавь → добавь 4 штуки✅
5Прямая ссылка на корзинупосле подтверждения✅ (демокорзина)

Казахский язык и мобильная версия

Казахский (опционально, требуется LLM)

Ввести по очереди:

  1. Сәлеметсіз бе! 027228 Астанада бар ма?
  2. екеуін себетке қосыңызшы
  3. иә, қосыңыз

Ассистент понимает казахский (в том числе числа словами: «екеуін» = 2), отвечает по-казахски и добавляет товар после «иә». Цифры в ответе те же, что в каталоге.

Казахский

Мобильная версия

Вёрстка адаптивная. Автотест проверяет ширину 390 px (iPhone): горизонтальной прокрутки нет. Проверить вручную можно так: F12 → значок телефона.

Мобильная версия

Архитектура

flowchart LR
    U[Покупатель] --> UI[Чат<br/>React + TypeScript + Vite]
    UI -->|/api| API[FastAPI<br/>backend/app/main.py]
    API --> ORCH[Оркестратор<br/>assistant/orchestrator.py]
    ORCH --> SEARCH[Поиск<br/>retrieval.py]
    ORCH --> ANALOG[Аналоги<br/>analogs.py]
    ORCH -. только разбор текста<br/>и перевод ответа .-> LLM[Gemini<br/>llm_client.py]
    SEARCH --> CAT[Каталог и остатки<br/>catalog.py / inventory.py]
    ORCH --> FAQ[Условия покупки<br/>faq.py]
    API --> CART[Корзина<br/>cart/adapter.py]
    CART --> INV2[Повторная проверка остатка]
    CAT --> DATA[(dataset/*.json<br/>выгрузка ekt.kz)]
    CAT --> DEMO[(demo_data/*.json<br/>синтетика, помечена)]

Как обрабатывается сообщение:

  1. Детерминированный разбор (мгновенно): артикул, код производителя, слова из названия с синонимами («автомат» → «АВ»), количество (в том числе «две штуки», «екеуін»), город («в Астане» → склад «Нур-Султан»), намерение (добавить, аналог, сертификат, условия).
  2. LLM подключается только если нужна: если детерминированный разбор ничего не нашёл, Gemini превращает живую фразу в поисковый запрос. Казахский ответ Gemini переписывает по-человечески.
  3. Ответ собирается только из данных: цена, остаток, характеристики, аналоги.
  4. Корзина: агент возвращает только намерение «добавить N шт.». Backend создаёт одноразовое предложение, ждёт подтверждения и повторно проверяет остаток.

Подробнее: docs/ARCHITECTURE.md.

API

МетодПутьНазначение
GET/api/v1/healthПроверка сервера
POST/api/v1/sessionsНовая анонимная сессия
POST/api/v1/sessions/{id}/messagesСообщение в чат → ответ, карточки товаров, предложение добавить; при текстовом «да» — обновлённая корзина
POST/api/v1/sessions/{id}/confirmationsСоздать предложение (кнопка «Добавить» на карточке)
POST/api/v1/sessions/{id}/confirmations/{cid}/confirmПодтвердить предложение (кнопка «Да, добавить»)
GET/api/v1/sessions/{id}/cartТекущая корзина и ссылка на неё

Интерактивная документация: http://127.0.0.1:8000/docs


Безопасность и достоверность

Требование кейсаКак обеспечено
Корзина не меняется без подтвержденияАгент не имеет доступа к корзине. Предложение одноразовое, живёт 5 минут и привязано к сессии, товару и количеству. Текстом подтверждается только последнее предложение
Защита от несанкционированного измененияЧужая сессия не может подтвердить предложение (HTTP 409). Повторное подтверждение не удваивает количество
Не больше остаткаОстаток проверяется при создании предложения и ещё раз перед записью, с учётом того, что уже лежит в корзине
Недостоверные цены и наличиеЧисла берутся только из каталога. Все цифры в ответе LLM сверяются с данными: если модель изменила или добавила число, её ответ отбрасывается. Неизвестный остаток показывается как «не уточнено», а не 0. Противоречия в данных (160/250 А) показываются
Объяснимость аналоговВ карточке перечислено, что совпадает, что отличается, сколько в наличии, и есть напоминание проверить совместимость
Платёжные данныеНе запрашиваются и не хранятся. Об этом написано в интерфейсе и в ответах об оплате
ПриватностьСессия анонимная, персональные данные не собираются. Ключ LLM хранится только на сервере в backend/.env, в git не попадает
Текст каталога и сообщения клиента как инструкцииНе выполняются: LLM получает их как данные, а цены и количество из сообщения клиента игнорируются

Данные

ФайлЧто внутриИсточник
dataset/api_products.json, api_products_page2.json40 товаров: название, артикул, цена, фото, ссылкаВыгрузка ekt.kz от партнёра
dataset/api_products_detail.jsonТовар 515291 (027228): характеристики и остатки по 24 складамВыгрузка ekt.kz от партнёра
demo_data/stock_overrides.jsonОстатки для 4 товаров, чтобы показать сценарий «нет в наличии → аналог»Синтетика, в интерфейсе помечена «демо»
demo_data/faq.jsonОплата, доставка, минимальная партия, возврат (с ключевыми словами на русском и казахском)Допущение демомагазина, помечено в ответе

Кейс разрешает синтетические данные с сохранением структуры полей. Реальные данные выгрузки не изменяются и не смешиваются с синтетикой.


Тесты

# Backend: 18 тестов по всем обязательным требованиям (без LLM, ключ не нужен)
python -m pip install -r backend/requirements-dev.txt
python -m pytest backend/tests -q

# Frontend e2e: 5 сценариев в браузере, включая мобильную вёрстку (нужен запущенный backend)
cd frontend
$env:E2E_LIVE="1"
$env:CHROME_PATH="C:\Program Files\Google\Chrome\Application\chrome.exe"   # или: npx playwright install
npx playwright test tests/live.spec.ts

Скриншоты для README пересоздаются командой $env:SCREENSHOTS="1"; npx playwright test tests/readme-screenshots.spec.ts.


Ограничения

  • Данные. Реальные остатки есть только у одного товара из 40, у остальных наличие «не уточнено». Сертификатов и условий покупки в выгрузке нет.
  • Корзина демонстрационная. Она хранится в памяти сервера и сбрасывается при перезапуске. Настоящая корзина ekt.kz подключается реализацией CartAdapter (cart/adapter.py) без изменения логики подтверждения.
  • Вложения (фото, Excel, Word, PDF) не реализованы.
  • Сопутствующие товары не показываются: в поле RECOMMEND указаны товары, которых нет в выгрузке, а выдумывать их карточки мы не стали.
  • История для авторизованных пользователей не реализована: в прототипе нет авторизации.
  • Эскалация на менеджера частичная: при неизвестных данных ассистент советует обратиться к менеджеру, но диалог менеджеру не передаётся.
  • LLM на бесплатном тарифе иногда отвечает медленно. В этом случае приходит ответ из данных на русском, функциональность не ломается.

Структура репозитория

backend/
  app/
    main.py                  HTTP API, сессии, подтверждение корзины
    catalog.py               загрузка каталога, поиск, синонимы
    inventory.py             остатки по складам и городам
    faq.py                   условия покупки
    cart/adapter.py          MockCartAdapter: предложение → подтверждение → проверка остатка
    assistant/
      orchestrator.py        логика ассистента: намерения, ответы, аналоги, корзина
      retrieval.py           поиск товаров по артикулу, коду и названию
      analogs.py             объяснимый подбор аналогов
      llm_client.py          OpenAI-совместимый клиент (Gemini) и проверка чисел
  tests/                     автотесты backend
  .env.example               пример настроек LLM
frontend/
  src/main.tsx               интерфейс чата, карточки, корзина
  src/api.ts                 работа с API
  tests/                     e2e-тесты Playwright
dataset/                     выгрузка ekt.kz (40 товаров + 1 подробная карточка)
demo_data/                   синтетические остатки и условия демомагазина
docs/
  ARCHITECTURE.md            подробная архитектура и контракты
  screenshots/               скриншоты из README
Звёзды
⭐ 0
Форки
0
Язык
Python
Последний push
в прошлом месяце
Коммиты
52
Авторы
3
Файлы
60
Ветки
7

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

Dinara Abdukhamitova 26Kurmanay Zhanel 14DaniyaSeitzhaparova 12

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

  • 2026-09-23 17:59docs: explain switch from NVIDIA to GeminiDinara Abdukhamitova
  • 2026-09-23 17:57Merge branch 'main' of https://github.com/BAITC-Hacks/hack-682bab9a-til-3am into integration/demoDinara Abdukhamitova
  • 2026-09-23 17:57test(frontend): live e2e demo scenario and mobile layout checkDinara Abdukhamitova
  • 2026-09-23 17:56docs: drop demo-script reference from READMEDinara Abdukhamitova
  • 2026-09-23 17:52test: configure backend test importsDaniyaSeitzhaparova
  • 2026-09-23 17:52test: cover five required assistant scenariosDaniyaSeitzhaparova
  • 2026-09-23 17:52test: add backend test dependenciesDaniyaSeitzhaparova
  • 2026-09-23 17:49chore: default frontend env to live mode, drop dead LLM helperDinara Abdukhamitova
Все коммиты (52)
  • 2026-09-23 17:47docs: jury-facing README with setup, verification steps and screenshotsDinara Abdukhamitova
  • 2026-09-23 17:35Merge remote-tracking branch 'origin/feature/backend' into feature/backendKurmanay Zhanel
  • 2026-09-23 17:34Improve README for project demoKurmanay Zhanel
  • 2026-09-23 17:34feat: Gemini fallback chain, Kazakh quantities and fast deterministic pathDinara Abdukhamitova
  • 2026-09-23 17:29Initial backend commitKurmanay Zhanel
  • 2026-09-23 17:15feat: LLM for Russian/Kazakh understanding and natural answers with number guardDinara Abdukhamitova
  • 2026-09-23 17:08feat: confirm cart by text, refresh UI labels and READMEDinara Abdukhamitova
  • 2026-09-23 16:59merge: NVIDIA model selection and fallback logging from feature/backendDinara Abdukhamitova
  • 2026-09-23 16:51fix: city-aware product cards and honest cart refusal messageDinara Abdukhamitova
  • 2026-09-23 16:49chore: select available NVIDIA chat modelDaniyaSeitzhaparova
  • 2026-09-23 16:46feat: analogs, demo FAQ, certificates, city stock and cart limitsDinara Abdukhamitova
  • 2026-09-23 16:41chore: use available NVIDIA model in env exampleDaniyaSeitzhaparova
  • 2026-09-23 16:35style(frontend): restore previous green paletteDinara Abdukhamitova
  • 2026-09-23 16:34fix: log NVIDIA adapter fallback reasonDaniyaSeitzhaparova
  • 2026-09-23 16:30fix(frontend): recover expired chat sessionsDinara Abdukhamitova
  • 2026-09-23 16:28fix(agent): avoid repeating previous product for unrelated queriesDinara Abdukhamitova
  • 2026-09-23 16:19merge: update backend conflict labels and NVIDIA fallbackDinara Abdukhamitova
  • 2026-09-23 16:18feat: add NVIDIA LLM adapter with fallbackDaniyaSeitzhaparova
  • 2026-09-23 16:16Normalize product conflict warning labelsKurmanay Zhanel
  • 2026-09-23 16:13fix(frontend): translate catalog conflict warningDinara Abdukhamitova
  • 2026-09-23 16:13Merge remote-tracking branch 'origin/feature/backend' into feature/backendKurmanay Zhanel
  • 2026-09-23 16:13Add optional NVIDIA client configurationKurmanay Zhanel
  • 2026-09-23 15:50fix: parse quantity from follow-up messagesDaniyaSeitzhaparova
  • 2026-09-23 15:47feat(frontend): handle live pending confirmationsDinara Abdukhamitova
  • 2026-09-23 15:45fix: resolve follow-up product from chat historyDaniyaSeitzhaparova
  • 2026-09-23 15:39merge: update backend live confirmation flowDinara Abdukhamitova
  • 2026-09-23 15:34merge: assemble backend AI and frontend demoDinara Abdukhamitova
  • 2026-09-23 15:33merge: integrate backend and assistant servicesDinara Abdukhamitova
  • 2026-09-23 15:28feat(frontend): add confirmed mock cart flowDinara Abdukhamitova
  • 2026-09-23 15:24feat(frontend): connect backend v1 assistant APIDinara Abdukhamitova
  • 2026-09-23 15:23Expose pending cart confirmation in messagesKurmanay Zhanel
  • 2026-09-23 15:20feat: add assistant MVP orchestrationDaniyaSeitzhaparova
  • 2026-09-23 15:18Add confirmed mock cart flowKurmanay Zhanel
  • 2026-09-23 15:13style(frontend): align UI palette with EKT storefrontDinara Abdukhamitova
  • 2026-09-23 15:12Connect assistant handler to backend servicesKurmanay Zhanel
  • 2026-09-23 15:11feat: add assistant MVP orchestrationDaniyaSeitzhaparova
  • 2026-09-23 15:10Add assistant module boundariesKurmanay Zhanel
  • 2026-09-23 15:08Add backend contracts and data repositoriesKurmanay Zhanel
  • 2026-09-23 15:02Update dataset directory pathKurmanay Zhanel
  • 2026-09-23 14:54Merge branch 'main' of github.com:BAITC-Hacks/hack-682bab9a-til-3amKurmanay Zhanel
  • 2026-09-23 14:52refactor(frontend): use synthetic public demo catalogDinara Abdukhamitova
  • 2026-09-23 14:47Add initial assistant prototypeKurmanay Zhanel
  • 2026-09-23 14:45docs: update project READMEDaniyaSeitzhaparova
  • 2026-09-23 14:44feat(frontend): initialize chat interfaceDinara Abdukhamitova
Структура репозитория (60 файлов)
.gitignore
README.md
backend/.env.example
backend/app/__init__.py
backend/app/assistant/__init__.py
backend/app/assistant/analogs.py
backend/app/assistant/contracts.py
backend/app/assistant/llm_client.py
backend/app/assistant/nvidia.py
backend/app/assistant/orchestrator.py
backend/app/assistant/retrieval.py
backend/app/cart/__init__.py
backend/app/cart/adapter.py
backend/app/catalog.py
backend/app/contracts.py
backend/app/faq.py
backend/app/inventory.py
backend/app/main.py
backend/requirements-dev.txt
backend/requirements.txt
backend/tests/conftest.py
backend/tests/test_assistant.py
dataset/api_products.json
dataset/api_products_detail.json
dataset/api_products_page2.json
demo_data/faq.json
demo_data/stock_overrides.json
docs/ARCHITECTURE.md
docs/screenshots/01-start.png
docs/screenshots/02-stock-specs.png
docs/screenshots/03-proposal.png
docs/screenshots/04-cart.png
docs/screenshots/05-stock-limit.png
docs/screenshots/06-analog.png
docs/screenshots/07-terms.png
docs/screenshots/08-kazakh.png
docs/screenshots/09-mobile.png
frontend/.env.example
frontend/.gitignore
frontend/PROMPT.md
frontend/README.md
frontend/index.html
frontend/package-lock.json
frontend/package.json
frontend/playwright.config.ts
frontend/src/App.tsx
frontend/src/api.ts
frontend/src/cart.css
frontend/src/demoCatalog.ts
frontend/src/env.d.ts
frontend/src/main.tsx
frontend/src/style.css
frontend/src/styles.css
frontend/src/types.ts
frontend/src/vite-env.d.ts
frontend/tests/chat.spec.ts
frontend/tests/live.spec.ts
frontend/tests/readme-screenshots.spec.ts
frontend/tsconfig.json
frontend/vite.config.ts
Ветки (7)

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

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

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