AAYMA
RU
Войти

ИИ-ассистент для чата ekt.kz

Чат-консультант по каталогу электротехники ekt.kz (300 реальных позиций из API партнёра).

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

О проекте

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

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

Суть

Чат-консультант по каталогу электротехники ekt.kz (300 реальных позиций из API партнёра). Отвечает про наличие, характеристики, сертификаты, аналоги с обоснованием и условия покупки, кладёт товар в корзину только после явного согласия и даёт ссылку на корзину.

Как устроено

Серверный цикл tool-calling в route handler Next.js с 9 инструментами (get_product, find_alternatives, get_terms, confirm_add, get_cart_link, find_related и др.) поверх SQLite/Turso, ответ стримится через SSE. Модель только предлагает товар; реплику клиента на согласие проверяет сервер, предложение одноразовое и ограничено остатком. Вложения Excel/Word/PDF/фото разбираются отдельно (фото и PDF через vision). Без ключа работает записанный mock-режим с роутингом по намерению.

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

  • function calling
  • SSE-стриминг
  • серверная проверка согласия
  • одноразовые предложения
  • vision для накладных
  • маскирование PII
  • определение языка (kk/ru)

Метрики

92 детерминированные smoke-проверки, 22 реплики mock-check; ход агента на живой модели 2–4 с

Особенности

Подтверждение корзины проверяет сервер, а не промпт: вопрос или условие («если есть, добавь») не считаются согласием. Ссылка на корзину отдаёт токен только для чтения. Есть embed.js-виджет для чужого сайта и закладка для ekt.kz, поддержка казахского и эскалация на менеджера.

Стек

TypeScript · Next.js 15 · React 19 · SQLite (@libsql/client) · Turso · Vercel · zod

Модели

gpt-4.1-mini (OpenAI) · gpt-4.1-nano (fallback)


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

README

README.md

ИИ-ассистент для чата ekt.kz

Прототип для HackAlem AI, трек «Торговля». Кейс партнёра — ТОО «Электрокомплект» (ekt.kz).

Консультант в одном окне чата на сайте: отвечает по каталогу электротехники — наличие, технические характеристики, сертификаты, аналоги, условия покупки — и добавляет товары в корзину только после явного подтверждения клиента. Последний шаг — ссылка на корзину; заказ клиент оформляет сам.

  • Настоящие данные. 300 позиций из API ekt.kz: артикулы, цены, характеристики, остатки по складам, рекомендации партнёра. Условия покупки — с публичных страниц ekt.kz. Синтетические только сертификаты, и они помечены.
  • Подтверждение проверяет сервер, а не промпт. Модель может только предложить товар. Добавление исполняется после проверки реплики клиента на согласие — одноразово и не больше остатка.
  • Работает без сети. Нет ключа или интернета — записанный режим проходит сценарий, и интерфейс честно помечает его бейджем MOCK.

Попробовать: витрина с чатом · чужой сайт с виджетом · закладка для ekt.kz


Проверка за 5 минут

Нажмите «Новый диалог». Демо-артикулы берутся из каталога (npm run demo печатает текущие): R9F12110 — в наличии, 404029х — без остатка (последняя буква — кириллическая «х»; латинская «x» тоже найдётся).

  1. Наличие, характеристики, сертификат. «Расскажи про артикул R9F12110: есть в наличии, какие характеристики, есть ли сертификат?» Ожидается карточка из каталога: 558 шт., 2 295 ₸, номинальный ток 10 А и сертификат с явной отметкой «демонстрационный».
  2. Аналог с обоснованием. «А 404029х есть? Если нет — что посоветуешь взамен и почему?» Ожидается нулевой остаток и хотя бы один аналог в наличии; ответ объясняет, что совпадает и чем аналог отличается.
  3. Условия покупки. «Какие условия оплаты и доставки?» Ожидается ответ по условиям с сайта ekt.kz: способы оплаты, стоимость и сроки доставки, когда нужен менеджер.
  4. Добавление с подтверждением. «Добавь R9F12110 в корзину, 2 штуки». Появляется карточка-предложение, корзина остаётся пустой. Затем «Да, добавь» или кнопка «Да» — в корзине 2 шт. на 4 590 ₸. Запрос сверх остатка сервер урежет до доступного, и агент об этом скажет.
  5. Актуальная корзина. «Открыть страницу корзины» — те же позиция и количество. В адресе /cart?c=… токен только для чтения, а не идентификатор сессии.
  6. Попытка обойти подтверждение. В новом диалоге: «Добавь R9F12110 в корзину, 1 штуку» — карточка, корзина пуста. «А можно добавить?» — это вопрос, корзина пуста. «Если есть на складе, добавь» — это условие, корзина пуста. «Да» — позиция добавлена. Карточка после этого теряет кнопки, а повторное подтверждение того же предложения сервер отклонит: оно одноразовое (smoke, раздел 4).

