ДАРА
Разделы документации

ДокументацияРуководства

Рекомендации

Лента, похожие и «дополните заказ», переранжирование, фильтры по свойствам, пагинация и recommendation_id, что не показывается, метрики выдачи, запасная выдача и таймауты.

ДАРА подбирает объекты проекта для конкретного посетителя тремя методами. Пользователь передаётся объектом user, как в событиях; без него выдача неперсональная. Каждый ответ 200 содержит recommendation_id — id выдачи: передавайте его в событиях показа, открытия и цели, по нему считаются метрики.

Метод Для чего Ответ
POST /recommendations/feed лента: главная, бесконечная прокрутка recommendation_id, items: [{id, score, reason}], has_more, mode, identity
POST /recommendations/similar похожие на объект и «дополните заказ» для корзины recommendation_id, items: [{id, score, reason}], mode, identity
POST /recommendations/rerank упорядочить ваш список id под пользователя recommendation_id, items: [{id, score}], identity

Запросы идут с вашего сервера с ключом проекта: Authorization: Bearer ck_XXXXXXXX… — в примерах он в переменной окружения DARA_KEY. Лимит частоты выдачи — 100 запросов в секунду на ключ. Все поля и схемы — в справочнике API.

Лента: POST /recommendations/feed

curl -X POST https://core.daratech.ru/api/v1/recommendations/feed \
  -H "Authorization: Bearer $DARA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"user": {"user_id": "u-501", "anonymous_id": "a-7f3c2e"}, "limit": 24, "offset": 0}'
{"recommendation_id": "01JC8Z6Q9W3M5N7P2R4T6V8X0Y",
 "items": [{"id": "v-2210", "score": 0.9132, "reason": "interest"},
           {"id": "v-1877", "score": 0.8417, "reason": "together"},
           {"id": "v-2301", "score": 0.6205, "reason": "fresh"}],
 "has_more": true, "mode": "personal", "identity": "user"}
Поле Тип По умолчанию Описание
user object {user_id, anonymous_id, fingerprint_uuid}, хотя бы одно поле — см. Пользователи
limit int от 1 до 100 24 объектов на странице
offset int от 0 до 1000 0 0 — новая выдача, больше 0 — следующие страницы той же выдачи (пагинация)
types array все типы от 1 до 5 разных типов: video, image, audio, text, mixed
exclude array до 500 id объектов, которые не показывать
filters object условия по свойствам объектов — Фильтры
recommendation_id string id выдачи первой страницы; необязателен

Пустое тело — все умолчания. Неизвестное поле — ответ 422 validation_failed с кодом unknown_field в details.

reason — почему объект в ленте:

reason Источник
interest близок к интересам пользователя — по его действиям и указанным вами интересам
session близок к тому, что пользователь смотрит сейчас
together его открывают или покупают вместе с тем, что понравилось пользователю
author новое у близкого или любимого автора
trending набирает популярность в последние дни
fresh новый объект
popular стабильно популярный объект
segment популярный у похожих пользователей: та же страна, город, пол и возраст

Когда новое для пользователя заканчивается, лента продолжается уже досмотренными объектами — давно досмотренными первыми.

Похожие и «дополните заказ»: POST /recommendations/similar

curl -X POST https://core.daratech.ru/api/v1/recommendations/similar \
  -H "Authorization: Bearer $DARA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"item": "v-1042", "user": {"anonymous_id": "a-7f3c2e"}, "limit": 8}'
{"recommendation_id": "01JC8Z8C3E5G7J9L1N3Q5S7U9W",
 "items": [{"id": "v-1188", "score": 0.7311, "reason": "similar"},
           {"id": "v-0977", "score": 0.6824, "reason": "together"}],
 "mode": "cold", "identity": "anonymous"}
Поле Тип По умолчанию Описание
item string id исходного объекта
items array корзина — «дополните заказ»: от 1 до 20 id; неизвестные ДАРА id не учитываются
user object как у ленты
limit int от 1 до 50 8 объектов в ответе
types, exclude, filters как у ленты

