API ВЕГА
Jev API на TypeScript: конвейер модерации объявлений от состояния до решения
Открыть галерею
27 сентября 2026 г.

Jev API на TypeScript: конвейер модерации объявлений от состояния до решения

Jev — модель принятия решений от TypeSafe. Она принимает неструктурированные входные данные и возвращает типизированные суждения с вероятностями, а что делать дальше — решает ваш код. В этой статье разберём применение Jev от начала до конца на одном сквозном примере: модерация объявлений на маркетплейсе. Все запросы идут через Decisions API на API ВЕГА.

Как работает Jev

Каждый запрос к Jev состоит из state (состояния) и набора questions (вопросов).

Состояние — это входные данные, которые Jev должен оценить: строка, JSON-объект или массив строк. Для последовательности элементов, например сообщений в переписке, правильный выбор — массив.

Вопросы — это список суждений, которые Jev должен выполнить над состоянием. Вопрос состоит из instructions (инструкции) и criteria (критериев). Инструкция описывает суждение, критерии — возможные ответы. Оценивая состояние, Jev определяет, насколько хорошо каждый критерий к нему подходит, и возвращает это как результат. Если состояние — JSON-объект, инструкция может ссылаться на именованное поле через обратные кавычки: listing.description. Это называется путём к полю (field path) — Jev прочитает значение указанного поля и выполнит суждение именно по нему.

Типы вопросов:

  • choice — выбор одного варианта из заданного вами набора;
  • noul — истинно ли утверждение (вероятность «да» от 0 до 1);
  • score — позиция на упорядоченной шкале уровней, которые вы описываете сами.

Jev оценивает каждый вопрос отдельно и параллельно по одному и тому же состоянию и возвращает ровно один ответ на каждый вопрос:

  • Ответ choice: победивший вариант, вероятность каждого варианта и уверенность (confidence) от 0 до 1 — сводка по всему распределению. Если вероятность сконцентрирована на победителе, уверенность высокая; если распределение размазано — низкая, даже когда победивший вариант сам по себе имеет наибольшую вероятность. Уверенность — это не вероятность победителя.
  • Ответ noul: единственная вероятность истинности утверждения — без отдельной уверенности, потому что она уже выражает степень уверенности модели.
  • Ответ score: взвешенное по вероятностям среднее номеров уровней, вероятность каждого уровня, уверенность и legend — сопоставление номеров уровней с их описаниями.

Каждый ответ — вариант или число, которые код может сравнивать, отсекать по порогу и комбинировать. Никакого текста для разбора не генерируется — поэтому Jev предпочтительнее чат-модели, которой предложено ответить «ДА» или «НЕТ». Вы получаете распределение по ответам, которые сами определили, и неопределённость — это число, по которому можно маршрутизировать логику.

Jev доступен через Decisions API с идентификатором модели typesafe/jev-1.13. Запрос состоит исключительно из текста, а токенный бюджет расходуется и на состояние, и на вопросы. Текущие цены и контекст — на странице модели в каталоге VEGA.

Сквозной пример: маркетплейс

Допустим, вы строите маркетплейс, где пользователи продают бывшие в употреблении вещи. Продавец пишет заголовок и описание, выбирает категорию, указывает состояние товара и загружает фотографии. Прежде чем объявление будет опубликовано, нужно убедиться, что никто не продаёт запрещённый товар и не просит оплатить покупку вне площадки. Плюс отловить товары в неверной категории и описания, противоречащие заголовку или заявленному состоянию.

Часть этих проверок — просто код. Другие требуют действительно прочитать объявление и понять, что оно означает — именно для этого нужен Jev. Каждый раздел ниже сначала описывает часть работы с Jev вообще, а затем — как она выглядит на примере маркетплейса. Та же схема применима к маршрутизации тикетов, ограничению доступа агентов к инструментам и любой другой задаче суждения.

Как Jev встраивается в программу

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

На маркетплейсе этот принцип даёт конвейер между нажатием продавцом кнопки «Опубликовать» и появлением объявления в продаже. Конвейер принимает объявление и возвращает одно из трёх решений: опубликовать, отправить на удержание человеку или отклонить с указанием причин.

mermaid
flowchart LR
    A["1. Жёсткие правила"] --> B["2. Построение состояния"]
    B --> C["3. Суждение (Jev)"]
    C --> D["4. Валидация ответа"]
    D --> E["5. Политика (пороги)"]
    E --> F["Опубликовать"]
    E --> G["На удержание (человек)"]
    E --> H["Отклонить с причинами"]