Соответствие ТЗ

Пункт ТЗГде реализованоЧем проверяется
§7.1 Наличие, характеристики, сертификатget_product в src/lib/agent/tools.tsnpm run smoke, раздел 1
§7.2 Аналог с обоснованиемfind_alternatives в src/lib/agent/tools.ts, подбор и reason в src/lib/db.tssmoke, раздел 2
§7.3 Условия покупкиget_terms в src/lib/agent/tools.ts, data/terms.json с источниками ekt.kzsmoke, раздел 3
§7.4 Корзина только после согласия, количество не больше остаткаconfirm_add в src/lib/agent/tools.ts, consumeProposal в src/lib/db.ts, кнопка в src/app/api/confirm/route.tssmoke, раздел 4: три проверки согласия, остаток, повтор
§7.5 Ссылка на актуальную корзинуget_cart_link в src/lib/agent/tools.ts, токен для чтения в src/lib/db.ts, src/app/cart/page.tsxsmoke, раздел 5; на проде — сквозной прогон «товар → да → страница корзины»
§5 Вход: Excel, Word, PDF и фотоsrc/lib/spec-parse.ts, src/lib/llm/vision.ts, src/app/api/upload/route.tssmoke, разделы 5г и 6; файлы scripts/fixtures/ для ручной загрузки, PDF и фото требуют модели
§5 Задержка — единицы секундстриминг ответа, один вызов модели на шагход агента на живой модели — 2–4 с
§8 Казахский языкdetectLang в src/lib/agent/loop.ts, матчер в src/lib/agent/tools.ts, src/components/i18n.tssmoke, разделы 0 и 5а; npm run mock-check; с ключом: npm run demo -- --live --kk
§8 Сопутствующие товарыfind_related в src/lib/agent/tools.ts, RECOMMEND партнёра в src/lib/db.ts, подсказки в src/components/AgentApp.tsxsmoke, раздел 5б; вручную — подтвердить товар 027004 кнопкой
§8 История диалогаlocalStorage в src/components/AgentApp.tsx, в пределах сессиивручную: переписка → перезагрузка → история на месте
§8 Эскалация на менеджераget_terms(contacts) в src/lib/agent/tools.ts, правило 9б в src/lib/agent/prompts.tssmoke, раздел 3; npm run mock-check
§9 Защита корзиныsrc/lib/agent/tools.ts, src/lib/db.ts: токен ссылки, три проверки согласия, одноразовое предложениеsmoke, разделы 4 и 5
§9 Приватностьsrc/lib/redact.ts: карты, CVV, IBAN; src/lib/logger.ts: маскирование контактов в журналеsmoke, раздел 5в
§9 Объяснимостьполе reason у аналогов и сопутствующих в src/lib/db.tssmoke, разделы 2 и 5б
§9 Совместимость с сайтомpublic/embed.js, src/app/demo-site/page.tsx, src/app/try/page.tsxвручную: /demo-site, закладка с /try
§9 Десктоп и мобильная версиямедиазапросы в src/app/globals.cssвручную на телефоне, в том числе ширина 375px
§10 Репозиторий и READMEэтот файл: архитектура, запуск, данные—

У ekt.kz нет открытой интеграции авторизации: история хранится в браузере в пределах сессии (до 40 элементов ленты, без миниатюр фотографий). История для авторизованных клиентов — план интеграции: нужна подписанная передача id пользователя с сервера Bitrix. Сейчас она не реализована.


Запуск

Нужен Node.js 20 или новее (проверено на 22).

git clone https://github.com/BAITC-Hacks/hack-0af00165-iwbhappy.git
cd hack-0af00165-iwbhappy
npm install
npm run dev

Открыть http://localhost:3000. Ключ к модели не обязателен: без него приложение работает в записанном режиме и проходит весь сценарий.

С живой моделью — создать .env.local:

LLM_MODE=auto
OPENAI_API_KEY=<ключ>
LLM_MODEL=gpt-4.1-mini

Переменные окружения

