01Обзор архитектуры
Система состоит из четырёх основных компонентов: виджет (React/TypeScript), AI Backend (Python/FastAPI), LLM Gateway (TensorZero) и внешние LLM-провайдеры. Виджет общается с backend через REST (JSON), backend обращается к базе данных и к LLM-провайдерам через абстрактный шлюз.
| Компонент | Стек | Назначение |
|---|---|---|
| AI Widget | React 19.2, TypeScript 6, Vite 8 | Диалоговый интерфейс, Shadow DOM изоляция |
| AI Backend | Python 3.13, FastAPI, Pydantic v2, SQLAlchemy 2.0 | Управление сессиями, бизнес-логика, конвейер LLM |
| LLM Gateway | TensorZero | Абстракция над провайдерами, горячая замена |
| PostgreSQL + pgvector | PostgreSQL 17, pgvector | Данные сессий и вектора МКТУ-базы знаний |
02AI Виджет
Технология и изоляция
Виджет реализован на React 19.2 с TypeScript 6 в строгом режиме (strict: true). Он упакован в самодостаточный standalone-бандл и монтируется в Shadow DOM — стили виджета не конфликтуют со стилями страницы-хозяина и наоборот.
Бандл подключается через один тег <script> и не требует отдельной установки React или других зависимостей на стороне хозяина.
Транспорт: REST
В альфа-версии виджет общается с backend исключительно через REST (JSON): создание сессии, отправка сообщения, загрузка изображения. Все операции — синхронные запросы с ожиданием ответа.
Подключение
Виджет доступен по пути /widget/index.standalone.js. После загрузки скрипта доступен глобальный объект BrandsideAIWidget. Конфигурация передаётся через /config.js, который загружается до виджета.
03AI Backend
Слоёная архитектура
Backend построен по принципам Clean Architecture: слои API, Use Cases, Domain и Infrastructure строго разделены. Внешние зависимости (FastAPI, SQLAlchemy) присутствуют только на слое Infrastructure — бизнес-логика не зависит от фреймворка.
| Слой | Ответственность |
|---|---|
| API | HTTP-маршруты FastAPI, Pydantic-схемы запросов и ответов |
| Use Cases | Сценарии использования: старт сессии, обработка сообщения, загрузка изображения |
| Domain | Агрегаты Session, Identity, Draft; доменные события |
| Infrastructure | Репозитории SQLAlchemy, порт LLM Gateway, объектное хранилище |
Сессии и учётные записи посетителей
Доступ к защищённым операциям регулируется через систему инвайт-кодов: тестировщик получает одноразовый код, обменивает его на cookie __Host-bs_identity, после чего может создавать сессии диалога. Подробности — в разделе «Пользователям».
Каждая сессия изолирована: история сообщений и черновик принадлежат конкретной сессии и недоступны другим.
База данных
Основные данные (сессии, история сообщений, черновики, учётные записи посетителей) хранятся в PostgreSQL. МКТУ-база знаний индексируется через расширение pgvector: при обработке описания бизнеса backend выполняет векторный поиск по ней, чтобы точнее предложить классы.
04LLM Gateway
LLM Gateway (реализован на TensorZero) — абстрактный слой между backend и внешними LLM-провайдерами. Бизнес-логика обращается к Gateway через единый интерфейс независимо от того, какой провайдер используется в данный момент.
Горячая замена провайдера (например, с OpenAI на Anthropic) не требует изменений в бизнес-логике backend — только обновления конфигурации Gateway. Это решение зафиксировано в ADR-007.
Gateway также выполняет анонимизацию персональных данных (PII) перед отправкой в LLM: четыре категории данных заменяются на обратимые метки вида [REDACTED_TYPE_N].
05Безопасность и PII
Анонимизация
Перед отправкой в LLM содержимое сообщений проходит через конвейер анонимизации: персональные данные четырёх категорий (имена, контакты, идентификаторы, финансовые данные) заменяются обратимыми масками, зашифрованными по AES-256. После получения ответа маски раскрываются обратно.
Ограничение запросов
Каждая сессия ограничена 10 сообщениями в минуту. Запросы сверх лимита отклоняются с кодом 429 Too Many Requests.
Cookie учётной записи посетителя
Cookie __Host-bs_identity выпускается с атрибутами HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=2592000. В базе данных хранится только хеш токена (SHA-256), а не сам токен.
Никогда не раскрывайте API-ключи LLM-провайдеров в публично доступных файлах (config.js, клиентском коде). Ключи должны быть только в серверной конфигурации.
06Встраивание виджета
Быстрый старт
Для встраивания виджета на страницу необходим контейнер с идентификатором widget-root и два скрипта: конфигурация и бандл виджета.
<!-- 1. Контейнер виджета -->
<div id="widget-root"></div>
<!-- 2. Runtime-конфиг (загружается первым) -->
<script src="/config.js"></script>
<!-- 3. Загрузчик -->
<script src="/assets/bootstrap-widget.js?v=0.0.2"></script>
Конфигурация
Файл /config.js задаёт публичные параметры без секретов. Основной параметр — apiBaseUrl: базовый URL backend без суффикса /api. Значение вычисляется из текущего origin, поэтому один и тот же статический артефакт работает и на локальном стенде, и в production без пересборки.
Параметр apiBaseUrl не должен включать суффикс /api. Правильно: https://brandside.ru. Неправильно: https://brandside.ru/api.
Статические ресурсы
CSS, логотип и загрузчик лежат в /assets. Бандл виджета и его стили отдаются отдельно из /widget. Это позволяет пересобирать виджет независимо от HTML-страниц лендинга.