Этапы 1, 2, 4 и 5 выполняет ваш код; только этап 3 выполняет Jev.

  1. Жёсткие правила идут первыми, в коде: количество фотографий, границы цены, минимальная длина текста. Объявления, которые их не прошли, отклоняются до того, как в Jev отправится хоть что-то.
  2. Построение состояния объединяет текст продавца с фактами, вычисленными кодом, и определяет, что именно увидит Jev.
  3. Суждение — один запрос, задающий Jev несколько типизированных вопросов об этом состоянии. Jev возвращает вероятности и никогда — действие.
  4. Валидация проверяет, что ответ соответствует форме, которую ожидает политика, и останавливает обработку, если это не так.
  5. Политика сравнивает вероятности с порогами, откалиброванными на объявлениях, по которым модераторы уже приняли решения.

Этап 3 — это Jev, остальные четыре — ваши. Так устроены все интеграции с Jev: это функция «состояние → свидетельства», а всё остальное — обычное программное обеспечение.

Что решает Jev, а что — код

Первый вопрос проектирования: что отправлять модели на суждение. Критерий проверки такой — может ли код вычислить ответ, не читая никакого текста? Если может (подсчёт, сравнение дат, поиск по справочнику) — оставляйте это в коде, где результат точен и бесплатен. Jev предназначен для суждений, требующих интерпретации естественного языка.

На маркетплейсе граница проходит так: количество фото, диапазон цены, дата публикации и длина описания считаются кодом на этапе жёстких правил. А вот «просит ли продавец оплатить вне площадки» и «противоречит ли описание заголовку» — вопросы для Jev: код здесь бессилен, потому что ответ зависит от смысла текста. Страница TypeSafe о «рваности» (jaggedness) Jev 1.13 перечисляет арифметику, точный подсчёт и сравнение дат как задачи, которые следует оставить в коде.

Состояние и вопросы

Правило для state — полнота без лишнего. Jev видит только то, что вы передали: если важный факт не попал в состояние, модель о нём не узнает, а каждый лишний блок текста тратит токены и размывает суждение. Поэтому в состояние удобно складывать два слоя: сами данные (объявление) и facts — факты, которые код уже вычислил и которые помогают интерпретировать текст.

Пример состояния для объявления:

JSON
{
  "listing": {
    "title": "Горный велосипед Stels Navigator, б/у",
    "description": "Продаю велосипед после сезонной езды, есть мелкие царапины на раме. Торг уместен. Пишите в WhatsApp по номеру в подписи, договоримся об оплате наличными при встрече.",
    "category": "Электроника",
    "condition": "Как новый",
    "photoCount": 4,
    "price": 12000,
    "currency": "RUB",
    "createdAt": "2026-09-23T10:15:00Z"
  },
  "facts": {
    "photoCountOk": true,
    "priceInBounds": true,
    "descriptionLength": 187,
    "minDescriptionLength": 30
  }
}

Для каждого суждения выбирайте тип вопроса по форме ответа, который нужен политике:

  • «Предлагает ли объявление запрещённый товар?» — это noul: одна вероятность, решение порогом.
  • «К какому исходу отнести попытку оплаты вне площадки?» — это choice, когда исходов несколько и они взаимоисключающие. Всегда добавляйте вариант вроде ambiguous, иначе модель вынуждена выбирать из вариантов, которые не подходят.
  • «Насколько описание соответствует заявленному состоянию?» — это score: упорядоченная шкала, где среднее значение даёт градацию, которой нет у «да/нет».

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

Вызов Jev API из TypeScript

Эндпоинт — POST https://api.vega.chat/api/v1/decisions, авторизация ключом VEGA. Один запрос — все пять вопросов над одним состоянием:

TypeScript
const response = await fetch("https://api.vega.chat/api/v1/decisions", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.VEGA_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model: "typesafe/jev-1.13",
    state: {
      listing: {
        title: "Горный велосипед Stels Navigator, б/у",
        description:
          "Продаю велосипед после сезонной езды, есть мелкие царапины на раме. " +
          "Торг уместен. Пишите в WhatsApp по номеру в подписи, договоримся " +
          "об оплате наличными при встрече.",
        category: "Электроника",
        condition: "Как новый",
        photoCount: 4,
        price: 12000,
        currency: "RUB",
        createdAt: "2026-09-23T10:15:00Z",
      },
      facts: {
        photoCountOk: true,
        priceInBounds: true,
        descriptionLength: 187,
        minDescriptionLength: 30,
      },
    },
    questions: {
      off_platform_payment: {
        type: "choice",
        instructions:
          "Оцени поле `listing.description`: содержит ли объявление " +
          "попытку организовать оплату вне площадки?",
        criteria: {
          off_platform:
            "Продавец прямо или косвенно предлагает оплатить товар вне " +
            "маркетплейса (наличные, перевод на карту, сторонние мессенджеры, " +
            "номер телефона для сделки).",
          platform_only:
            "Оплата предполагается через маркетплейс; внешние контакты " +
            "упоминаются только для связи, но не для расчётов.",
          ambiguous:
            "Текст допускает оба прочтения и не даёт основания для " +
            "однозначного вывода.",
        },
      },
      contradiction: {
        type: "choice",
        instructions:
          "Оцени поля `listing.title` и `listing.description` вместе: " +
          "противоречит ли описание заголовку или заявленному состоянию товара?",
        criteria: {
          contradiction:
            "Описание описывает предмет, отличный от заголовка, или явно " +
            "противоречит заявленному состоянию.",
          consistent:
            "Заголовок, описание и состояние согласованы между собой.",
          ambiguous: "Согласованность определить невозможно.",
        },
      },
      category_fit: {
        type: "choice",
        instructions:
          "Соответствует ли содержимое объявления выбранной категории " +
          "`listing.category`?",
        criteria: {
          fits: "Товар логично отнесён к указанной категории.",
          wrong_category: "Товар явно относится к другой категории.",
          needs_review: "Товар находится на границе нескольких категорий.",
        },
      },
      banned_item: {
        type: "noul",
        instructions:
          "Объявление предлагает запрещённый товар (оружие, наркотические " +
          "вещества, поддельные документы, краденое и т. п.).",
      },
      condition_score: {
        type: "score",
        instructions:
          "Оцени, насколько точно состояние товара в `listing.condition` " +
          "соответствует реальному состоянию, описанному в `listing.description`.",
        criteria: [
          "Полное несоответствие: описанные дефекты опровергают заявленное состояние.",
          "Заметное несоответствие.",
          "Частичное несоответствие.",
          "Мелкие расхождения.",
          "Полное соответствие заявленного и описанного состояния.",
        ],
      },
    },
  }),
});

const verdict = await response.json();

Ответ приходит синхронно — объект answers с одним типизированным ответом на каждый вопрос:

JSON
{
  "id": "gen-dec-1790475301-kQ8mPwRtXvN2bHzLcYeA",
  "model": "typesafe/jev-1.13-20260917",
  "provider": "TypeSafe",
  "answers": {
    "off_platform_payment": {
      "type": "choice",
      "choice": "off_platform",
      "confidence": 0.88,
      "probabilities": {
        "off_platform": 0.93,
        "platform_only": 0.05,
        "ambiguous": 0.02
      }
    },
    "contradiction": {
      "type": "choice",
      "choice": "contradiction",
      "confidence": 0.31,
      "probabilities": {
        "contradiction": 0.42,
        "consistent": 0.33,
        "ambiguous": 0.25
      }
    },
    "category_fit": {
      "type": "choice",
      "choice": "wrong_category",
      "confidence": 0.9,
      "probabilities": {
        "fits": 0.06,
        "wrong_category": 0.9,
        "needs_review": 0.04
      }
    },
    "banned_item": { "type": "noul", "noul": 0.04 },
    "condition_score": {
      "type": "score",
      "score": 3.7,
      "confidence": 0.71,
      "probabilities": { "0": 0, "1": 0.01, "2": 0.05, "3": 0.19, "4": 0.75 },
      "legend": {
        "0": "Полное несоответствие: описанные дефекты опровергают заявленное состояние.",
        "1": "Заметное несоответствие.",
        "2": "Частичное несоответствие.",
        "3": "Мелкие расхождения.",
        "4": "Полное соответствие заявленного и описанного состояния."
      }
    }
  },
  "usage": { "input_tokens": 812, "output_tokens": 154 }
}

Как это читать. Первый вопрос уверенно (confidence 0.88 при концентрации 0.93) указывает на оплату вне площадки — сработали «WhatsApp по номеру в подписи» и «наличными при встрече». Категория определённо неверна: велосипед лежит в «Электронике». Запрещённого товара почти наверняка нет (noul = 0.04) — у noul нет отдельной уверенности, потому что вероятность сама её выражает. По шкале состояния средний балл 3.7 при максимуме 4: «мелкие царапины на раме» согласуются с «б/у» в заголовке и расходятся с «Как новый». А вот contradiction показывает размазанное распределение — победитель есть, но confidence 0.31 говорит, что модель колеблется; такой сигнал логичнее отдать человеку, чем автоматически отклонять объявление.