Нужно ровно одно из полей item и items, иначе 422 с кодом invalid_value; больше 20 id в itemstoo_many. Страниц у похожих нет: поля offset и recommendation_id в запросе нет — переданные, они дают 422 с кодом unknown_field; has_more в ответе нет.

  • Кандидаты — близкие по содержанию, объекты «вместе» с исходными, новое того же автора (для одного исходного объекта) и, если выдача персональная, объекты по интересам пользователя. Исходные объекты в ответ не попадают.
  • Корзина. Для нескольких объектов важнее то, что покупают вместе, чем замены: «дополните заказ» подбирает дополнения к корзине.
  • Новый объект участвует в похожих сразу после PUT — по быстрому вектору из заголовка и описания, ещё до конца анализа.
  • Удалённый или недоступный исходный объект работает как обычный: похожие считаются по его сохранённым векторам.
  • Объекта нет в ДАРА (например, вы не передаёте непубличные страницы) — ответ 200 с популярным проекта: mode: fallback, reason: popular, с recommendation_id для метрик. Так же, если у исходных объектов нет ни векторов, ни соседей «вместе».

reason у похожих: similar — близок по содержанию, together — покупают или смотрят вместе, author — тот же автор, interest — интересы пользователя, popular — популярное, trending — в тренде (только в запасной выдаче).

Переранжирование: POST /recommendations/rerank

Упорядочивает ваш список — например, результаты поиска или подборку редакции — по оценке ленты для этого пользователя.

curl -X POST https://core.daratech.ru/api/v1/recommendations/rerank \
  -H "Authorization: Bearer $DARA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"user": {"user_id": "u-501"}, "items": ["v-5", "v-9", "v-404", "v-12"]}'
{"recommendation_id": "01JC8Z9D4F6H8K0M2P4R6T8V0X",
 "items": [{"id": "v-9", "score": 0.5521}, {"id": "v-5", "score": 0.4108},
           {"id": "v-404", "score": null}, {"id": "v-12", "score": null}],
 "identity": "user"}
Поле Тип Описание
items array, обязательно от 1 до 1000 id объектов; больше 1000 — ответ 413 payload_too_large
user object как у ленты
filters object принимается и не применяется
  • В ответе все переданные id, столько же, сколько в запросе. Сначала — по убыванию оценки (при равной оценке — в исходном порядке), затем в конце, в исходном порядке и с score: null:
    • неизвестные ДАРА, удалённые и недоступные объекты;
    • объекты без векторов (ещё не проанализированы);
    • объекты, скрытые пользователем;
    • повторы id после первого вхождения.
  • Не применяются: фильтры, скрытые авторы, возраст, досмотренное и приглушение купленного, разведка и разнообразие. Объект скрытого пользователем автора получает оценку как обычно.
  • Поля mode в ответе нет. Если оценить не успели — исходный порядок, у всех объектов score: null.

Поля ответа

Как определён пользователь: identity

Запрос identity
нет user или в нём нет учитываемых идентификаторов none
есть user_id — пользователь известен ДАРА или ещё нет user
без user_id, устройство узнано — узнавание без входа recognized
без user_id, пользователь найден по anonymous_id или проверенному fingerprint_uuid anonymous
без user_id, anonymous_id или fingerprint_uuid ДАРА ещё не знает anonymous
идентификатор неверного формата ответ 422, код invalid_user
  • fingerprint_uuid учитывается, только если в проекте подключён DARA Fingerprint; без подключения запрос только с ним — identity: none.
  • Запрос выдачи пользователей не создаёт: неизвестный пользователь получает неперсональную выдачу, а его события потом создадут пользователя.

Режим выдачи: mode

mode Когда Из чего выдача
personal у пользователя достаточно истории действий или указанных вами интересов интересы профиля, текущая сессия, объекты «вместе» с понравившимся, близкие и любимые авторы, тренды, популярное, новое
cold новый или неизвестный посетитель, user не передан, мало истории, отказ от персонализации (personalization: false) тренды, популярное, новое, текущая сессия (кроме отказавшихся), популярное в сегменте пользователя
fallback запасная выдача: подбор не уложился во время ответа, индекс проекта загружается, база данных недоступна; у похожих — исходного объекта нет в ДАРА или у него нет векторов и соседей трендовый или популярный список проекта

