О проекте
Команда: SKMA-
Кейс HackAlem.ai: Торговля — Электрокомплект · ekt.kz
Стадия: Доведённый продукт
Задача кейса: AI-консультант по каталогу ekt.kz: товары, аналоги, цены, остатки, корзина
Суть
Чат-консультант интернет-магазина ekt.kz: нечёткий поиск товаров, карточки с ценой и остатками по складам, подбор аналогов, условия оплаты/доставки, корзина с подтверждением и загрузка спецификаций CSV/XLSX/DOCX.
Как устроено
LangGraph-граф: guard (код, а не модель) обрабатывает «да/нет» на ожидающее предложение, остальное идёт в agent ⇄ ToolNode с инструментами search_products, get_product, find_analogs, get_purchase_terms, propose_add_to_cart (LangChain ChatOpenAI, около 6 раундов). Поиск через RapidFuzz с синонимами по локальному каталогу, карточки из API ekt.kz с кэшем в SQLite, корзины и pending actions в PostgreSQL. Без ключа работает режим правил по ключевым словам. Если в вопросе есть казахские буквы, готовый ответ переписывает отдельная модель, числовые фрагменты при этом сверяются.
Ключевые решения
- LangGraph
- LangChain tools
- ToolNode
- InMemorySaver checkpointer
- RapidFuzz fuzzy search
- spec file parsing
- Kazakh answer rewriting
- rule-based fallback
Особенности
Гибрид для казахского языка: ответ переписывает Sherkala-8B, при изменении чисел возвращается исходный ответ. Загрузка спецификации с сопоставлением строк каталогу и статусами наличия. Guard-узел в коде не даёт LLM менять корзину без явного «да».
Стек
Python · FastAPI · PostgreSQL 17 · SQLAlchemy AsyncIO · asyncpg · SQLite · HTTPX · Next.js 16 · React 19 · Tailwind CSS 4 · Docker Compose · Make · pytest
Модели
OpenAI gpt-5.4-mini · qwen/qwen3-next-80b-a3b-instruct (NVIDIA) · inception42/Llama-3.1-Sherkala-8B-Chat (vLLM)
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-f80b8ce9-skma.
README
README.mdИИ-консультант для ekt.kz
Прототип для кейса HackAlem AI: чат-консультант интернет-магазина электротехники ТОО «Электрокомплект». Помогает покупателю найти товар по артикулу или описанию, узнать цену и наличие, подобрать альтернативу и подготовить корзину.
Что реализовано
- Поиск по артикулу, коду производителя и названию, включая нечёткие совпадения и отдельные синонимы: например, «автомат» → «АВ».
- Карточки товаров: цена в тенге, общий остаток и остатки по складам, характеристики, кратность, ссылка на товар.
- Подбор аналогов в наличии из той же серии или категории с объяснением совпавших характеристик и параметров названия.
- Ответы об оплате, доставке и минимальной партии из локального файла условий.
- Предложение добавить товар в корзину, подтверждение или отказ. Корзина меняется после кнопки «Да, добавить» либо короткого подтверждения в чате при наличии ожидающего предложения.
- Страница корзины
/cart/[id]с количеством, стоимостью позиций и итогом. - Адаптивный чат с карточками товаров, состояниями загрузки и повтором запроса при ошибке; на небольшом экране чат занимает весь экран. При недоступности карточек каталог возвращает 503 вместо пустой выдачи, а чат показывает «Каталог временно недоступен» с кнопкой повтора. На мобильном экране открытый чат блокирует прокрутку фоновой страницы.
- Два режима ассистента: LLM с вызовом инструментов и резервный режим правил. Для вопросов с характерными буквами казахского языка можно включить отдельную модель, которая переписывает готовый ответ на казахский.
- Загрузка спецификации
.csv,.xlsxили.docxкнопкой 📎 (до 2 МБ, первые 50 товарных строк): по каждой строке отображаются найденный товар, остаток и статус «В наличии», «Частично», «Нет в наличии» или «Не найдено». Для отсутствующего товара сервис пытается подобрать аналог; число таких проверок ограничено десятью. Содержание последней загруженной спецификации доступно LLM в следующих сообщениях этой сессии, поэтому можно попросить «добавь из файла автомат на 250А».
Как работает решение
- Покупатель открывает чат и вводит артикул или описание товара.
- Backend ищет кандидатов в локальном списке каталога, получает подробные карточки через API ekt.kz или из кэша.
- Ассистент формирует ответ, а интерфейс показывает структурированные карточки. Для отсутствующего товара предусмотрен подбор аналогов.
- По просьбе «добавь 2 шт» создаётся ожидающее предложение. Само предложение ещё не добавляет позицию в корзину.
- После подтверждения backend повторно запрашивает карточку, ограничивает количество доступным остатком с учётом уже добавленного и сохраняет корзину. При сбое API возможен возврат к кэшированным данным — см. ограничения ниже.
- Покупатель может загрузить спецификацию: backend разбирает файл, сопоставляет строки с каталогом и показывает результат в чате. Загрузка сама по себе не добавляет товары в корзину.
- Покупатель переходит на страницу корзины. Кнопка оформления открывает ekt.kz; автоматического переноса позиций на сайт партнёра пока нет.
LLM-режим построен на LangGraph (agent_graph.py):
START → guard ─┬─ confirm ─────────▶ END короткое «да» на открытое предложение → корзина
├─ reject ──────────▶ END «нет» → предложение отменено
└─ agent ⇄ tools ───▶ END ChatOpenAI + инструменты каталога
guard— код, а не модель: корзину меняет только явное «да» на последнее предложение.agent— LangChainChatOpenAIс инструментами@tool:search_products,get_product,find_analogs,get_purchase_terms,propose_add_to_cart. Инструменты выполняетToolNode, лимит — около шести раундов «модель ⇄ инструменты».- История диалога — в чекпойнтере LangGraph (поток =
session_id). В модель уходят последние 30 сообщений.
При пустом ключе LLM или ошибке её API используется обработка по ключевым словам и те же сервисы каталога и корзины. История LLM-диалога этой сессии при ошибке сбрасывается.
Технологии
- Backend: Python 3.10+, FastAPI, Uvicorn, Pydantic Settings.
- Хранение: PostgreSQL 17, SQLAlchemy AsyncIO и asyncpg; SQLite для кэша карточек.
- Поиск и интеграции: RapidFuzz, HTTPX.
- AI: LangGraph (граф диалога, чекпойнтер истории), LangChain (
ChatOpenAI,@tool). По умолчанию — OpenAIhttps://api.openai.com/v1, модельgpt-5.4-mini. Подходит любой OpenAI-совместимый API с вызовом инструментов, например build.nvidia.com с модельюqwen/qwen3-next-80b-a3b-instructили свой NIM на NVIDIA Brev. - Дополнительная модель для казахского: при настроенном
KAZAKH_LLM_BASE_URLсервис обнаруживает характерные казахские буквы в вопросе и отправляет готовый ответ на переписывание отдельной OpenAI-совместимой модели. В конфигурации указанаinception42/Llama-3.1-Sherkala-8B-Chatдля vLLM. При ошибке модели, пустом ответе или изменении набора числовых фрагментов возвращается исходный ответ. Основная модель для диалога может быть OpenAI, NVIDIA или другим совместимым сервисом. Инструкция по NVIDIA API. - Frontend: TypeScript, Next.js 16 App Router, React 19, Tailwind CSS 4.
- Запуск и проверки: Docker Compose, Make, pytest, Ruff, ESLint, TypeScript. Тесты backend используют SQLite в памяти через aiosqlite и подмены внешних API.
Архитектура проекта
Браузер обращается к /api/* на Next.js, который проксирует запросы в FastAPI.
Маршруты backend передают работу сервисам:
agent_graph— граф LangGraph: guard, агент с инструментами, чекпойнтер;assistant_service— инструменты для LLM, подтверждение корзины, резервные правила;kazakh_service— обнаружение казахских букв и необязательное переписывание ответа;attachment_service— разбор спецификаций и сопоставление строк с каталогом;search_service— поиск и ранжирование аналогов;catalog_service— локальный список товаров, API ekt.kz, кэш в памяти и SQLite;cart_service— корзины и ожидающие подтверждения в PostgreSQL.
История LLM-диалогов хранится в InMemorySaver, а контекст последней спецификации —
в памяти процесса backend. Идентификаторы сессии и корзины frontend сохраняет в
localStorage.
backend/
app/api/routes/ HTTP-маршруты chat, catalog, cart, health, items
app/services/ ассистент, поиск, каталог, корзина, файлы и казахский ответ
app/models/ таблицы carts, cart_items, pending_actions
app/schemas/ модели запросов и ответов
app/core/ настройки и подключение к БД
data/ образец каталога, условия покупки, локальный каталог и кэш
scripts/ синхронизация каталога, прогрев кэша, проверка LLM
tests/ тесты и примеры ответов API
frontend/
app/_components/ чат, карточки, таблица спецификации и подтверждение
app/cart/[id]/ страница корзины
public/guide/ иллюстрации пяти шагов на главной
lib/api.ts типизированный клиент backend
examples/ пример CSV-спецификации
docs/ отслеживаемый отчёт, скриншоты и требования безопасности
Makefile команды установки, запуска и проверки
docker-compose.yml PostgreSQL, backend и frontend
Установка и запуск
Все команды выполняются из корня репозитория. Нужен доступ к партнёрскому API для загрузки каталога и карточек. Ключ LLM необязателен.
Настройка окружения
Создайте файл настроек, если его ещё нет:
cp backend/.env.example backend/.env
В backend/.env укажите:
EKT_API_USER,EKT_API_PASSWORD— выданные партнёром учётные данные;EKT_API_URL— по умолчаниюhttps://ekt.kz/api;EKT_SYNC_PAGES— число страниц для синхронизации,0означает все;OPENAI_API_KEY— ключ выбранного LLM-сервиса; оставьте пустым для режима правил;OPENAI_BASE_URL,OPENAI_MODEL— адрес сервиса и имя модели; по умолчаниюhttps://api.openai.com/v1иgpt-5.4-miniв коде; примерbackend/.env.exampleзадаёт NVIDIAhttps://integrate.api.nvidia.com/v1иqwen/qwen3-next-80b-a3b-instruct;OPENAI_REASONING_EFFORT— для reasoning-моделей (minimal/low/medium), пусто — значение модели по умолчанию;KAZAKH_LLM_BASE_URL,KAZAKH_LLM_API_KEY,KAZAKH_LLM_MODEL— казахская модель для гибридного режима (vLLM на Brev); пустой адрес — режим выключен;DATABASE_URL— для локального backend по умолчаниюpostgresql+asyncpg://app:app@localhost:5432/ekt.
В Docker Compose адрес БД для backend задаётся автоматически. Параметры PostgreSQL
POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB можно задать в корневом .env;
при локальном запуске согласуйте с ними DATABASE_URL. Реальные ключи и пароли
не добавляйте в Git.
Вариант 1: Docker
Требования: Docker с актуальным Compose v2 и поддержкой env_file.required, Make;
свободные порты 3000, 8000 и 5432.
make demo
Команда скачивает каталог, если backend/data/catalog.json отсутствует,
прогревает карточки демо-товаров и аналогов, затем собирает и запускает стек.
Отдельные шаги:
make docker-sync-catalog
make docker-warm-cache
make up
Для повторного запуска с подготовленными данными достаточно make up.
make logs показывает логи, make down останавливает стек.
PostgreSQL хранит данные в volume pgdata, каталог и кэш — в подключённой
папке backend/data.
Вариант 2: локальная разработка
Требования: Python 3.10+ с модулем venv, Node.js 22 (как в Dockerfile), npm, Make
и Docker Compose для PostgreSQL. Настройте backend/.env, как описано выше.
make install
cp frontend/.env.example frontend/.env.local
make sync-catalog
make warm-cache
make dev
Не перезаписывайте существующий frontend/.env.local; проверьте в нём
BACKEND_URL=http://localhost:8000. make dev поднимает PostgreSQL в Docker,
backend и frontend локально. Если полный Docker-стек уже запущен, сначала
выполните make down, чтобы освободить порты.
Адреса после запуска
- Интерфейс: localhost:3000.
- Swagger с маршрутами и схемами API: localhost:8000/docs.
- Проверка backend: localhost:8000/api/health.
- Проверка БД: localhost:8000/api/health/db.
Как проверить решение
Сценарий для жюри
После загрузки каталога и прогрева кэша откройте интерфейс и нажмите «Консультант».
Для воспроизводимой проверки режима правил оставьте OPENAI_API_KEY пустым.
Внешний API ekt.kz с учётными данными всё равно нужен для карточек, остатков
и проверки товарных строк спецификации.
- Введите
Есть в наличии 200300285_?— проверьте карточку, цену, остаток и склады. - Введите
Есть в наличии 150100218_?— если товар отсутствует, появятся аналоги с обоснованием. Остатки зависят от текущих данных API и кэша. - Спросите
Какие условия доставки и оплаты? Минимальная партия?— получите ответ из локального файла условий. - Снова выберите товар в наличии, затем введите
добавь 2 шт— появится предложение с кнопками подтверждения и отказа. - Нажмите «Нет» — позиция не должна добавиться. Повторите запрос и нажмите «Да, добавить» — откройте корзину и проверьте количество и итог.
- Запросите количество больше остатка — предложенное количество должно быть ограничено наличием. Сам запрос ещё не подтверждает добавление.
- Нажмите 📎 и загрузите пример спецификации — появится
таблица со статусом каждой позиции. Статусы и аналоги зависят от актуального каталога.
Затем напишите
из файла добавь автомат на 250А — сколько есть(нужен ключ LLM). - Если настроена вторая модель, задайте вопрос с казахскими буквами, например
Бұл автомат қоймада бар ма?. При недоступности второй модели ответ останется от основного ассистента.
Формат файла: первая строка с колонками (можно после заголовка документа)
«Артикул» / «Код», «Наименование» и «Количество». Достаточно одной из первых двух.
Разделитель CSV — ; или ,, кодировка UTF-8 или Windows-1251. В Word берётся первая
таблица с такими колонками (таблица реквизитов пропускается); если таблиц нет —
строки текста с разделителем Tab, ; или |. Файл без заголовка тоже читается:
первая колонка — товар, числовая — количество.
Автоматические проверки
После make install:
make check # Ruff, ESLint, TypeScript и pytest
make build # production-сборка frontend
Отдельно доступны make test, make lint и make typecheck. Тесты не требуют
партнёрских ключей, работающей LLM или PostgreSQL: используются фикстуры,
подменённые HTTP-ответы и SQLite в памяти. Покрыты поиск, адаптация карточек,
аналоги, сценарии чата, подтверждение и отказ, ограничения количества,
разбор CSV/XLSX/DOCX, обработку сбоев каталога и казахское переписывание.
Для запущенного приложения:
curl -fsS http://localhost:8000/api/health
curl -fsS http://localhost:8000/api/health/db
curl -fsS http://localhost:3000/api/health
Все три запроса должны вернуть {"status":"ok"}. Последний также проверяет
проксирование через Next.js.
make llm-smoke прогоняет настоящий граф LangGraph с реальным каталогом и LLM на
демо-вопросах и печатает ответы, вызванные инструменты и время ответа. Для него
нужны ключ LLM, доступ к API партнёра и подготовленный каталог. Сравнение моделей:
make llm-smoke MODELS="gpt-5.4-mini gpt-4.1-mini".
Отчёт о проверке интерфейса и известных ограничениях содержит скриншоты и результаты отдельного прогона проекта.
Данные и интеграции
- Партнёрский API ekt.kz с Basic Auth:
/api/products?page=Nдля списка товаров и/api/products/detail?id=...для подробностей, остатков и свойств. - Локальный каталог:
make sync-catalogсохраняет список вbackend/data/catalog.json. Если файла нет, поиск используетbackend/data/catalog_sample.json. Образец содержит список товаров, но не заменяет API подробных карточек и не обеспечивает автономное демо. - Кэш карточек: память процесса и
backend/data/details_cache.sqlite. Срок актуальности по умолчанию — 600 секунд; при ошибке API сервис может вернуть сохранённую карточку и после истечения этого срока. - Условия покупки: backend/data/terms.md. В файле указаны публичные страницы-источники; обновление условий вручную.
- LLM: получает историю диалога и результаты инструментов через настроенный OpenAI-совместимый сервис. Структурированные карточки формируются из данных каталога, а текст ответа в LLM-режиме генерирует модель.
Полный каталог, кэш и файлы .env исключены из Git. В docs/ уже отслеживаются
отчёт, скриншоты и требования безопасности; новые локальные файлы в этой папке
игнорируются. Пример для загрузки хранится в examples/.
Ограничения текущей версии
- Это прототип: оформление заказа и перенос корзины в систему партнёра не реализованы. Кнопка оформления открывает главную страницу ekt.kz.
- Корзина не резервирует товар на складе. При недоступности API даже повторная проверка перед подтверждением может использовать устаревший кэш.
- Одновременные подтверждения не защищены отдельной блокировкой строк БД; последовательный повтор проверяется тестами.
- Кратность выводится в карточке, но не применяется как обязательное ограничение количества при добавлении.
- Авторизации пользователей нет; доступ к корзине определяется её случайным идентификатором. История диалогов теряется при перезапуске backend и не разделяется между несколькими процессами.
- Аналоги выбираются эвристически по категории, названию и совпадению свойств; техническая взаимозаменяемость не гарантируется.
- Промпт требует от LLM использовать данные инструментов, однако отдельной проверки всех фактов в сгенерированном тексте нет. Проверка казахского переписывания сравнивает набор числовых фрагментов, но не проверяет сохранность названий и смысл каждого предложения.
- Определение казахского языка основано на наличии характерных букв; интерфейс и ответы режима правил преимущественно русскоязычные. Дополнительная модель выключена, пока не задан её адрес.
- Сертификаты не загружаются:
certificate_urlсейчас всегда пуст. Вложения поддерживаются только.csv,.xlsx(первый лист) и.docx. Старый.doc, PDF и распознавание фото не реализованы. Добавить все строки спецификации в корзину одним подтверждением нельзя: позиции добавляются по одной. - Внешние API влияют на доступность и скорость ответа. Условия покупки в локальном файле необходимо подтвердить у партнёра перед рабочим запуском.
Развёрнутая версия
Публичная ссылка на развёрнутый прототип в репозитории не указана. Для проверки используйте локальный запуск по инструкции выше. Сайт ekt.kz — сайт партнёра, а не развёрнутая версия этого приложения.
Авторы коммитов
История коммитов
- 2026-09-23 17:48docs: restore test screenshots under assetsYersultan
- 2026-09-23 17:45docs: remove PNG files from docs rootYersultan
- 2026-09-23 17:42docs: rename report to testreportYersultan
- 2026-09-23 17:40Merge branch 'main' of https://github.com/BAITC-Hacks/hack-f80b8ce9-skmaSabyr
- 2026-09-23 17:39docs: record visual tests with live OpenAI APIYersultan
- 2026-09-23 17:39docs: collect user guide resources in assetsSabyr
- 2026-09-23 17:39docs: move NVIDIA instructions into docsmaxat
- 2026-09-23 17:38docs: align README with current projectmaxat
Все коммиты (28)
- 2026-09-23 17:37docs: move user documentation into docsSabyr
- 2026-09-23 17:31Merge branch 'main' of https://github.com/BAITC-Hacks/hack-f80b8ce9-skmaSabyr
- 2026-09-23 17:31docs: add illustrated user guide and PDFSabyr
- 2026-09-23 17:28feat(assistant): add Kazakh reply rewriting modemaxat
- 2026-09-23 17:27fix: clarify catalog failures and improve mobile chat usabilityYersultan
- 2026-09-23 17:15docs: update report with latest README test results and screenshotsYersultan
- 2026-09-23 17:13docs: rename Docs directory to lowercase docsYersultan
- 2026-09-23 16:59feat: support specification uploads and guided chat UImaxat
- 2026-09-23 16:55docs: add test report and group security documentation under DocsYersultan
- 2026-09-23 16:26chore: ignore local Hackton2026 workspace fileYersultan
- 2026-09-23 16:23docs: remove README prompt file with Windows-incompatible nameYersultan
- 2026-09-23 16:22feat(assistant): connect LLM through LangGraph toolsmaxat
- 2026-09-23 16:12docs: add security requirements and acceptance criteriaSabyr
- 2026-09-23 15:09chore(git): merge GitHub repository historymaxat
- 2026-09-23 15:06docs: update Russian README for hackathon jurymaxat
- 2026-09-23 15:02chore(git): ignore local documentationmaxat
- 2026-09-23 14:59feat(backend): add catalog search, assistant and confirmed cartsmaxat
- 2026-09-23 14:59feat(frontend): add consultant chat and cart pagesmaxat
- 2026-09-23 14:59chore(dev): configure database and Docker demo workflowmaxat
- 2026-09-23 12:43Initial commitmaxat
Структура репозитория (115 файлов)
.agents/skills/add-api-endpoint/SKILL.md .agents/skills/add-frontend-page/SKILL.md .agents/skills/hackathon-readme/SKILL.md .agents/skills/run-and-verify/SKILL.md .codex/skills .gitignore AGENTS.md Makefile README.md backend/.dockerignore backend/.env.example backend/AGENTS.md backend/Dockerfile backend/app/__init__.py backend/app/api/__init__.py backend/app/api/router.py backend/app/api/routes/__init__.py backend/app/api/routes/cart.py backend/app/api/routes/catalog.py backend/app/api/routes/chat.py backend/app/api/routes/health.py backend/app/api/routes/items.py backend/app/core/__init__.py backend/app/core/config.py backend/app/core/db.py backend/app/main.py backend/app/models/__init__.py backend/app/models/cart.py backend/app/schemas/__init__.py backend/app/schemas/cart.py backend/app/schemas/chat.py backend/app/schemas/item.py backend/app/schemas/product.py backend/app/services/__init__.py backend/app/services/agent_graph.py backend/app/services/assistant_service.py backend/app/services/attachment_service.py backend/app/services/cart_service.py backend/app/services/catalog_service.py backend/app/services/item_service.py backend/app/services/kazakh_service.py backend/app/services/search_service.py backend/data/catalog_sample.json backend/data/terms.md backend/pyproject.toml backend/scripts/__init__.py backend/scripts/llm_smoke.py backend/scripts/sync_catalog.py backend/scripts/warm_cache.py backend/tests/__init__.py backend/tests/catalog_data.py backend/tests/conftest.py backend/tests/fixtures/detail_515291.json backend/tests/fixtures/products_page1.json backend/tests/test_cart.py backend/tests/test_catalog.py backend/tests/test_chat.py backend/tests/test_health.py backend/tests/test_items.py backend/tests/test_kazakh.py backend/tests/test_search.py backend/tests/test_upload.py docker-compose.yml docs/.gitattributes docs/README.md docs/assets/01-start.png docs/assets/02-stock.png docs/assets/03-analogs.png docs/assets/04-terms.png docs/assets/05-cart.png docs/assets/fixed-desktop.png docs/assets/fixed-mobile-error.png docs/assets/fixed-mobile-upload.png docs/assets/live-api-boundaries.png docs/assets/live-api-catalog-error.png docs/assets/live-api-desktop.png docs/assets/live-api-file-context.png docs/assets/live-api-mobile.png docs/assets/retest-desktop.png docs/assets/retest-mobile.png docs/assets/retest-upload-desktop.png docs/assets/spec-example.csv docs/docs-security/security-section.md docs/nvida-llm-instructions.md docs/testreport.md docs/user-guide.pdf examples/spec-example.csv frontend/.dockerignore frontend/.env.example frontend/AGENTS.md frontend/Dockerfile frontend/app/_components/assistant-banner.tsx frontend/app/_components/chat-widget.tsx frontend/app/_components/confirm-card.tsx frontend/app/_components/product-card.tsx frontend/app/_components/spec-table.tsx frontend/app/cart/[id]/cart-view.tsx frontend/app/cart/[id]/page.tsx frontend/app/globals.css frontend/app/items.tsx frontend/app/layout.tsx frontend/app/page.tsx frontend/eslint.config.mjs frontend/lib/api.ts frontend/next.config.ts frontend/package-lock.json frontend/package.json frontend/postcss.config.mjs frontend/public/.gitkeep frontend/public/guide/01-start.png frontend/public/guide/02-stock.png frontend/public/guide/03-analogs.png frontend/public/guide/04-terms.png frontend/public/guide/05-cart.png frontend/tsconfig.json
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.