ПеременнаяНазначениеПо умолчанию
LLM_MODEauto — модель, при сбое записанный режим; live — только модель; mock — только записанныйauto
OPENAI_API_KEYключ модели; живёт только на сервере—
LLM_MODELосновная модельgpt-4.1-mini
LLM_FALLBACK_MODELзапасная модель при rate limitgpt-4.1-nano
OPENAI_BASE_URLсовместимый прокси, если доступ выдадут на площадке—
DB_URL, DB_AUTH_TOKENбаза: file:… локально, libsql://… — удалённая (Turso)file:./.data/hack.db
TURSO_DATABASE_URL, TURSO_AUTH_TOKENто же; их подставляет интеграция Turso в Vercel—
EKT_API_BASE, EKT_API_USER, EKT_API_PASSWORDтолько для npm run seed — перевыгрузки каталога—

Ключи и пароль к API партнёра — только в .env.local, он в .gitignore. В репозитории лежит .env.example: значения по умолчанию заполнены, ключи и пароли — пустые.

Проверки

npm run verify

Одна команда перед сдачей: проверка типов → smoke → demo → mock-check. Останавливается на первой ошибке. Demo и mock-check работают в записанном режиме без вызова внешней модели, каждый со своей тестовой базой.

КомандаЧто проверяет
npm run smoke92 детерминированные проверки через инструменты, без модели: пять тестов ТЗ и отрицательные случаи — подтверждение в том же ходу, без согласия, на другой товар, повторное; маскирование, поиск, вложения
npm run demoпять тестов ТЗ через агентный цикл: какие инструменты модель зовёт и в каком порядке. -- --live — на живой модели, -- --live --kk — по-казахски с шагом эскалации
npm run mock-check22 свободные реплики в записанном режиме: русский и казахский, смена товара, согласие и отказ — страховка на случай без сети
npm run checkвалидация data/catalog.json
npm run typecheckTypeScript без сборки

Архитектура

Схемы потока данных и подтверждения добавления.

СлойРешение
ПриложениеNext.js 15 (App Router), React 19, TypeScript — один репозиторий
Агентсерверный цикл tool-calling в route handler, ответ стримится через SSE
МодельOpenAI gpt-4.1-mini, запасная gpt-4.1-nano, записанный режим без сети
БазаSQLite через @libsql/client: файл локально, Turso на Vercel
Проверка аргументовzod
ХостингVercel
Браузер или виджет на сайте
  │
  ├─ POST /api/chat ───────▶ агентный цикл ──▶ модель (tool-calling)
  │  ◀─ SSE: текст, вызовы,       │     ▲
  │     состояние корзины         ▼     │ результаты
  │                         9 инструментов ──▶ SQLite / Turso
  │
  ├─ POST /api/confirm ────▶ кнопка «Да» мимо модели ──▶ consumeProposal
  ├─ POST /api/upload  ────▶ разбор файла / чтение фото ──▶ предложение
  └─ GET  /cart?c=токен ───▶ корзина только для чтения

Ключ к модели живёт только на сервере и в браузер не попадает.

СлойФайлРоль
Конфигурацияsrc/lib/config.tsмодель, ключ, таймауты, лимиты — одно место
Доступ к моделиsrc/lib/llm/client.tsстриминг, таймауты, ретраи, запасная модель, откат в mock
Чтение вложенийsrc/lib/llm/vision.tsфото и PDF — отдельным вызовом, вне агентного цикла
Записанный режимsrc/lib/llm/mock.tsмаршрутизация по намерению реплики, без сети
Агентный циклsrc/lib/agent/loop.tsмодель → инструменты → результаты → модель; язык ответа
Инструментыsrc/lib/agent/tools.tsдевять тонких обёрток над БД + проверка согласия
Системный промптsrc/lib/agent/prompts.tsправила поведения агента
Данныеsrc/lib/db.tsсхема, засев, поиск, аналоги, корзина, предложения
Разбор таблицsrc/lib/spec-parse.tsxlsx, csv, docx — без внешних библиотек
Приватностьsrc/lib/redact.tsмаскирование платёжных и личных данных
Выгрузка каталогаscripts/seed-from-api.tsоднократный забор данных из API партнёра

Инструменты

ИнструментНазначение
search_catalogпоиск по каталогу, до 5 позиций
get_productкарточка: характеристики, сертификат, цена, остатки по складам
find_alternativesаналоги, у каждого — поле reason с обоснованием
find_relatedсопутствующие товары из рекомендаций партнёра, с reason
get_termsоплата, доставка, самовывоз, гарантия, возврат, контакты менеджера
propose_addпредложение добавить товар; корзину не меняет
confirm_addдобавление по подтверждённому предложению
get_cartсодержимое корзины
get_cart_linkпрямая ссылка на корзину