Пользователь с указанными интересами получает personal сразу, как только интересы получат векторы — обычно в течение минуты после записи профиля; исключение — контрольная группа, её выдача указанные интересы не использует. Лента смещается к новым интересам пользователя в течение минуты после его действий.

Оценка: score

score — оценка объекта для этого пользователя, 4 знака после точки. Сравнивайте оценки только внутри одного ответа: шкала зависит от режима. В fallback score — трендовость или популярность объекта в запасном списке, от 0 до 1. В переранжировании score: null — объект не оценивался (выше).

Фильтры

filters — условия по свойствам объектов (properties), которые отмечены фильтруемыми в кабинете: проект → «Выдача» → «Фильтруемые свойства объектов», с типом свойства.

{"filters": {"in_stock": true, "brand": {"in": ["acme", "nord"]}, "price": {"gte": 100, "lte": 5000},
             "regions": {"any": ["msk", "spb"]}}}
Тип свойства Условие Пример
строка равенство; in, not_in — от 1 до 100 строк "brand": "acme", "brand": {"not_in": ["noname"]}
число равенство; in, not_in; gte, lte "price": {"gte": 100, "lte": 5000}
да/нет равенство "in_stock": true
список строк any — у объекта есть хотя бы одно из значений, от 1 до 100 строк "regions": {"any": ["msk", "spb"]}
  • В одном запросе — до 10 свойств. Несколько свойств и несколько условий одного свойства объединяются по «И».

  • Строки сравниваются точно, с учётом регистра.

  • Нет свойства у объекта или значение другого типа — условие не выполнено, кроме not_in: оно выполнено.

  • Свойство не отмечено фильтруемым, условие не подходит к типу свойства, null, пустой массив или больше 100 значений — ответ 422 с кодом unknown_filter и полем filters.<свойство>:

    {"error": {"code": "validation_failed", "message": "Поля не прошли проверку",
      "details": [{"field": "filters.color", "code": "unknown_filter", "message": "свойство не отмечено фильтруемым в настройках выдачи проекта"}]}}
    
  • Где действуют: лента, похожие, следующие страницы и запасная выдача. В переранжировании filters не применяются.

  • Узкий фильтр. Если подходящих кандидатов мало, выдача добирается популярными объектами, которые проходят фильтр.

  • После изменения фильтруемых свойств фильтры работают сразу, но пока перестраивается индекс проекта (секунды, у больших каталогов — минуты), запросы с фильтрами получают запасную выдачу (mode: fallback) — тоже только из подходящих объектов.

Пагинация и recommendation_id

  • Первая страница (offset: 0) строит новую выдачу — до 500 объектов — и сохраняет её снимком на 30 минут.
  • Следующие страницы — тот же запрос с offset: страница берётся из снимка, без повторов и пропусков. recommendation_id передавать не обязательно: без него страница находится по идентификаторам пользователя и параметрам запроса.
  • Передавайте на всех страницах одни и те же user, types, exclude и filters. Другие types, exclude или filters — это другая выдача: не дописывайте показанные объекты в exclude между страницами.
  • Первая страница без anonymous_id (посетитель пришёл впервые и cookie ещё нет), а следующие с ним — лента продолжается. Вход между страницами (добавился user_id) тоже продолжает ту же ленту.
  • Без user выдача одинакова для всех посетителей в течение часа и меняется со следующим часом.
  • has_more — есть ли объекты в снимке после этой страницы. Страница может быть короче limit: объекты, которые удалили, скрыли или сделали недоступными после первой страницы, из неё выбрасываются, а has_more остаётся честным.
  • offset за концом снимка — items: [], has_more: false.
  • Снимок истёк (30 минут) или не найден — строится новая выдача и отдаётся срез от offset; на стыке возможны повторы.
  • Узнавание прекратилось между страницами (на устройстве вошёл другой аккаунт, узнавание выключили) — следующие страницы уже не берутся из ленты узнанного пользователя; на стыке возможны повторы.

У похожих и переранжирования страниц нет, но recommendation_id есть в каждом ответе — для метрик.

Что никогда не показывается