Обратите внимание на поле model: отправляли typesafe/jev-1.13, ответил датированный снапшот typesafe/jev-1.13-20260917 — версия резолвится в конкретный релиз в момент запроса. В usage видно, что на всё ушло меньше тысячи входных токенов: при цене jev-1.13 в $0.042 за миллион входных одно объявление стоит доли цента.

Вероятности в решения: политика и пороги

Последний этап — код, который сравнивает ответы с порогами. Порядок проверок имеет значение: сначала самые дорогие ошибки (запрещённый товар), потом явные нарушения, в конце — то, что спокойно уйдёт человеку:

TypeScript
// Пороги откалиброваны на размеченной выборке решений модераторов
const THRESHOLDS = {
  offPlatformPayment: 0.85, // вероятность «оплата вне площадки»
  contradiction: 0.8,       // вероятность «противоречие»
  wrongCategory: 0.75,      // вероятность «неверная категория»
  bannedItem: 0.3,          // noul: даже низкая вероятность требует проверки
  conditionScoreMin: 2.5,   // средний балл ниже — расхождение состояния
  minConfidence: 0.5,       // ниже — суждению нельзя доверять без человека
};

function decide(answers: JevAnswers): Decision {
  const { off_platform_payment, contradiction, category_fit, banned_item, condition_score } =
    answers;

  if (banned_item.noul > THRESHOLDS.bannedItem)
    return hold("возможный запрещённый товар");

  if (
    off_platform_payment.probabilities.off_platform > THRESHOLDS.offPlatformPayment &&
    off_platform_payment.confidence > THRESHOLDS.minConfidence
  )
    return reject("оплата вне площадки", off_platform_payment.probabilities);

  if (condition_score.score < THRESHOLDS.conditionScoreMin)
    return hold("расхождение состояния товара");

  if (category_fit.probabilities.wrong_category > THRESHOLDS.wrongCategory)
    return hold("неверная категория");

  if (
    contradiction.probabilities.contradiction > THRESHOLDS.contradiction ||
    contradiction.confidence < THRESHOLDS.minConfidence
  )
    return hold("противоречие описания");

  return publish();
}

Ключевые правила, которые здесь отражены:

  • Ничего не решать по тексту, который можно посчитать. Количество фото, границы цены и длина описания остаются в коде на этапе жёстких правил и в Jev не отправляются.
  • Один запрос — все вопросы. Jev оценивает все пять вопросов параллельно по одному состоянию и возвращает ровно по одному ответу.
  • Смотреть не только на победителя, но и на confidence. Уверенность — сводка всего распределения, а не вероятность победившего варианта. Размазанное распределение (как у contradiction выше) — повод отправить объявление человеку, а не отклонять автоматически.
  • Пороги — не круглые числа. Соберите выборку объявлений, по которым модераторы уже вынесли решения, прогоните её через Jev и подберите пороги по балансу пропущенных нарушений и ложных срабатываний. Низкий порог на banned_item — это осознанная асимметрия: цена пропущенного запрещённого товара несравнимо выше цены лишней ручной проверки.

Как попробовать через API ВЕГА

  1. Получите ключ в личном кабинете.
  2. Отправляйте запросы на POST https://api.vega.chat/api/v1/decisions. Работает и https://api.vega.chat/api/alpha/decisions — этот путь повторяет оригинальный адрес метода, поэтому если код уже рассчитан на такой base URL, достаточно поменять домен и ключ.
  3. Если у вас код на TypeSafe SDK (@typesafe-ai/sdk или typesafe_sdk), укажите baseURL: 'https://api.vega.chat/api' — SDK сам добавит /v1/systemone. Либо задайте переменные TYPESAFE_BASE_URL и TYPESAFE_API_KEY с ключом VEGA.

В запросах указывайте typesafe/jev-1.13, а если нужна всегда свежая версия — алиас ~typesafe/jev-latest. В TypeSafe SDK можно писать короткие id: jev-1.13 или jev-latest. Страница модели в каталоге: api.vega.chat/aimodels/jev-1.13.

[!NOTE] Jev — не чат-модель. Если отправить её в /chat/completions, API вернёт ошибку wrong_endpoint с подсказкой использовать /api/v1/decisions.

Источники

Статья подготовлена по материалам туториала OpenRouter «How to use Jev» (автор — Kenny Rogers).

Комментарии

0/5000

Поддержка Markdown