Устойчивость

  • Нет сети или ключа — LLM_MODE=auto уводит в записанный режим, сценарий доходит до конца. В шапке загорается MOCK: выдавать записанный ответ за живую модель нельзя.
  • Rate limit, 5xx, таймаут — три попытки с экспоненциальной паузой, затем запасная модель, затем mock. Повтор запрещён, если часть ответа уже ушла пользователю.
  • Мусор в аргументах — схема отклоняет, текст ошибки уходит модели, даётся одна попытка переписать вызов.
  • Смена схемы БД — версия схемы хранится в таблице meta и проверяется до применения схемы; при расхождении база пересобирается сама. Каталог засевается одной транзакцией: несколько экземпляров на Vercel стартуют одновременно и не видят его полузаполненным.

Правило подтверждения

Ключевое требование кейса: состав корзины не меняется без явного согласия клиента. Это не строчка в промпте, а инвариант на сервере.

  1. Модель не может добавить товар сама. propose_add только записывает предложение со статусом pending и возвращает proposalId; корзина не трогается.
  2. Подтверждение приходит одним из двух путей, и решение принимает сервер, а не модель:
    • кнопка «Да» — POST /api/confirm идёт из интерфейса напрямую, минуя модель; подделать этот вызов модель не может, потому что в нём не участвует;
    • текст — модель вправе вызвать confirm_add, но сервер исполнит его только после трёх проверок. Текст реплики клиента сервер сохраняет сам, до обращения к модели.
  3. Три проверки текстового пути:
    • предложение создано в предыдущем ходу — иначе модель в одном ответе предложила бы и сама подтвердила, подставив под проверку ту же фразу клиента;
    • реплика — явное согласие. Вопросы («а можно добавить?»), условия («если есть на складе, добавь») и отрицания согласием не считаются; по-русски и по-казахски. Сомнительное — отказ;
    • реплика не называет другой товар. «Добавь 027004» — новая просьба, а не согласие на висящее предложение про другой артикул. Эту дыру нашли на живом демо: модель попыталась подтвердить старое предложение на просьбу о новом товаре.
  4. Предложение одноразовое: условный UPDATE … WHERE status='pending', гонкой двух запросов его не обойти.
  5. Количество не больше доступного остатка. Если запрошено больше — добавляется доступное, и агент обязан об этом сказать.
  6. Спецификация из файла проходит через ту же функцию consumeProposal. Второго пути изменения корзины в приложении нет.

Всё перечисленное покрыто npm run smoke, включая отрицательные случаи.


Данные

Каталог — боевая выгрузка из API партнёра, не выдумка:

GET https://ekt.kz/api/products?page=N       список
GET https://ekt.kz/api/products/detail?id=N  карточка с характеристиками и складами

scripts/seed-from-api.ts ходит туда один раз и пишет data/catalog.json, который лежит в репозитории. В рантайме сеть к ekt.kz не нужна: приложение читает SQLite. Демонстрация не зависит ни от чужого API, ни от Wi-Fi.

Срез: 300 позиций. Характеристики берутся из properties карточки (номинальный ток, число полюсов, напряжение, отключающая способность), сопутствующие товары — из properties.RECOMMEND, собственных рекомендаций партнёра. Изначально мы читали это поле как список аналогов; проверка на данных показала, что все связи ведут из автоматов и УЗО на клеммы, то есть это допродажа, а не замена. Аналоги подбираются по разделу каталога, типу, бренду и цене.

Что пришлось учесть в реальных данных

Не все склады торговые. В stores[] вперемешку городские склады и служебные: «Брак MEGALIGHT», «Востановленный продукт», «перемещение», «Образцы Отдел Закупа», «Витрина ТЗ». Их остаток не считается доступным — показать брак как наличие значит соврать клиенту. Поле quantity верхнего уровня суммирует все склады, включая служебные, поэтому доступный остаток пересчитывается по отфильтрованному списку.

Описание противоречит характеристикам. У товара id 515291 в названии и описании «160А», а в properties.NOMINALNYY_TOK — «250 А». Источником чисел объявлены только properties; описание показывается как текст и никогда не служит источником характеристик. Это же зафиксировано в системном промпте.

