О проекте
Команда: Go Team
Кейс HackAlem.ai: Торговля — Электрокомплект · ekt.kz
Стадия: Рабочий MVP
Задача кейса: AI-консультант по каталогу ekt.kz: товары, аналоги, цены, остатки, корзина
Суть
Чат-консультант по каталогу ekt.kz: поиск по артикулу/названию в локальной SQLite-копии каталога, актуальная карточка из EKT API, подбор аналога при нулевом остатке и demo-корзина с явным подтверждением.
Как устроено
Каталог (15 037 товаров, 752 страницы) синхронизируется из EKT API в SQLite, поиск идёт локально, цены и остатки берутся из EKT detail. /api/chat работает через OpenAI Responses API с function calling поверх сервисов search/detail/analogs; без ключа или при ошибке включается детерминированный fallback (точный артикул + шаблонный ответ). Аналоги ищутся по совпадениям в названиях/свойствах, проверяется до 3 кандидатов. Корзина меняется только после отдельного подтверждения с повторной проверкой остатка.
Ключевые решения
- OpenAI Responses API
- function calling
- SQLite catalog sync
- deterministic fallback
- analog matching by name/properties
- confirmation flow
- HTTPX MockTransport tests
Метрики
Backend-тесты 60/60, frontend 35/35, итоговый QA 117 passed / 4 failed; синхронизировано 15 037 товаров
Особенности
README честно описывает статус: LLM-режим не подтверждён (ключи давали 401), демо идёт через детерминированный fallback. Двухшаговое подтверждение корзины с повторной проверкой остатка и отказ выдумывать товары (503 до синхронизации).
Стек
Python · FastAPI · Uvicorn · HTTPX · Pydantic Settings · SQLite · HTML/CSS/JS ES modules · Node.js
Модели
OpenAI gpt-5.6-luna
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-f6cfd1cd-go-team.
README
README.mdAI-консультант для EKT.kz
Прототип для HACKALEM AI: помогает найти электротехнический товар, проверить актуальную цену и остаток, рассмотреть возможный аналог и продемонстрировать добавление в корзину только после явного подтверждения.
Статус сдачи: backend и frontend объединены в integration/ekt, функции заморожены. Рабочий сценарий демонстрируется через deterministic fallback. Интеграция OpenAI реализована, но успешный LLM-сценарий в нашей среде не подтверждён: использованные API keys возвращали HTTP 401 invalid_api_key. Итоговый QA-отчёт: 117 passed / 4 failed, не полностью зелёный. Известные проблемы перечислены ниже.
Что умеет прототип
- Получает реальный каталог EKT через серверный Basic Auth. Проверенная синхронизация: 15 037 товаров с 752 страниц на 23.09.2026. Это размер конкретного снимка, а не фиксированное число товаров в коде.
- Ищет по
nameиarticleв SQLite без обращения к EKT на каждый поисковый запрос. Точный артикул имеет приоритет, регистр не учитывается, включая кириллицу. - Получает актуальную карточку через EKT detail: цену, общий остаток, склады, описание, свойства и изображение, если они предоставлены источником.
- Предоставляет
POST /api/chat: официальный OpenAI Responses API и function calling как основной режим; при отсутствии ключа или ошибке OpenAI — резервный поиск точного артикула и шаблонный ответ по реальному detail. Fallback — детерминированная логика, не LLM и не AI-модель. - При нулевом остатке пытается найти доступный аналог по совпадениям в реальных названиях/свойствах. Проверяет максимум три detail кандидатов, объясняет совпадения и не гарантирует взаимозаменяемость. Если данных недостаточно, сообщает об этом.
- Ведёт demo/session cart в памяти backend: предложение → отдельное подтверждение → повторная проверка остатка → добавление. Без подтверждения корзина не меняется.
- Включает frontend с поиском, чатом, карточками, отображением аналогов и состояния demo/session cart. Отдельная «Демо-подборка» содержит 8 демонстрационных позиций; она не подменяет реальные ответы API.
Архитектура и стек
Frontend (127.0.0.1:5173)
→ FastAPI (127.0.0.1:8000)
→ Search → SQLite (последний снимок каталога)
→ Detail → EKT API (актуальная карточка)
→ Chat → OpenAI Responses + tools / deterministic fallback
→ существующие Search + Detail
→ AnalogService при нулевом остатке
→ Demo/session cart → подтверждение на сервере → EKT detail
Ручная синхронизация: EKT API → SQLite
| Часть | Технологии |
|---|---|
| Backend | Python, FastAPI, Uvicorn, HTTPX, Pydantic Settings, встроенный sqlite3 |
| OpenAI | Официальный Python SDK openai==2.48.0, Responses API, gpt-5.6-luna по умолчанию; другой моделью автоматически не заменяется |
| Frontend | HTML, CSS, JavaScript ES modules; небольшой Node.js HTTP-сервер, без npm-зависимостей |
| Тесты | Python unittest, fake LLM/HTTPX MockTransport; Node.js test runner |
Основные файлы: backend/main.py — endpoints/CORS; catalog.py — SQLite; sync_catalog.py — синхронизация; ekt_service.py — EKT HTTP-клиент; chat.py / chat_fallback.py — чат; analogs.py — кандидаты замены; demo_cart.py — подтверждение. Frontend находится в frontend/, backend-тесты — в tests/.
Запуск с нуля
Нужны Git, Python 3.9+, Node.js 20+ с npm, доступ к интернету и выданные credentials EKT. Все команды ниже — для macOS/Linux. Backend и frontend запускаются на одном компьютере. SQLite в Git не хранится: на новой машине её нужно загрузить.
1. Получить репозиторий и установить backend
Для клонирования именно финальной сборки ветка integration/ekt должна быть опубликована командой. Этот аудит не выполняет push: пока ветка существует только локально, используйте предоставленную копию этой ветки и начните с создания venv. Более ранний main не равен итоговой сборке.
git clone --branch integration/ekt https://github.com/BAITC-Hacks/hack-f6cfd1cd-go-team.git
cd hack-f6cfd1cd-go-team
python3 -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txt
cp -n .env.example .env
В уже клонированном проекте выполняйте команды из его корня; повторное клонирование не нужно. cp -n сохраняет существующий .env.
2. Настроить локальный .env
EKT_API_BASE_URL=https://ekt.kz/api
EKT_API_USERNAME=
EKT_API_PASSWORD=
OPENAI_API_KEY=
OPENAI_MODEL=gpt-5.6-luna
| Переменная | Назначение |
|---|---|
EKT_API_BASE_URL | Базовый HTTPS-адрес EKT API |
EKT_API_USERNAME, EKT_API_PASSWORD | Заполнить выданными реквизитами для синхронизации и detail |
OPENAI_API_KEY | Необязателен для fallback; нужен для основного LLM-режима |
OPENAI_MODEL | Модель Responses API, по умолчанию gpt-5.6-luna |
CATALOG_DB_PATH | Необязательный путь SQLite; по умолчанию data/catalog.sqlite3 в корне проекта. Для переопределения удобнее абсолютный путь |
Значения credentials вводятся только локально, никогда во frontend и не в Git. Для воспроизводимой fallback-демонстрации оставьте OPENAI_API_KEY пустым: это исключит ожидание ошибочного OpenAI-запроса. Переменные окружения процесса имеют приоритет над .env; после изменения настроек перезапустите сервер.
3. Загрузить реальный каталог
python -m backend.sync_catalog
Команда последовательно получает страницы и печатает прогресс. Загрузка может занять несколько минут. Пустая страница или повтор первой страницы означает конец; существует защитный предел обхода. При временных ошибках предусмотрены ограниченные повторы. Обновление публикуется одной транзакцией; при сбое предыдущий каталог сохраняется. Эта же команда используется для ручного обновления. До первой успешной синхронизации поиск возвращает 503, а не выдуманные товары.
4. Запустить FastAPI
python -m uvicorn backend.main:app --reload --host 127.0.0.1 --port 8000
Документация: http://127.0.0.1:8000/docs. Для MVP используйте один worker: история, pending confirmation и demo-cart хранятся в памяти одного процесса. Перезапуск, в том числе reload, очищает их.
5. Запустить frontend в другом терминале
cd hack-f6cfd1cd-go-team/frontend
npm run dev
Если терминал уже открыт в корне проекта, используйте cd frontend. Устанавливать npm-зависимости не требуется. Откройте http://127.0.0.1:5173. CORS разрешает именно этот origin, GET/POST и Content-Type, без wildcard и cookies. http://localhost:5173 — другой origin. Подробности: frontend/README.md; описания ранних этапов в нём могут отставать от итогового кода и этого README.
Основные API
| Метод | Endpoint | Источник |
|---|---|---|
| GET | /api/products?page=1 | Реальная страница EKT |
| GET | /api/products/search?q=200300285_ | Локальная SQLite |
| GET | /api/products/detail?id=515291 | Актуальный EKT detail |
| POST | /api/chat | OpenAI/tools либо fallback; серверный confirmation flow |
Контракты: поиск и detail, чат, аналоги и demo/session cart.
Запрос чата: {"message":"Есть ли товар 200300285_ в наличии и сколько он стоит?","session_id":null}. Для следующего сообщения передавайте session_id из ответа. Ответ содержит session_id, message, products, analogs, mode, tools_used, cart, pending_confirmation. mode — openai или fallback; детерминированный flow корзины также обозначен fallback.
products[].quantity — остаток EKT; cart.items[].quantity — количество в demo-cart. При сетевых ошибках данные не подменяются демонстрационными. Некорректные параметры дают 422; остальные форматы ошибок описаны в контрактах.
Сценарий демонстрации
- Поиск. В строке каталога введите
200300285_. Ожидается товар ID515291. Наличие в поисковой выдаче не выдаётся за актуальное: для него нужен detail. - Цена и наличие. В чат:
Есть ли товар 200300285_ в наличии и сколько он стоит?. Backend выполняет search и EKT detail; при последней реальной проверке получены цена 64920 и остаток 23. Не фиксируйте эти числа как неизменные. - Отсутствующий товар → аналог. В чат:
Есть ли товар ярп4520 в наличии? Если нет, предложи аналог.При проверке исходный ID45357имел остаток 0, кандидат150200474_/ ID18284— 341. Основание: совпало название LED STARK, включая 30W, 2400Lm, d98x180, 4000K, IP20. Остатки могут измениться, тогда результат будет другим. - Подтверждение. В том же диалоге:
Добавь 2 штуки товара 200300285_. Убедитесь, чтоcart.itemsещё пуст, аpending_confirmation.quantityравно 2. Затем отдельным сообщениемДа, добавь: backend повторно проверяет остаток и добавляет в demo/session cart.Нет,Отмена, неоднозначный ответ и повторное подтверждение не добавляют товар.
Пример через терминал:
curl -sS http://127.0.0.1:8000/api/chat \
-H 'Content-Type: application/json' \
-d '{"message":"Добавь 2 штуки товара 200300285_"}'
Скопируйте возвращённый session_id вместо UUID_ИЗ_ОТВЕТА:
curl -sS http://127.0.0.1:8000/api/chat \
-H 'Content-Type: application/json' \
-d '{"message":"Да, добавь","session_id":"UUID_ИЗ_ОТВЕТА"}'
Тестирование и статус QA
Из корня проекта с активированной venv:
python -m unittest discover -s tests -v
Backend: 60/60, повторно запущены при финальной подготовке. Тесты используют временные базы, fake LLM и HTTPX MockTransport, не расходуют OpenAI API и не меняют реальную SQLite.
cd frontend
npm test
npm run check
Frontend: 35/35 по переданному отчёту до merge. В среде финального аудита npm отсутствует, повторный запуск не выполнен; frontend не изменялся.
Отдельный финальный QA-отчёт команды: 117 passed / 4 failed. Он учтён по предоставленному результату, не перезапускался здесь; QA-ветка не мержилась. Эти числа относятся к отдельному набору проверок и не складываются с 60/60 и 35/35. Полная приёмка не пройдена.
Предыдущий замер по реальным 15 037 товарам через FastAPI TestClient: точный артикул — медиана 0,77 мс, Legrand (668 результатов) — 8,53 мс. Это локальное измерение без TCP/браузера; время чата зависит от EKT/OpenAI.
Known limitations
Четыре незакрытых класса проблем из финального QA (не исправлены в рамках feature freeze):
- Раскрытие конфликтующих характеристик в fallback. Шаблонный ответ не гарантирует предупреждение о противоречии названия/описания и properties. Например, у
515291встречались 160 А в названии и 250 А в свойстве. Нельзя использовать ответ как техническое заключение. - Чувствительные данные в истории сессии. Нет автоматической очистки/маскирования пользовательского текста перед сохранением в памяти и передачей контекста OpenAI. Не вводите персональные, платёжные или секретные данные. TTL и ограничения объёма не заменяют такую защиту.
- Подбор аналогов при конфликтующих характеристиках. Эвристика не гарантирует выявление всех внутренних противоречий между полями и названием. Возможный аналог требует проверки специалистом; полная взаимозаменяемость не подтверждена.
- Нет серверной верификации произвольных фактических утверждений LLM. Инструкции требуют опираться на tools, а структурированные products собирает backend, но свободный текст модели не проходит независимую проверку каждого факта. Это не гарантия отсутствия выдуманных утверждений.
Другие границы прототипа:
- Настоящий API корзины EKT не предоставлен. Demo/session cart не резервирует товар, не создаёт заказ и не имеет настоящей checkout/cart URL. Отдельная корзина frontend-демо-подборки в localStorage не равна серверной session cart.
- Сертификаты/ссылки на них не обнаружены в проверенной карточке EKT detail. Это не доказывает отсутствие сертификатов у всех товаров; путь получения не подтверждён.
- OpenAI интеграция реализована, но используемые ключи в нашей среде возвращали 401. Доступность
gpt-5.6-lunaименно для проекта не подтверждена. Рабочая демонстрация использует deterministic fallback, а не успешную генерацию LLM. - Fallback поддерживает точные артикулы, а не произвольное понимание свободного текста; demo-cart — ограниченный формат команды. Аналоги ищутся среди небольшого числа кандидатов, результат не гарантирован для любого товара.
- SQLite отражает последнюю успешную синхронизацию; detail получает актуальные данные EKT. Новые товары и изменения названий появятся в поиске после следующей синхронизации. EKT может вернуть таймаут/ошибку, единый снимок каталога во время загрузки не гарантирован.
- Сессии ограничены 100 записями, 6 ходами и 60 000 символами истории; TTL — 30 минут неактивности. Нет авторизации пользователей, постоянной истории или production-защиты публичного API от злоупотреблений. MVP рассчитан на локальную демонстрацию.
- Подтверждённые условия оплаты/доставки/минимальной партии не загружены; обработка вложений, реальное оформление заказа и платёжная интеграция не реализованы. Прототип не закрывает все требования исходного ТЗ.
- «Демо-подборка» frontend содержит отдельные тестовые допущения о наличии, валюте и единицах; её нельзя представлять как актуальный каталог. Реальный поиск и чат используют backend.
Безопасность и данные
Credentials EKT/OpenAI находятся только в server-side .env, не передаются frontend и не входят в Git. .gitignore исключает .env, виртуальное окружение, SQLite/data, node_modules и генерируемые файлы. В репозитории хранится только пустой .env.example.
Изменение demo-cart выполняет серверный код, не LLM: отдельное явное подтверждение, повторная проверка актуального остатка, учёт уже добавленного количества, защита от повторного подтверждения. Реальная корзина сайта не изменяется.
Данные товаров берутся из SQLite/EKT tools; отсутствующие поля не должны выдумываться. Серверные credentials в tool output не входят. Frontend использует textContent для внешнего текста и проверяет товарные ссылки. Ограничения проверки свободного текста модели и приватности истории явно перечислены в Known limitations.
Документация кейса
- Исходные требования
- EKT API и наблюдения по реальным ответам
- API поиска/detail
- API чата, аналогов и подтверждения
- Чек-лист приёмки всего ТЗ
- Источники и открытые вопросы
- Frontend: запуск и детали реализации
Документация находится в корне, отдельной папки docs/ нет. Исторические примеры цен, остатков и измерений — результаты конкретных проверок, не обещание текущих значений.
Авторы коммитов
История коммитов
- 2026-09-23 17:55docs: finalize hackathon READMEszhakano
- 2026-09-23 17:49test: activate EKT chat, analog and demo-cart acceptanceYernat "GhostOfAstana" Soltanbekov
- 2026-09-23 17:48test: publish EKT chat QA harness and final findingsYernat "GhostOfAstana" Soltanbekov
- 2026-09-23 17:47Merge pull request #1 from BAITC-Hacks/backendYernat "GhostOfAstana" Soltanbekov
- 2026-09-23 17:44merge: integrate frontendszhakano
- 2026-09-23 17:41merge: integrate backendszhakano
- 2026-09-23 17:31feat(frontend): display chat analogs and session cartMrAitzhanovAzamat
- 2026-09-23 17:23feat: add confirmed demo cart flowszhakano
Все коммиты (22)
- 2026-09-23 17:22Upgraded frontendMrAitzhanovAzamat
- 2026-09-23 17:19feat(frontend): connect chat API with session handlingMrAitzhanovAzamat
- 2026-09-23 17:15test: add EKT endpoint QA and deferred chat acceptance testsYernat "GhostOfAstana" Soltanbekov
- 2026-09-23 17:14docs: add EKT AI logic and QA acceptance matrixYernat "GhostOfAstana" Soltanbekov
- 2026-09-23 17:13test: initialize EKT QA directoryYernat "GhostOfAstana" Soltanbekov
- 2026-09-23 17:10feat: add AI chat with EKT grounded fallbackszhakano
- 2026-09-23 16:58Adding ai functions to backendszhakano
- 2026-09-23 16:42continue backendszhakano
- 2026-09-23 16:05feat: implement chat interfaceMrAitzhanovAzamat
- 2026-09-23 15:45chore: remove unrelated RSI filesszhakano
- 2026-09-23 15:22feat: add SQLite catalog sync and fast product searchszhakano
- 2026-09-23 15:21feat: add RSI signals and simple backtesterYernat "GhostOfAstana" Soltanbekov
- 2026-09-23 14:57feat: add local product searchszhakano
- 2026-09-23 14:46feat: add EKT product searchszhakano
Структура репозитория (46 файлов)
.env.example .gitignore README.md acceptance-checklist.md api-contract.md backend/__init__.py backend/analogs.py backend/catalog.py backend/chat.py backend/chat_fallback.py backend/config.py backend/demo_cart.py backend/ekt_service.py backend/main.py backend/sync_catalog.py chat-api-contract.md ekt-api.md frontend/README.md frontend/api.js frontend/app.js frontend/catalog.js frontend/chat-api.js frontend/domain.js frontend/favicon.svg frontend/index.html frontend/live-chat.js frontend/live-search.js frontend/package.json frontend/search-model.js frontend/server.mjs frontend/styles.css frontend/tests/api.test.mjs frontend/tests/chat.test.mjs frontend/tests/domain.test.mjs frontend/tests/search-model.test.mjs requirements.md requirements.txt sources-and-open-questions.md tests/test_analogs.py tests/test_backend.py tests/test_chat.py tests/test_cors.py tests/test_demo_cart.py tests/test_detail.py tests/test_fallback.py tests/test_search.py
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.