В ленте, похожих, на следующих страницах и в запасной выдаче не бывает:

  • удалённых и недоступных объектов (available: false) и объектов без векторов — новый объект получает быстрый вектор вскоре после PUT;
  • объектов, скрытых пользователем: события типов с флагом «скрывает объект», например report и not_interested;
  • недавно досмотренного и дочитанного — типы событий со значением watch_seconds или percent; когда новое заканчивается, лента продолжается досмотренным;
  • купленного и почти одинаковых с ним объектов — весь срок приглушения типа события (в пресете «Магазин» у покупки 30 дней);
  • объектов скрытых авторов (hidden_authors);
  • объектов с возрастной отметкой старше возраста пользователя — при включённом фильтре по возрасту и известном годе рождения; пользователю с неизвестным возрастом такие объекты показываются;
  • объектов других типов, из exclude и не прошедших filters.

События по удалённым, недоступным и скрытым объектам принимаются и учитываются в профиле: это видно в кабинете, в карточке пользователя. Как скрытые объекты ведут себя в переранжировании — выше. Запасная выдача, собранная без обращения к базе данных (база недоступна или время ответа вышло), применяет то, что есть в памяти ДАРА: объекты, скрытые пользователем, и только что удалённые в ней могут встретиться.

Разведка и разнообразие

  • Разведка. Несколько позиций в каждых 24 объектах ленты отдаются новым объектам: они получают показы и статистику, даже если пока проигрывают популярным. Какой новый объект встанет на позицию, решают близость к интересам пользователя и доля случайности. Случайность детерминирована: у одной выдачи порядок один и тот же.
  • Разнообразие. Каждый следующий объект выбирается с учётом того, насколько он похож на уже выбранные, — лента не состоит из почти одинаковых объектов. У одного автора — не больше нескольких объектов в любом окне из 24 позиций ленты и в одном ответе похожих.
  • Числа. Веса оценки, число кандидатов, позиции разведки и ограничения разнообразия настраивает ДАРА для проекта; в документации они не публикуются и могут меняться без изменения API.

Метрики выдачи

Метрики считаются по событиям, которые вы присылаете. Для каждого показа и открытия объекта из выдачи передайте:

Поле события Что передавать
recommendation_id id выдачи из ответа, где был объект
surface место на сайте: feed, next, cart — как вы его называете; сравнивается точно, Feed и feed — разные поверхности
position позиция объекта в выдаче, с 0; на следующих страницах — offset плюс номер на странице

Показы первой и второй страницы ленты по 24 объекта:

{"events": [
  {"id": "imp-91a7-0", "type": "impression", "item": "v-2210", "user": {"anonymous_id": "a-7f3c2e"},
   "recommendation_id": "01JC8Z6Q9W3M5N7P2R4T6V8X0Y", "surface": "feed", "position": 0},
  {"id": "imp-91a7-1", "type": "impression", "item": "v-1877", "user": {"anonymous_id": "a-7f3c2e"},
   "recommendation_id": "01JC8Z6Q9W3M5N7P2R4T6V8X0Y", "surface": "feed", "position": 1},
  {"id": "imp-91a7-24", "type": "impression", "item": "v-0412", "user": {"anonymous_id": "a-7f3c2e"},
   "recommendation_id": "01JC8Z6Q9W3M5N7P2R4T6V8X0Y", "surface": "feed", "position": 24}
]}
  • Цели. Целевое событие без recommendation_id (досмотр, покупка, в том числе через POST /orders) приписывается выдаче, если за 2 часа до него тот же пользователь открыл этот объект из выдачи.
  • База для сравнения. Показы вашей собственной ленты (хронологической, редакционной) отправляйте с той же surface и без recommendation_id — они образуют срез «без рекомендаций».
  • Показатели — в кабинете, проект → «Метрики», по поверхностям и дням по Москве: показы, открытия, CTR (открытия / показы), цели на 100 показов, вовлечённость (средняя дневная по парам «пользователь × объект» у типов со значением watch_seconds и percent, не больше 1), доля показов свежих объектов, покрытие каталога (доля доступных объектов, показанных из выдачи за 7 дней), доля запасной выдачи — основная без запросов похожих для объектов, которых нет в ДАРА, и разбивка по причинам.
  • Контрольная группа. Часть пользователей получает выдачу без ваших данных о пользователях — так измеряется их эффект. Правила — Данные о пользователях, сравнение групп — проект → «Данные о пользователях».