Грязные имена и артикулы. Названия встречаются с префиксом *** и продублированным артикулом; поле article бывает служебным (200300285_), а настоящий артикул поставщика лежит в properties.ARTIKULPOSTAVSHCHIKA. Чистка делается на этапе выгрузки, оригинал сохраняется в rawName.

Кириллица и латиница вперемешку. В каталоге есть и «16А», и «25A» латиницей, и «3P», и артикул «404029х» с русской «х». Встроенный LOWER() в SQLite понимает только латиницу, а LIKE для кириллицы чувствителен к регистру: «узо» не находило «УЗО». Поэтому текст для поиска готовится при засеве средствами JS: нижний регистр, ё → е, латинские буквы-двойники — в кириллицу. Плюс синонимы (УЗО ↔ ВДТ, автомат ↔ АВ, дифавтомат ↔ АВДТ) и бонус за совпадение с типом изделия: «автомат 16А» ставит автоматы выше боксов для автоматов.

Что синтетическое

  • Сертификаты. В API их нет, а требование кейса есть. Проставлены примерно трети позиций, детерминированно по артикулу, с флагом demo: true. Интерфейс и агент помечают такой сертификат как демонстрационный. Выдавать придуманный сертификат за настоящий нельзя.

Всё остальное — реальные данные партнёра. Артикулы, названия, цены, характеристики, остатки и рекомендации — из API. Условия покупки (data/terms.json) — с публичных страниц ekt.kz (/about/howto, /about/faq, /about/contacts): оплата для физлиц и юрлиц, тарифы доставки по Алматы, порог бесплатной доставки по стране, города самовывоза, гарантия, возврат, контакты. Источник и дата сверки записаны в самом файле.


Вложения

Раздел 5 ТЗ: вход — не только текст, но и файлы.

Спецификация (.xlsx, .xlsm, .csv, .tsv, .txt, .docx) разбирается своим кодом, без внешних библиотек. Готовый пакет для xlsx на npm тянет известные уязвимости, а разбирать предстоит файл, который прислал кто угодно. Формат — zip с XML внутри, и нужная его часть читается средствами Node: zlib умеет inflateRaw, остальное — разбор заголовков. Из файла берутся только значения ячеек и текст: ни формулы, ни макросы, ни внешние ссылки не исполняются.

PDF и фото накладной читает модель отдельным вызовом и возвращает строки «артикул — количество»; дальше они идут тем же путём, что и таблица. Фото модель сначала классифицирует: изделие или документ.

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

Фото товара читается отдельным одноразовым вызовом модели, вне агентного цикла: картинка не попадает в поток сообщений и не трогает стриминг с ретраями. Сравнения изображений нет намеренно — в каталоге электротехника, где модульные автоматы внешне неотличимы, визуальное сходство почти не несёт сигнала. Зато на фото читается текст: артикул, бренд, номинал. Модель возвращает короткий JSON, дальше работает обычный поиск по каталогу.


Сопутствующие товары, казахский язык, менеджер

Сопутствующие. После подтверждения одной позиции сервер сам прикладывает до двух сопутствующих товаров из рекомендаций партнёра — и в ответ модели, и в ответ кнопки «Да». Агент предлагает их одной фразой с обоснованием, интерфейс — подсказками. В корзину они сами не попадают: это новое предложение и новое подтверждение.

Казахский. Агент отвечает на языке клиента. Язык определяет сервер, а не модель: правило в промпте живая модель игнорировала, отвечая на казахские реплики по-русски, — промпт и каталог на русском и перетягивают. Сервер смотрит на буквы, которых нет в русском алфавите (ә ғ қ ң ө ұ ү һ і), и добавляет на ход явную инструкцию. Названия и характеристики товаров не переводятся. Интерфейс переключается RU / KZ.

Правило подтверждения работает и на казахском: «иә», «жарайды», «қосыңыз» — согласие; «жоқ», «керек емес», «қоспа» — отказ. Вопрос с частицей «ма/ме» («қосуға бола ма») согласием не считается, даже без знака вопроса.

Менеджер. Крупная партия, претензия или возврат, доставка не по Алматы, прямая просьба позвать человека — агент даёт реальные контакты из условий покупки и не обещает обратного звонка. В интерфейсе есть кнопка «Связаться с менеджером».


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

