О проекте
Команда: Salem
Кейс HackAlem.ai: Торговля — Электрокомплект · ekt.kz
Стадия: Рабочий MVP
Задача кейса: AI-консультант по каталогу ekt.kz: товары, аналоги, цены, остатки, корзина
Суть
Чат-консультант по каталогу ekt.kz: ищет товары, сравнивает их, подбирает аналоги и комплекты под задачу покупателя, показывает цены и остатки по складам и добавляет позиции в демо-корзину после явного подтверждения.
Как устроено
Серверный адаптер получает реальные данные ekt.kz через Basic Auth (выборка из 200 товаров, догружается постранично) и обновляет цены и остатки при открытии карточки. Агент на OpenAI Responses API (gpt-5.4-mini) сам выбирает инструменты поиска, сравнения, аналогов, условий покупки и подготовки предложения (не больше 8 раундов и 18 вызовов). Корзина в Cloudflare D1 меняется только атомарно после подтверждения с повторной проверкой цены и остатка. Конфликты данных, например номинал тока в названии и в свойствах, обнаруживаются и блокируют добавление.
Ключевые решения
- OpenAI Responses API function calling
- bounded tool loop
- точное сопоставление номиналов
- подбор комплектов под задачу
- expiring cart proposals
- atomic idempotent cart ops
- data conflict detection
Метрики
Задержка ответа: 2–4 с на простые вопросы, ~10–15 с на запросы к API каталога; аудит данных API по 200 товарам
Особенности
Работает с реальным API ekt.kz и проводит аудит его данных. Товары с противоречивыми характеристиками (конфликт 160 А / 250 А) блокируются. Предложения корзины истекают, а подтверждение защищено от гонок и дублей. Есть живая оценка агента на сценариях: казахский язык, попытка выведать ключ, подтверждение корзины.
Стек
TypeScript · React · Vinext · Cloudflare Workers · Cloudflare D1 · Drizzle ORM · shadcn/ui · Vite · Node.js
Модели
OpenAI gpt-5.4-mini
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-8aa2aaa2-salem.
README
README.mdhack-8aa2aaa2-salem
Hackathon team repository for Salem
ЭКТ Помощник по каталогу
Рабочий прототип для кейса HackAlem AI. Каталог и детальные карточки получают данные ekt.kz через сервер с Basic Auth. Демонстрационная корзина хранится в D1 и не связана с заказами магазина.
Что работает
- Подсказки чата заполняют редактируемый черновик, не отправляют ответ. Поля шаблона выделяются и требуют заполнения; введённый текст сохраняется.
- Локальный SF Pro Display из предоставленного WOFF (Semibold, 600), с предзагрузкой и системным резервным шрифтом.
- Адаптивный каталог, поиск по названию, артикулу магазина и производителя, категории и сортировка.
- Начальная выборка 200 реальных товаров из 10 страниц; 200 подробных карточек. Кнопка загрузки добавляет следующие страницы в общий серверный индекс. Это не полный каталог.
- Обновление цены, характеристик и остатков по складам при открытии карточки.
- ИИ-агент на OpenAI Responses API (gpt-5.4-mini, reasoning low): сам выбирает инструменты поиска, проверки карточек, сравнения, условий покупки, аналогов и подготовки предложения. Поддерживает русский и казахский, контекст последних 20 сообщений. При отсутствии ключа сохраняется явно обозначенный ограниченный режим поиска.
- История сообщений, контекст последнего найденного товара и корзина сохраняются на сервере в рамках сессии.
- Создание предложения на 5 минут без изменения корзины. Явное подтверждение кнопкой или сообщением в чате, повторная проверка цены и остатка, защита от параллельного повторного добавления.
- Подбор решения по проблеме: до двух ключевых вопросов за ответ, до шести обязательных компонентов, один или два полных комплекта. Проверка характеристик и цены, объяснение каждой позиции, сравнение итоговых сумм и скачиваемый отчёт.
- Выбор комплекта кнопкой или явным сообщением «Выбираю бюджетный» / «Выбираю расширенный» добавляет все позиции одной атомарной операцией после повторной проверки API. Предложение действует 15 минут; повторные запросы не удваивают количество. Новый подбор заменяет старый.
- Удаление с подтверждением и экспорт тестовой корзины в CSV.
- Выявление конфликтов номинального тока в названии/описании и структурированном свойстве. Товар 515291 показывает конфликт 160 А / 250 А и блокируется для добавления.
- Кандидаты на замену отбираются по четырём заданным параметрам. Интерфейс не объявляет их полностью взаимозаменяемыми: монтаж, тип устройства и остальные свойства должен проверить специалист.
Границы прототипа
- API реальной корзины не предоставлен. На ekt.kz не отправляются операции изменения корзины, резервирования или оформления заказа. Ссылка «Перейти на ekt.kz» не переносит выбранные позиции.
- Валюта KZT принята как допущение и обозначена в интерфейсе. В ответе API отдельное поле валюты отсутствует. Включение НДС и персональные цены не подтверждены.
- Доступность для тестовой корзины — не больше общего остатка API и суммы складов без служебных записей (брак, резерв, маркетинг, образцы, витрина, перемещение, восстановленный товар); при расхождении суммы складов и общего остатка добавление блокируется. Это демонстрационное правило, а не подтверждённая политика отгрузки магазина. Общий остаток API отдельно виден в карточке.
- Единица количества для корзины — целые единицы; фактическую единицу продажи подтверждает магазин. Кратность, метры, упаковки и варианты offers требуют отдельного контракта. Товары с непустым offers не добавляются автоматически.
- Условия покупки основаны на проверенной 23.09.2026 публичной странице https://ekt.kz/checkout-delivery/. Противоречащие друг другу пороги бесплатной доставки не используются для обещаний. Ссылки на сертификаты в доступной выборке не представлены; агент сообщает об ограничении данных.
- Загрузка и распознавание Excel, Word, PDF и фотографий пока не реализованы. В интерфейсе нет неработающей кнопки загрузки.
- Для агента нужен серверный OPENAI_API_KEY; OPENAI_MODEL по умолчанию gpt-5.4-mini. Запросы расходуют API-баланс. В OpenAI передаются текст диалога и результаты инструментов; store:false, платежные данные запрашивать запрещено. На сайте действует 60 сообщений в час на сессию, не более одного одновременного агентного запроса. При сбое модели интерфейс сообщает о недоступности, не имитирует ответ ИИ.
- Классификация категорий эвристическая, поиск ограничен загруженной выборкой. Для промышленного применения нужна полная фоновая синхронизация и словарь категорий.
- Сессия действует 7 дней. Очистка истёкших сессий и связанных пользовательских данных выполняется при очередном запуске сессии; неиспользуемый сайт требует отдельно запланированной очистки. Каталог сохраняется независимо.
Архитектура
React UI → API Vinext / Cloudflare Worker → адаптер ekt.kz и D1.
app/catalog-app.tsx— интерфейс и взаимодействия.app/api/[...path]/route.ts— HTTP-маршруты, сессия, обработка ошибок.lib/agent.ts— ограниченный цикл вызова инструментов, строгая валидация, проверка происхождения карточек, максимум 8 раундов / 18 вызовов.lib/agent-search.ts— поиск с бюджетом, наличием и точным сопоставлением номиналов (25 А не совпадает с 125 А).lib/purchase-conditions.ts— проверенные условия покупки, источник и ограничения.lib/server.ts— адаптер каталога, поиск и сценарии чата, предложения, атомарные операции корзины.lib/solutions.ts— потребности, варианты, проверка полноты и точных ограничений, суммы и явный выбор.app/solution-card.tsx— карточки комплектов и скачиваемый отчёт.lib/catalog.ts— нормализация, обнаружение конфликтов, валидация количества, фильтры аналогов.db/schema.tsиdrizzle/— схема и миграции.data/catalog.json— исходный снимок реальных данных с датой загрузки.docs/openapi.json— контракт реализованного API.
Секреты находятся только в локальных игнорируемых .env / .dev.vars и серверных секретах опубликованного сайта. Они не входят в клиентский код и Git.
Локальный запуск
Нужен Node.js 24 и npm.
npm ci
Создайте .env и .dev.vars из .env.example, заполните доступ к каталогу. В .dev.vars для Windows-предпросмотра дополнительно укажите:
EKT_LOCAL_API_BASE=http://127.0.0.1:5173/__ekt-local-api/
Этот режим направляет запросы из локального Worker через Node-транспорт Vite. Шлюз работает только в dev-режиме, проверяет Basic Auth, допускает только GET списка/карточки и обращается только к ekt.kz. В production Worker обращается напрямую к фиксированному origin ekt.kz; локальный шлюз не включается в сборку.
npm run build
node --import ./scripts/sites-env.mjs ./node_modules/wrangler/bin/wrangler.js d1 execute DB --local --config dist/server/wrangler.json --persist-to .wrangler/state --file drizzle/0000_same_hellion.sql
npm run dev
Примените по порядку все SQL-файлы из drizzle/: 0000, 0001 и 0002 (каждый один раз). Откройте адрес, напечатанный сервером. Локальный шлюз по умолчанию ожидает порт 5173; при смене порта обновите настройку и allowlist в адаптере.
Проверки
node --test tests/*.test.mjs
node node_modules/typescript/bin/tsc --noEmit
python tests/integration.py
Интеграционные проверки требуют работающего локального сервера и доступа к ekt.kz; они создают отдельные тестовые сессии, не оформляют заказы. Проверяют обязательное подтверждение, изоляцию сессий, конфликт данных, остатки, идемпотентность и параллельные подтверждения, историю и загрузку следующей страницы.
Публикация
Проект подготовлен для Sites с D1 DB. Установите серверные секреты EKT_API_USER и EKT_API_PASSWORD, соберите и опубликуйте через Sites. При переносе в другой проект замените идентификатор Sites своим. Первоначальная публикация закрытая.
Следующие интеграции
- Подтвердить контракт корзины, правила складов, валюту, единицы и кратность.
- Получить утверждённые условия покупки, сертификаты и правила аналогов по категориям.
- Расширять оценку качества подключённого агента и проверки разрешений на действия с корзиной.
- Добавить изолированную обработку файлов и экран сверки распознанных строк.
- Настроить полный импорт, плановую очистку, мониторинг и лимиты перед публичным запуском.
Проверка агента
node --test tests/agent.test.mjs tests/catalog.test.mjs tests/http-client.test.mjs
Живые проверки с реальной моделью и API каталога записаны в docs/agent-live-evaluation.json. Пройдены уточнение, поиск, контекст, сравнение, конфликт данных, условия покупки, казахский, запрос раскрытия ключа, подготовка и явное подтверждение корзины. Это небольшая проверочная выборка, не гарантия отсутствия ошибок модели. В измерениях простые ответы занимали 2–4 с, запросы с API каталога около 10–15 с: требование «единицы секунд» для всех сценариев пока не достигнуто.
Агент читает корзину и доступные ранее показанные предложения через read_cart, подтверждает выбор через apply_cart_selection, меняет точное количество/удаляет одну позицию через set_cart_quantity. Сервер проверяет явность просьбы, ссылку на товар, принадлежность сессии и актуальные данные. Оплата и оформление заказа недоступны. prepare_cart создаёт истекающее предложение, подтверждение кнопкой или сообщением повторно проверяет цену и остатки. Таблица сравнения формируется из API, не генерируется моделью. Отрицательные проверки используют поддельные вызовы модели, недопустимые идентификаторы и количество, неизвестные товары в ответе, ошибки квоты.
Оставшиеся ограничения кейса: реальный API корзины отсутствует; файлы/изображения пока не принимаются; гарантировать хотя бы один совместимый аналог для каждой отсутствующей позиции нельзя по частичной выборке. Для промышленного запуска нужны полная синхронизация, утверждённые условия магазина, расширенная оценка качества и управление общим бюджетом на уровне OpenAI-проекта.
Проверка достоверности API
Отчёт проверки 200 товаров, машиночитаемые результаты. Повторный запуск: node scripts/audit-api.mjs; обновление снимка: node scripts/audit-api.mjs --refresh. Цены, остатки и дата получения показываются без подмены неизвестных значений нулями.
Интерфейс подбора
- Сравнение 2–4 товаров из каталога или ответа агента: свежие запросы, подсветка различий, фильтр отличий, отдельное обозначение неизвестных значений и ошибок обновления.
- Карточка с основными параметрами, копированием артикула, вкладками характеристик, складов и документов, закреплённой панелью количества.
- Расширенное окно помощника, контекст выбранного товара, многострочный ввод (Enter — отправка, Shift+Enter — перенос).
- Сравнение и просмотр карточек не изменяют корзину. При закрытии сравнения незавершённые ответы не меняют его состояние.
Агентный подбор решения
Подробности и сценарии проверки: docs/SOLUTION-AGENT.md. Бюджетный вариант — минимальный среди показанных проверенных комплектов, не гарантия самой низкой цены во всём магазине. Поиск проверяет до шести кандидатов на потребность в загруженной выборке. Если подтверждённой альтернативы нет, агент предлагает один вариант; обязательный отсутствующий компонент нельзя молча исключать. Цена сама по себе не подтверждает качество.
Завершение задачи и доступ к корзине
Готовый ответ или успешное действие заканчивает задачу без необязательных вопросов и подсказок. Статус хранится в D1. Кнопка «Продолжить разговор» возвращает ввод с прежним контекстом. Доступны ручное завершение и корзина из панели чата. Подробности и проверки: docs/CONVERSATION-LIFECYCLE.md.
Авторы коммитов
История коммитов
- 2026-09-23 17:47Enable agent cart actions and complete/resume conversations without repeated promptsIL4elove4ek
- 2026-09-23 17:18Make assistant suggestions editable drafts, improve dialogue context and add SF Pro DisplayIL4elove4ek
- 2026-09-23 16:49Add problem-solving AI bundles with selection, atomic cart and explanation reportsIL4elove4ek
- 2026-09-23 16:13Improve product comparison, assistant UI and verified catalog dataIL4elove4ek
- 2026-09-23 15:28Add EKT AI assistant with catalog tools, confirmed cart, docs and testsIL4elove4ek
Структура репозитория (145 файлов)
.env.example .gitignore .npmrc .openai/hosting.json README.md app/api/[...path]/route.ts app/catalog-app.tsx app/chat-suggestions.tsx app/chatgpt-auth.ts app/globals.css app/layout.tsx app/page.tsx app/product-comparison.tsx app/product-details.tsx app/product-ui.tsx app/solution-card.tsx cloudflare-env.d.ts components.json components/ui/accordion.tsx components/ui/alert-dialog.tsx components/ui/alert.tsx components/ui/aspect-ratio.tsx components/ui/attachment.tsx components/ui/avatar.tsx components/ui/badge.tsx components/ui/breadcrumb.tsx components/ui/bubble.tsx components/ui/button-group.tsx components/ui/button.tsx components/ui/calendar.tsx components/ui/card.tsx components/ui/carousel.tsx components/ui/chart.tsx components/ui/checkbox.tsx components/ui/collapsible.tsx components/ui/combobox.tsx components/ui/command.tsx components/ui/context-menu.tsx components/ui/dialog.tsx components/ui/direction.tsx components/ui/drawer.tsx components/ui/dropdown-menu.tsx components/ui/empty.tsx components/ui/field.tsx components/ui/form.tsx components/ui/hover-card.tsx components/ui/input-group.tsx components/ui/input-otp.tsx components/ui/input.tsx components/ui/item.tsx components/ui/kbd.tsx components/ui/label.tsx components/ui/marker.tsx components/ui/menubar.tsx components/ui/message-scroller.tsx components/ui/message.tsx components/ui/native-select.tsx components/ui/navigation-menu.tsx components/ui/pagination.tsx components/ui/popover.tsx components/ui/progress.tsx components/ui/radio-group.tsx components/ui/resizable.tsx components/ui/scroll-area.tsx components/ui/select.tsx components/ui/separator.tsx components/ui/sheet.tsx components/ui/sidebar.tsx components/ui/skeleton.tsx components/ui/slider.tsx components/ui/sonner.tsx components/ui/spinner.tsx components/ui/switch.tsx components/ui/table.tsx components/ui/tabs.tsx components/ui/textarea.tsx components/ui/toggle-group.tsx components/ui/toggle.tsx components/ui/tooltip.tsx data/catalog-preview.json data/catalog.json db/index.ts db/schema.ts docs/API-DATA-AUDIT.md docs/CHAT-UX.md docs/CONVERSATION-LIFECYCLE.md docs/SOLUTION-AGENT.md docs/agent-live-evaluation.json docs/api-audit.json docs/openapi.json drizzle.config.ts drizzle/0000_same_hellion.sql drizzle/0001_complete_surge.sql drizzle/0002_old_albert_cleary.sql drizzle/0003_plain_harrier.sql drizzle/meta/0000_snapshot.json drizzle/meta/0001_snapshot.json drizzle/meta/0002_snapshot.json drizzle/meta/0003_snapshot.json drizzle/meta/_journal.json eslint.config.mjs examples/d1/app/api/notes/route.ts examples/d1/db/schema.ts hooks/use-mobile.ts lib/agent-search.ts lib/agent.ts lib/catalog.ts lib/chat-suggestions.ts lib/conversation.ts lib/http-client.ts lib/purchase-conditions.ts lib/server.ts lib/solutions.ts lib/utils.ts next.config.ts package-lock.json package.json postcss.config.mjs public/favicon.svg public/file.svg public/fonts/SF-PRO-DISPLAY.woff public/globe.svg public/window.svg scripts/audit-api.mjs scripts/build-verified.sh scripts/execution-profile.mjs scripts/install-ci.mjs scripts/install-ci.sh scripts/install-pnpm.sh scripts/npm-install.mjs scripts/pnpm-install.mjs scripts/run-framework.mjs scripts/sites-env.mjs scripts/sites-env.sh tests/agent.test.mjs tests/catalog.test.mjs tests/chat-suggestions.test.mjs tests/conversation-integration.py tests/conversation.test.mjs tests/http-client.test.mjs tests/integration.py tests/solutions-integration.py tests/solutions.test.mjs tsconfig.json vite.config.ts
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.