Тайм-аут и запасная выдача

  • Тайм-аут на вашей стороне — около 700 мс на запрос выдачи и около 200 мс на соединение. ДАРА отвечает быстрее своего дедлайна: если подбор не успевает, вы получаете запасную выдачу, а не ожидание.
  • Запасная выдача — ответ 200 с mode: fallback:
    • лента и похожие, когда подбор не уложился во время ответа, индекс проекта загружается или база данных недоступна, — трендовый список проекта, reason: trending;
    • похожие для объекта, которого нет в ДАРА, или без векторов и соседей, — популярный список, reason: popular;
    • score — трендовость или популярность объекта в списке, от 0 до 1;
    • удалённое, недоступное, exclude, types, filters и данные пользователя, которые ДАРА успела прочитать, учитываются и здесь;
    • переранжирование — исходный порядок, все score: null.
  • 503 unavailable с Retry-After: 2 — запасную выдачу собрать не из чего: база данных недоступна, а ключ не проверялся последние минуты. Не повторяйте запрос в том же показе страницы — покажите свою ленту.
  • 429 rate_limited — превышен лимит 100 запросов в секунду на ключ (кратковременный всплеск допускается).
  • На любой ошибке, таймауте и ответе не 200 показывайте свою запасную выдачу — например, хронологическую ленту. Доля запасной выдачи видна в кабинете, в «Метриках».

Ошибки полей запроса выдачи (422 validation_failed) приходят одним ответом, до 20 строк details: unknown_field, invalid_type, invalid_value, required, too_long, too_many, unknown_filter, invalid_user — см. Ошибки и лимиты.

Примеры на PHP и Python

<?php
/** Выдача ДАРА или null: при null покажите свою ленту. */
function dara_recs(string $method, array $body): ?array
{
    $ch = curl_init('https://core.daratech.ru/api/v1/recommendations/' . $method);
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('DARA_KEY'), 'Content-Type: application/json'],
        CURLOPT_POSTFIELDS => json_encode($body, JSON_UNESCAPED_UNICODE),
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_CONNECTTIMEOUT_MS => 200,
        CURLOPT_TIMEOUT_MS => 700,
    ]);
    $response = curl_exec($ch);
    $status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
    if ($response === false || $status !== 200) {
        return null;
    }
    $data = json_decode($response, true);
    return is_array($data) ? $data : null;
}

$user = ['anonymous_id' => 'a-7f3c2e'];
if ($userId !== null) {
    $user['user_id'] = (string) $userId;
}
$feed = dara_recs('feed', ['user' => $user, 'limit' => 24, 'offset' => $offset]);
$next = dara_recs('similar', ['item' => 'v-1042', 'user' => $user, 'limit' => 8]);
$ids = $feed !== null ? array_column($feed['items'], 'id') : chronological_ids($offset, 24);
import os

import httpx

api = httpx.Client(
    base_url="https://core.daratech.ru/api/v1",
    headers={"Authorization": f"Bearer {os.environ['DARA_KEY']}"},
    timeout=httpx.Timeout(0.7, connect=0.2),
)


def recs(method: str, body: dict) -> dict | None:
    """Выдача ДАРА или None: при None покажите свою ленту."""
    try:
        response = api.post(f"/recommendations/{method}", json=body)
        return response.json() if response.status_code == 200 else None
    except (httpx.HTTPError, ValueError):  # сеть, таймаут, неразборчивый ответ
        return None


user = {"user_id": "u-501", "anonymous_id": "a-7f3c2e"}
page1 = recs("feed", {"user": user, "limit": 24})
page2 = recs("feed", {"user": user, "limit": 24, "offset": 24}) if page1 and page1["has_more"] else None
cart = recs("similar", {"items": ["sku-3107", "sku-2240"], "user": user, "limit": 8})
search = recs("rerank", {"user": user, "items": ["v-5", "v-9", "v-12"]})