ТЗ требует учесть приватность клиента и защиту от несанкционированного изменения корзины. Что сделано:

  • Ссылка на корзину — только для чтения. В ней случайный токен, а не идентификатор сессии: сессия даёт право менять корзину через чат, а ссылкой клиент делится. По токену сессию не восстановить. Идентификатор сессии создаётся через crypto.randomUUID().
  • Платёжные данные не хранятся. Номер карты (с проверкой Луна), CVV и IBAN вырезаются из реплики до всего остального — в модель, журнал сервера и историю, которую видит модель, попадает метка. Лента чата сохраняется в браузер клиента тоже уже с меткой. Агент говорит клиенту, что в чате платёжные данные не нужны. В журнал дополнительно не пишутся телефон, email и ИИН.
  • Журнал сервера наружу не отдаётся. Он общий для всех сессий.
  • Лимиты: 20 сообщений и 6 файлов в минуту с адреса, реплика до 2000 символов. Лимитер в памяти экземпляра — защита от одного назойливого клиента, не от распределённой атаки; для прода нужен WAF.
  • Встраивание во фрейм разрешено только нам и ekt.kz (frame-ancestors): иначе чужая страница может накрыть кнопку «Да» прозрачным фреймом.
  • Вложения не исполняются: из xlsx и docx берутся только значения ячеек и текст, без формул, макросов и внешних ссылок.

База на Vercel

На serverless у каждой функции свой /tmp. С файловой SQLite чат, подтверждение и страница корзины попадали в разные базы, и страница корзины оказывалась пустой — это проверено на проде. Поэтому публичная версия работает на Turso (удалённый libSQL): переменные подставляет интеграция Turso в Vercel, код тот же. С ноутбука процесс один, и файловой базы достаточно.


Встраивание в сайт

Одна строка на любой странице, независимо от платформы сайта:

<script src="https://hackalem10.vercel.app/embed.js" async></script>

Скрипт рисует кнопку и панель с iframe на /embed. Стили изолированы дважды — shadow DOM и самим iframe, — поэтому ни сайт не ломает чат, ни чат сайт. Связь с родительской страницей идёт через postMessage с проверкой источника и origin с обеих сторон; по каналу ходят только служебные сообщения — готовность, высота, закрытие. Данные корзины и диалога через него не передаются.

  • /demo-site — сторонняя витрина с другим оформлением и подключённым виджетом.
  • /try — закладка для браузера: откройте ekt.kz, нажмите закладку — виджет появится на живом сайте партнёра. Сам сайт при этом не меняется: виджет видит только тот, кто нажал закладку.

Ограничения

По требованиям кейса

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

Ограничения прототипа

  • Каталог — срез из 300 позиций и снимок на момент выгрузки. Цены и остатки не обновляются в реальном времени; в проде — периодическая синхронизация через тот же API.
  • Корзина своя, а не корзина Bitrix. Для настоящей интеграции нужен API корзины сайта; сейчас ссылка ведёт на нашу страницу корзины.
  • Сертификаты синтетические и помечены как демонстрационные.
  • История диалога — в браузере, в пределах сессии. Для авторизованных клиентов id должен приходить с сервера Bitrix с подписью: верить идентификатору из браузера нельзя.
  • Лимитер запросов — в памяти экземпляра. Для прода — WAF или лимитер на Redis.

Как делали: Claude и Codex

Условие хакатона — использовать Codex. Роли разделены в AGENTS.md: архитектура, контракты инструментов, правила поведения агента, приёмочные критерии и системный промпт — одна зона; реализация кода — Codex.

Каждая задача Codex — отдельная постановка в docs/codex/ (01–08): какие файлы менять, что трогать нельзя, чем проверить результат. Каждый результат проверялся прогоном, а не по отчёту. Грабли, на которые наступили за день, собраны в конце AGENTS.md: от \b, который не видит кириллицу, до базы, которая на Vercel у каждой функции своя.

  • docs/codex/HANDOVER.md — состояние проекта, инварианты и безопасная процедура деплоя;
  • docs/DEFENSE.md — ответы на вопросы жюри.

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

src/app/              страницы и API: /, /cart, /embed, /demo-site, /try,
                      /api/chat, /api/confirm, /api/upload, /api/state, /api/reset, /api/products
