Интеграция Shopping Assistant по API
Shopping Assistant - это AI-помощник для покупателей. Он ведет диалог, уточняет потребность, помогает выбрать подходящие товары из каталога и возвращает результат в структурированном формате для отображения в клиентском интерфейсе.
Этот вариант интеграции подходит, когда сайт, приложение, backend или другой клиентский канал самостоятельно реализует UI/UX чата и напрямую вызывает API Gravity:
POST https://shopping-assistant-api.gravityfield.ai/shopping/generate
Content-Type: application/json
API не возвращает готовый экран чата. В ответе приходят текстовые блоки, товарные карточки, кнопки действий и быстрые ответы. Клиентская сторона отвечает за отображение этих блоков, пользовательские сценарии вокруг чата и локальную историю сообщений в интерфейсе.
Что нужно реализовать на стороне клиента
- Создать чат-интерфейс: поле ввода, список сообщений, состояние загрузки, обработку ошибок и повтор запроса.
- Генерировать стабильный
threadIdна одну чат-сессию. Все запросы одного диалога должны отправляться с тем жеthreadId. - Передавать стабильный
resourceId- этоuidпользователя Gravity Field. Удобный способ получить его заранее:POST /user. - В
messagesпередавать текущее сообщение пользователя. Историю диалога повторно отправлять не нужно: контекст сохраняется на стороне Shopping Assistant поthreadIdиresourceId. - Рендерить блоки из
response.contents:messageкак текстовое сообщение ассистента;productsкак список товарных карточек; если в блоке естьtitle, отображать его как заголовок рекомендательного блока;buttonкак действие, например переход в поддержку.
- Рендерить
response.ui.suggestsкак быстрые ответы. При нажатии быстрый ответ отправляется как обычное новое сообщение пользователя. - Для товарных карточек использовать
skuкак основной идентификатор товара. Открытие PDP, добавление в корзину, получение изображения и актуальной цены остаются на стороне клиентского канала или клиентского каталога. - Передавать события показа и взаимодействия:
eventsверхнего уровня относятся ко всему рекомендательному блоку, аeventsвнутри товара - к конкретной карточке.
Типовой поток
- Пользователь открывает чат, клиентская сторона создает
threadId. - Если у клиентской стороны еще нет
uidGravity Field, она получает или создает пользователя черезPOST /userи используетuser.uidкакresourceId. - Пользователь отправляет сообщение.
- Клиентская сторона вызывает
/shopping/generateс текущим сообщением пользователя вmessages. - API возвращает структурированный ответ и сохраняет ход диалога в memory по
threadIdиresourceId. - Клиентская сторона отображает ответ и сохраняет его в локальной истории интерфейса.
- На следующем ходе клиентская сторона снова отправляет только новое сообщение пользователя с тем же
threadIdиresourceId.
Для обычной интеграции используйте роль user.
Контракт запроса
type ShoppingGenerateRequest = {
threadId: string;
resourceId: string;
trafficType?: "real" | "test";
messages: Array<{
role: "user";
content: string;
}>;
runtimeContext: {
tenant_id: string;
ctx?: {
type?: string;
data?: unknown;
lng?: string | null;
location?: string | null;
};
};
};
Поля запроса
Как заполнять ctx
ctx использует ту же модель контекста, что и API V2. Основные типы: HOMEPAGE, SEARCH, PRODUCT, CATEGORY, CART, OTHER.
- Общие правила и список типов: API V2: общий контекст.
- Если нужно заранее создать пользователя вместе с контекстом:
POST /user. - Для старых V1/server-side интеграций с
context.page: Передача контекста страницы.
Если чат открыт на карточке товара, передайте SKU товара в контексте:
{
"runtimeContext": {
"tenant_id": "670ccaae56afcafaf808c146",
"ctx": {
"type": "PRODUCT",
"data": ["1066109"]
}
}
}
Минимальный пример
Запрос
curl --request POST \
--url 'https://shopping-assistant-api.gravityfield.ai/shopping/generate' \
--header 'Content-Type: application/json' \
--data '{
"threadId": "thread-8f2b1c",
"resourceId": "665f0a000000000000000001",
"trafficType": "test",
"messages": [
{
"role": "user",
"content": "Помогите выбрать корм для взрослой кошки"
}
],
"runtimeContext": {
"tenant_id": "670ccaae56afcafaf808c146"
}
}'
Ответ
{
"threadId": "thread-8f2b1c",
"resourceId": "665f0a000000000000000001",
"response": {
"contents": [
{
"type": "message",
"value": "Подберу корм, но сначала уточню пару деталей. Кошка стерилизована и есть ли особенности по здоровью?"
}
],
"ui": {
"suggests": [
"Стерилизована, без особенностей",
"Не стерилизована, чувствительное пищеварение",
"Пожилая кошка, нужен мягкий корм"
]
}
}
}
Пример ответа с товарами
{
"threadId": "thread-8f2b1c",
"resourceId": "665f0a000000000000000001",
"response": {
"contents": [
{
"type": "message",
"value": "Вот несколько подходящих вариантов. Смотрите на возраст, стерилизацию и чувствительность пищеварения."
},
{
"type": "products",
"title": "Подходящие корма для взрослых кошек",
"value": [
{
"sku": "1066109",
"name": "Сухой корм для взрослых кошек",
"price": 1299,
"features": "Для взрослых кошек; повседневный рацион",
"is_stm": false,
"brand": "Demo Brand",
"url": "https://example.com/product/1066109",
"image_url": "https://example.com/product/1066109.jpg",
"events": [
{
"type": "visible_impression",
"urls": [
"https://evs-01.gravityfield.ai/v2/engagement?...",
"https://evs-02.gravityfield.ai/v2/engagement?..."
]
},
{
"type": "click",
"urls": [
"https://evs-01.gravityfield.ai/v2/engagement?..."
]
}
]
},
{
"sku": "1023814",
"name": "Корм для стерилизованных кошек",
"price": 1599,
"features": "Для стерилизованных кошек; контроль веса",
"is_stm": true
}
]
},
{
"type": "message",
"value": "Если кошка стерилизована, лучше начать со второго варианта. Если нет, подойдет первый."
}
],
"ui": {
"suggests": [
"Показать варианты дешевле",
"Нужен влажный корм",
"У кошки чувствительное пищеварение"
]
}
},
"events": [
{
"type": "WRIMP",
"urls": [
"https://evs-01.gravityfield.ai/v2/engagement?...",
"https://evs-02.gravityfield.ai/v2/engagement?..."
]
}
]
}
Контракт ответа
type ShoppingGenerateResponse = {
threadId: string;
resourceId: string;
response: {
contents: Array<
| { type: "message"; value: string }
| { type: "products"; value: Product[]; title?: string }
| { type: "button"; value: string; action: string }
>;
ui?: {
suggests: string[];
};
};
events?: EngagementEvent[];
};
type Product = {
sku?: string;
name: string;
price?: number | null;
features?: string;
is_stm?: boolean;
brand?: string;
url?: string;
image_url?: string;
events?: EngagementEvent[];
};
type EngagementEvent = {
type: string;
urls: string[];
};
Для кнопок клиентская сторона должна обработать action. Например, open_support_chat можно связать с открытием клиентского чата поддержки.
Поле title в блоке products содержит готовый заголовок для этой группы рекомендаций. Если поле пришло, отобразите его над товарными карточками. Если title отсутствует, блок можно отобразить без заголовка.
Для товарных карточек считайте sku основным идентификатором товара. Поля price, brand, url, image_url и другие данные могут использоваться для быстрого отображения карточки, но актуализация цены, изображения, наличия, открытие PDP и добавление в корзину остаются на стороне клиентского канала или клиентского каталога.
Передача показов и кликов
В ответе Shopping Assistant tracking-события могут находиться на двух уровнях:
При наступлении события обработайте все элементы events[] с соответствующим type. Для каждого найденного элемента выполните GET-запрос по каждому URL из urls[]. Не ограничивайтесь первым элементом events[] или первым URL: один факт взаимодействия может требовать нескольких tracking-запросов.
async function sendTrackingEvents(events, type) {
const urls = events
.filter((event) => event.type === type)
.flatMap((event) => event.urls);
await Promise.allSettled(
urls.map((url) => fetch(url, { method: "GET", keepalive: true }))
);
}
// Весь рекомендательный блок стал видимым.
sendTrackingEvents(result.events ?? [], "WRIMP");
// Стала видимой конкретная карточка товара.
sendTrackingEvents(product.events ?? [], "visible_impression");
Отправляйте событие видимого показа один раз для одного фактического показа блока или карточки. Ошибка tracking-запроса не должна блокировать отображение рекомендаций или действие пользователя. Общие правила работы с tracking URL описаны в разделе Передача взаимодействий пользователя с кампаниями.
Ошибки и повтор запроса
Рекомендуем показывать индикатор загрузки до получения ответа, не создавать новый threadId при повторе того же пользовательского сообщения и защищаться от двойной отправки одинакового сообщения при нестабильной сети.