src/components/       витрина, чат, корзина, карточка подтверждения, i18n
src/lib/agent/        агентный цикл, инструменты, системный промпт
src/lib/llm/          доступ к модели, записанный режим, чтение фото и PDF
src/lib/db.ts         схема, засев, поиск, аналоги, корзина, предложения
src/lib/redact.ts     маскирование платёжных и личных данных
src/lib/ratelimit.ts  лимиты запросов
src/lib/spec-parse.ts разбор xlsx, csv, docx
public/embed.js       виджет одной строкой
data/                 каталог и условия покупки
scripts/              выгрузка каталога, smoke, demo, mock-check, образцы вложений
docs/                 постановки для Codex, передача, ответы жюри
AGENTS.md             спецификация и грабли
Звёзды
⭐ 0
Форки
0
Язык
TypeScript
Последний push
в прошлом месяце
Коммиты
52
Авторы
1
Файлы
73
Ветки
2

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

iwbhappy 52

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

  • 2026-09-23 17:43На мобильных показывать чат сразу под корзинойiwbhappy
  • 2026-09-23 17:37README: клонирование репозитория и версия Node.js в разделе «Запуск»iwbhappy
  • 2026-09-23 17:35README и DEFENSE: пробел с лентой в браузере закрыт в 97cbecciwbhappy
  • 2026-09-23 17:34Лента чата в браузере тоже не хранит платёжные данныеiwbhappy
  • 2026-09-23 17:30README: точная формулировка про платёжные данныеiwbhappy
  • 2026-09-23 17:26Сохранить авторскую редакцию README кроме ссылки на архитектуруiwbhappy
  • 2026-09-23 17:21Актуализировать тексты интерфейса и шпаргалку для защитыiwbhappy
  • 2026-09-23 17:20Документировать потоки и серверное подтверждение схемами Mermaidiwbhappy
Все коммиты (52)
  • 2026-09-23 17:17README к сдаче: порядок для жюри, актуальные факты, честные ограниченияiwbhappy
  • 2026-09-23 17:17Уточнить ошибки интерфейса и перенос длинных названийiwbhappy
  • 2026-09-23 17:15Добавить единую команду verify перед сдачейiwbhappy
  • 2026-09-23 17:14README: сопоставить требования ТЗ с реализацией и проверкамиiwbhappy
  • 2026-09-23 17:13README: добавить проверку за пять минут для жюриiwbhappy
  • 2026-09-23 17:09HANDOVER: прод работает с Turso, процедура чистого деплояiwbhappy
  • 2026-09-23 17:06Поиск находит кириллицу: «УЗО», «16А», «Автоматический» больше не теряютсяiwbhappy
  • 2026-09-23 17:04Добавить страницу с закладкой для демонстрации виджетаiwbhappy
  • 2026-09-23 17:01Показать сопутствующие товары после подтверждения кнопкойiwbhappy
  • 2026-09-23 17:00База готова к Turso: каталог одной транзакцией, версия схемы в таблицеiwbhappy
  • 2026-09-23 17:00Исправить маршрутизацию офлайн-режима и добавить mock-checkiwbhappy
  • 2026-09-23 16:51Постановка 08: шлифовка к сдаче — README для жюри, соответствие ТЗ, verify, схемыiwbhappy
  • 2026-09-23 16:48Постановка 07: записанный режим, который выдерживает вопросы жюриiwbhappy
  • 2026-09-23 16:47Кнопка «Да» возвращает сопутствующие товары, как и confirm_add в чатеiwbhappy
  • 2026-09-23 16:36Согласие на другой товар не подтверждает висящее предложениеiwbhappy
  • 2026-09-23 16:25Добавить историю и безопасную сессию в интерфейсiwbhappy
  • 2026-09-23 16:22Смоук: проверка маскирования телефона стала настоящейiwbhappy
  • 2026-09-23 16:22AGENTS §13: исправлена заметка, испорченная той граблей, которую она описываетiwbhappy
  • 2026-09-23 16:22Документация: безопасность, вложения, разделённая база на Verceliwbhappy
  • 2026-09-23 16:21Образцы вложений — бинарные: git не трогает переводы строк в PDFiwbhappy
  • 2026-09-23 16:21Вложения по ТЗ §5 полностью: Word, PDF, фото накладнойiwbhappy
  • 2026-09-23 16:17Приватность и защита публичной ссылки: маскирование, лимиты, заголовкиiwbhappy
  • 2026-09-23 16:15Ссылка на корзину только для чтения; журнал не уходит наружуiwbhappy
  • 2026-09-23 16:14Постановка 06: криптостойкая сессия, история диалога, Word и PDFiwbhappy
  • 2026-09-23 16:06Добавить казахский интерфейс и связь с менеджеромiwbhappy
  • 2026-09-23 16:03Сопутствующие товары; RECOMMEND партнёра больше не выдаётся за аналогiwbhappy
  • 2026-09-23 15:52Документация: реальные условия, казахский, виджет, три новые граблиiwbhappy
  • 2026-09-23 15:51Язык ответа и ссылку на корзину решает сервер, а не модельiwbhappy
  • 2026-09-23 15:48Постановка 05: казахский интерфейс и кнопка менеджераiwbhappy
  • 2026-09-23 15:47Условия покупки с ekt.kz, казахский язык, эскалация на менеджераiwbhappy
  • 2026-09-23 15:45Виджет для встраивания: embed.js, /embed и демо-витрина (Codex)iwbhappy
  • 2026-09-23 15:22Вложения закрыты: отмечаю в передачеiwbhappy
  • 2026-09-23 15:21Передача проекта: состояние, инварианты и защитаiwbhappy
  • 2026-09-23 15:15Разбор спецификаций без внешних зависимостейiwbhappy
  • 2026-09-23 15:12Вложения: батч-предложения и чтение фото товараiwbhappy
  • 2026-09-23 15:01Постановка 03: id каталога, чистка хвостовых маркеров, проверка демоiwbhappy
  • 2026-09-23 15:00Интерфейс: витрина, чат и постоянно видимая корзина (Codex)iwbhappy
  • 2026-09-23 15:00Промпт: добавление в корзину без лишнего шагаiwbhappy
  • 2026-09-23 14:44Аналоги называют расхождения характеристик; живой прогон на моделиiwbhappy
  • 2026-09-23 14:34Питч под кейс ekt.kz и раздел с граблямиiwbhappy
  • 2026-09-23 14:32Серверная часть кейса ekt.kz: инструменты, данные, приёмочные тестыiwbhappy
  • 2026-09-23 14:00WIP: кейс ekt.kz — инструменты, БД и подтверждение покупкиiwbhappy
  • 2026-09-23 13:49spec: кейс ekt.kz, выгрузка из API партнёра, серверное подтверждениеiwbhappy
  • 2026-09-23 06:58Каркас агентного торгового приложенияiwbhappy
Структура репозитория (73 файлов)
.claude/launch.json
.env.example
.gitattributes
.gitignore
AGENTS.md
HackAlem AI_ ИИ-ассистент для чата на сайте ekt.kz.docx
README.md
data/catalog.json
data/products.json
data/terms.json
docs/ARCHITECTURE.md
docs/CODEX.md
docs/DEFENSE.md
docs/PITCH.md
docs/TIMELINE.md
docs/codex/01-seed-from-api.md
docs/codex/02-ui.md
docs/codex/03-catalog-id.md
docs/codex/04-attachments.md
docs/codex/05-kazakh-ui.md
docs/codex/06-session-history.md
docs/codex/07-offline-mock.md
docs/codex/08-polish.md
docs/codex/HANDOVER.md
next.config.ts
package-lock.json
package.json
public/embed.js
scripts/check-catalog.ts
scripts/demo.ts
scripts/fixtures/invoice-photo.jpg
scripts/fixtures/invoice.pdf
scripts/fixtures/spec-lines.docx
scripts/fixtures/spec-table.docx
scripts/mock-check.ts
scripts/seed-from-api.ts
scripts/smoke.ts
src/app/api/chat/route.ts
src/app/api/confirm/route.ts
src/app/api/products/route.ts
src/app/api/reset/route.ts
src/app/api/state/route.ts
src/app/api/upload/route.ts
src/app/cart/page.tsx
src/app/demo-site/page.tsx
src/app/embed/page.tsx
src/app/globals.css
src/app/layout.tsx
src/app/page.tsx
src/app/try/page.module.css
src/app/try/page.tsx
src/components/AgentApp.tsx
src/components/CartPanel.tsx
src/components/ConfirmCard.tsx
src/components/EmbedBridge.tsx
src/components/EmbedDemoSite.tsx
src/components/ProductGrid.tsx
src/components/i18n.ts
src/lib/agent/bridge.ts
src/lib/agent/loop.ts
src/lib/agent/prompts.ts
src/lib/agent/tools.ts
src/lib/config.ts
src/lib/db.ts
src/lib/llm/client.ts
src/lib/llm/mock.ts
src/lib/llm/types.ts
src/lib/llm/vision.ts
src/lib/logger.ts
src/lib/ratelimit.ts
src/lib/redact.ts
src/lib/spec-parse.ts
tsconfig.json
Ветки (2)

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

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

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