# Jev: решения вместо текста

> Модель, которая не пишет ответы, а принимает решения — да или нет, выбор из вариантов, оценка по шкале — и возвращает вероятности.

Jev (TypeSafe) — модель другого класса, чем чат-модели. Она **не генерирует текст**. Вы отправляете ей данные и вопросы с заранее известными вариантами ответа, а она возвращает готовые значения: вероятность «да», выбранный вариант, оценку по шкале. Парсить JSON из текста не нужно — ответ сразу ложится в `if`, сортировку или порог в вашем коде.

В RouterAI Jev доступна через выделенный эндпоинт [`/api/v1/decisions`](https://routerai.ru/docs/reference#tag/decisions). В примерах используется алиас [`~typesafe/jev-latest`](https://routerai.ru/models/~typesafe/jev-latest) — он всегда указывает на актуальную версию; конкретную версию можно закрепить именем вроде [`typesafe/jev-1.13`](https://routerai.ru/models/typesafe/jev-1.13). Отдельный аккаунт TypeSafe не нужен — работает обычный ключ RouterAI.

| Чем хороша              | Почему                                                                                                    |
| ----------------------- | --------------------------------------------------------------------------------------------------------- |
| **Быстро**              | Ответ приходит меньше чем за секунду, сколько бы вопросов вы ни задали: они обрабатываются параллельно    |
| **Дёшево**              | Тарифицируются только входные токены. Классифицировать одно обращение стоит доли копейки                  |
| **Предсказуемо**        | Ответ всегда из ваших вариантов — модель не придумает значение, которого вы не предлагали                 |
| **Честно про сомнения** | Вместе с ответом приходят вероятности: видно, когда модель уверена, а когда решение лучше отдать человеку |

## Когда брать Jev, а когда чат-модель

| Задача                                                                                              | Что подойдёт                                                                   |
| --------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
| Определить тему, отдел, намерение, язык, тип документа                                              | **Jev**                                                                        |
| Проверить, есть ли в тексте признак: срочность, жалоба, персональные данные, попытка взлома промпта | **Jev**                                                                        |
| Оценить по шкале: критичность бага, раздражение клиента, релевантность документа запросу            | **Jev**                                                                        |
| Промодерировать список: ники, отзывы, объявления — одним запросом                                   | **Jev**                                                                        |
| Выбрать, какой модели или обработчику отдать запрос                                                 | **Jev**                                                                        |
| Написать ответ клиенту, пересказать, перевести, сгенерировать код                                   | Чат-модель ([Chat Completions](https://routerai.ru/docs/reference#tag/chat-completions)) |
| Посчитать, сравнить даты, решить многошаговую задачу                                                | Обычный код или модель с рассуждениями                                         |

Хорошее правило: Jev отвечает на вопросы, на которые знающий человек ответил бы **за пару секунд, взглянув на текст**. Если нужен анализ из нескольких шагов — разбейте его на простые вопросы и соберите результат в коде.

## Три типа вопросов

| Тип      | Вопрос                   | Что возвращает                                                                                   |
| -------- | ------------------------ | ------------------------------------------------------------------------------------------------ |
| `noul`   | Да или нет?              | `noul` — вероятность «да» от 0 до 1                                                              |
| `choice` | Какой вариант из списка? | `choice` — победивший вариант, `probabilities` — вероятность каждого, `confidence` — уверенность |
| `score`  | Где на шкале?            | `score` — положение на вашей шкале, `probabilities` по уровням, `confidence`                     |

В одном запросе типы можно смешивать. Подробно — в гайде [«Как задавать вопросы»](/docs/guides/overview/decisions/questions).

## Первый запрос

### 1. Получите ключ

Ключ создаётся в [личном кабинете](https://routerai.ru/settings/keys). Это тот же ключ, что и для остальных эндпоинтов RouterAI.

### 2. Отправьте данные и вопросы

В `state` — текст, который нужно оценить. В `questions` — ваши вопросы: имя вопроса придумываете сами, под ним же придёт ответ. Вопросы и критерии можно писать по-русски.


```bash
curl -X POST "https://routerai.ru/api/v1/decisions" \
  -H "Authorization: Bearer $ROUTERAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "~typesafe/jev-latest",
    "state": "Здравствуйте! Третий день не могу подключить оплату на сайте — платежи клиентов не проходят, мы теряем заказы. Помогите, пожалуйста, срочно.",
    "questions": {
      "is_urgent": {
        "type": "noul",
        "instructions": "Клиент сообщает о срочной проблеме?"
      },
      "department": {
        "type": "choice",
        "instructions": "Какая команда должна заняться обращением?",
        "criteria": {
          "billing": "Платежи, счета, возвраты, подписки",
          "technical": "Ошибки, сбои, интеграции, настройка",
          "sales": "Тарифы, покупка, вопросы до оплаты"
        }
      },
      "frustration": {
        "type": "score",
        "instructions": "Насколько раздражён клиент?",
        "criteria": [
          "Спокоен, просто описывает ситуацию",
          "Раздражён, но вежлив",
          "Очень зол, резкие выражения"
        ]
      }
    }
  }'
```

```python
import requests

response = requests.post(
    "https://routerai.ru/api/v1/decisions",
    headers={
        "Authorization": "Bearer YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={
        "model": "~typesafe/jev-latest",
        "state": "Здравствуйте! Третий день не могу подключить оплату на сайте — платежи клиентов не проходят, мы теряем заказы. Помогите, пожалуйста, срочно.",
        "questions": {
            "is_urgent": {
                "type": "noul",
                "instructions": "Клиент сообщает о срочной проблеме?",
            },
            "department": {
                "type": "choice",
                "instructions": "Какая команда должна заняться обращением?",
                "criteria": {
                    "billing": "Платежи, счета, возвраты, подписки",
                    "technical": "Ошибки, сбои, интеграции, настройка",
                    "sales": "Тарифы, покупка, вопросы до оплаты",
                },
            },
            "frustration": {
                "type": "score",
                "instructions": "Насколько раздражён клиент?",
                "criteria": [
                    "Спокоен, просто описывает ситуацию",
                    "Раздражён, но вежлив",
                    "Очень зол, резкие выражения",
                ],
            },
        },
    },
)

answers = response.json()["answers"]
print(answers["is_urgent"]["noul"])       # 0.98
print(answers["department"]["choice"])    # "billing"
print(answers["frustration"]["score"])    # 0.88
```

```typescript
const response = await fetch('https://routerai.ru/api/v1/decisions', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.ROUTERAI_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    model: '~typesafe/jev-latest',
    state:
      'Здравствуйте! Третий день не могу подключить оплату на сайте — платежи клиентов не проходят, мы теряем заказы. Помогите, пожалуйста, срочно.',
    questions: {
      is_urgent: {
        type: 'noul',
        instructions: 'Клиент сообщает о срочной проблеме?',
      },
      department: {
        type: 'choice',
        instructions: 'Какая команда должна заняться обращением?',
        criteria: {
          billing: 'Платежи, счета, возвраты, подписки',
          technical: 'Ошибки, сбои, интеграции, настройка',
          sales: 'Тарифы, покупка, вопросы до оплаты',
        },
      },
      frustration: {
        type: 'score',
        instructions: 'Насколько раздражён клиент?',
        criteria: [
          'Спокоен, просто описывает ситуацию',
          'Раздражён, но вежлив',
          'Очень зол, резкие выражения',
        ],
      },
    },
  }),
});

const { answers } = await response.json();
console.log(answers.is_urgent.noul);      // 0.98
console.log(answers.department.choice);   // "billing"
console.log(answers.frustration.score);   // 0.88
```


### 3. Прочитайте ответ

Ответ на запрос выше, полученный от живой модели (вероятности от прогона к прогону могут немного отличаться):

```json
{
  "id": "rai-dec-1790130957-6mleoJ8dF5ZpJfN2MAkK",
  "model": "typesafe/jev-1.13-20260917",
  "provider": "TypeSafe",
  "answers": {
    "is_urgent": { "type": "noul", "noul": 0.98 },
    "department": {
      "type": "choice",
      "choice": "billing",
      "confidence": 0.61,
      "probabilities": { "billing": 0.74, "technical": 0.26, "sales": 0 }
    },
    "frustration": {
      "type": "score",
      "score": 0.88,
      "confidence": 0.82,
      "probabilities": { "0": 0.12, "1": 0.88, "2": 0 },
      "legend": {
        "0": "Спокоен, просто описывает ситуацию",
        "1": "Раздражён, но вежлив",
        "2": "Очень зол, резкие выражения"
      }
    }
  },
  "usage": { "input_tokens": 633, "output_tokens": 73 }
}
```

- **`is_urgent` = 0,98** — почти наверняка срочно. В коде это `if noul > 0.8`.
- **`department` = `billing`**, но `technical` получил 0,26: в обращении есть и оплата, и «не могу подключить». Поэтому `confidence` всего 0,61 — модель честно показывает, что вариантов два. Такое обращение разумно отдать в биллинг с копией техподдержке.
- **`frustration` = 0,88** — между «спокоен» (0) и «раздражён, но вежлив» (1), ближе ко второму. Индекс 0 соответствует первому уровню в вашем списке `criteria`; список повторяется в ответе в поле `legend`.

Поле `model` в ответе содержит датированный снимок версии, который обслужил запрос (`typesafe/jev-1.13-20260917`), даже если в запросе стоял алиас.

## Официальный SDK TypeSafe

Если у вас уже есть код на SDK TypeSafe (`@typesafe-ai/sdk` для JavaScript и TypeScript, `typesafe-sdk` для Python), его не нужно переписывать: смените адрес и ключ. Запрос и ответ те же, что у `/api/v1/decisions`.


```python
# pip install typesafe-sdk
from typesafe_sdk import Choice, Noul, Score, TypeSafeClient

client = TypeSafeClient(
    base_url="https://routerai.ru/api",
    api_key="YOUR_API_KEY",
    model="jev-latest",
)

response = client.system_one(
    state="Здравствуйте! Третий день не могу подключить оплату на сайте — платежи клиентов не проходят, мы теряем заказы. Помогите, пожалуйста, срочно.",
    questions={
        "is_urgent": Noul(instructions="Клиент сообщает о срочной проблеме?"),
        "department": Choice(
            instructions="Какая команда должна заняться обращением?",
            criteria={
                "billing": "Платежи, счета, возвраты, подписки",
                "technical": "Ошибки, сбои, интеграции, настройка",
                "sales": "Тарифы, покупка, вопросы до оплаты",
            },
        ),
        "frustration": Score(
            instructions="Насколько раздражён клиент?",
            criteria=[
                "Спокоен, просто описывает ситуацию",
                "Раздражён, но вежлив",
                "Очень зол, резкие выражения",
            ],
        ),
    },
)

print(response.answers["is_urgent"].noul)      # 0.98
print(response.answers["department"].choice)   # "billing"
print(response.answers["frustration"].score)   # 0.88
```

```typescript
// npm install @typesafe-ai/sdk
import { TypeSafeClient, choice, noul, score } from '@typesafe-ai/sdk';

const client = new TypeSafeClient({
  baseURL: 'https://routerai.ru/api',
  apiKey: process.env.ROUTERAI_API_KEY,
  defaultModel: 'jev-latest',
});

const response = await client.systemOne({
  state:
    'Здравствуйте! Третий день не могу подключить оплату на сайте — платежи клиентов не проходят, мы теряем заказы. Помогите, пожалуйста, срочно.',
  questions: {
    is_urgent: noul('Клиент сообщает о срочной проблеме?'),
    department: choice('Какая команда должна заняться обращением?', {
      billing: 'Платежи, счета, возвраты, подписки',
      technical: 'Ошибки, сбои, интеграции, настройка',
      sales: 'Тарифы, покупка, вопросы до оплаты',
    }),
    frustration: score('Насколько раздражён клиент?', [
      'Спокоен, просто описывает ситуацию',
      'Раздражён, но вежлив',
      'Очень зол, резкие выражения',
    ]),
  },
});

console.log(response.answers.is_urgent.noul);      // 0.98
console.log(response.answers.department.choice);   // "billing"
console.log(response.answers.frustration.score);   // 0.88
```


Что нужно знать:

- Оба SDK читают адрес и ключ и из переменных окружения: `TYPESAFE_BASE_URL=https://routerai.ru/api` и `TYPESAFE_API_KEY=<ключ RouterAI>` — тогда конструктор можно не менять.
- Имена моделей принимаются в записи TypeSafe: `jev-latest` (значение SDK по умолчанию, если модель не указана) обслуживается как алиас `~typesafe/jev-latest`, `jev-1.13` — как `typesafe/jev-1.13`. Имя с автором используется как есть. В ответе поле `model` содержит идентификатор модели RouterAI.
- Список моделей через SDK (`client.models.list()`) не работает: SDK ждёт формат TypeSafe, а `GET /api/v1/models` отдаёт каталог RouterAI. Смотрите модели в [каталоге](https://routerai.ru/models?output_modalities[]=decisions).
- Стоимость запроса SDK не показывает, см. [раздел о стоимости](#сколько-это-стоит).

## Уверенность: когда действовать автоматически

Вероятности — главное отличие Jev от чат-модели, которая отвечает одинаково уверенным тоном и когда знает, и когда гадает. `confidence` у `choice` и `score` показывает, насколько ответ однозначен: вся вероятность на одном варианте — близко к 1, размазана по нескольким — близко к 0. У `noul` отдельного `confidence` нет: сама вероятность и есть сигнал, значение около 0,5 означает «не знаю».

Удобно делить на три зоны и ставить порог по цене ошибки:

| Уверенность | Что делать                                                            |
| ----------- | --------------------------------------------------------------------- |
| Высокая     | Действовать автоматически                                             |
| Средняя     | Действовать осторожно: попросить подтверждение, пометить для проверки |
| Низкая      | Не действовать: передать человеку или более сильной модели            |

```python
intent = answers["intent"]

if intent["confidence"] < 0.5:
    route_to_human(message)              # модель сомневается — не угадываем
elif intent["choice"] == "check_balance":
    show_balance(account_id)             # ошибка дешёвая — хватит умеренной уверенности
elif intent["choice"] == "approve_transfer":
    if intent["confidence"] > 0.9:
        confirm_then_execute(account_id)
    else:
        ask_user_to_confirm(account_id)  # ошибка дорогая — переспрашиваем
```

Конкретные пороги зависят от ваших данных. Начните с осторожных значений, прогоните на своих примерах с известными ответами и подстройте. Подбирали пороги под конкретную версию модели — логируйте поле `model` из ответа и перепроверяйте пороги после её обновления.

## Сколько это стоит

- Тарифицируются **только входные токены**: ваш `state` и вопросы. Выходные токены бесплатны. Актуальная цена в рублях — на [странице модели](https://routerai.ru/models/typesafe/jev-1.13).
- К каждому запросу провайдер добавляет около 280 служебных токенов: запрос с коротким `state` и одним вопросом занимает примерно 300 входных токенов. Поэтому **десять вопросов одним запросом заметно дешевле десяти запросов по одному вопросу** — текст и служебная часть оплачиваются один раз.
- Русский текст занимает больше токенов, чем английский той же длины.
- В ответе поле `usage` содержит только токены. Стоимость запроса в рублях доступна по идентификатору из заголовка ответа `X-Generation-Id` (он же поле `id` ответа) через `GET /api/v1/generation?id=<id>` — поле `total_cost`, а также в истории запросов в личном кабинете.

## Отличия от документации TypeSafe

Формат запроса и ответа тот же, что в [документации TypeSafe](https://docs.typesafe.ai), поэтому её материалы по формулировке вопросов и порогам применимы без изменений. Отличия только в подключении:

- Эндпоинт — `POST https://routerai.ru/api/v1/decisions`, ключ — обычный ключ RouterAI.
- Модель указывается через алиас `~typesafe/jev-latest` (с ведущей тильдой — так в RouterAI выглядят все алиасы) или конкретной версией, например `typesafe/jev-1.13`.
- Официальные SDK TypeSafe работают: укажите `base_url` `https://routerai.ru/api` и ключ RouterAI, см. [раздел про SDK](#официальный-sdk-typesafe).
- Стоимость не приходит в `usage`, см. [раздел выше](#сколько-это-стоит).

## Частые вопросы

**Это языковая модель?**
Нет. Jev — модель принятия решений: она возвращает типизированные ответы с вероятностями, а не текст, рассуждения или объяснения. Когда нужен текст — берите чат-модель. Когда нужно решение, на которое код может опереться, — Jev.

**Может ли Jev объяснить ответ?**
Нет, объяснений и рассуждений в ответе нет — только вероятности. Если нужна письменная причина, пусть решение принимает Jev, а формулирует чат-модель. Если уверенность низкая — отдайте случай человеку. Часто причину можно получить и без текста: вместо `noul` «одобрить?» задайте `choice` с вариантами «ок / бессмыслица / мат / ненависть» — выбранный вариант и будет причиной.

**Можно ли задать несколько вопросов в одном запросе?**
Да, и нужно: все независимые вопросы к одному `state` отправляйте вместе. Они обрабатываются параллельно и не видят ответов друг друга. Каждый вопрос даёт ровно один ответ, поэтому для списка элементов нужен свой вопрос на каждый — см. [рецепт пакетной проверки](/docs/guides/overview/decisions/recipes#пакетная-проверка-списка-одним-запросом).

**Можно ли использовать `~typesafe/jev-latest`?**
Да. Алиас указывает на текущую версию Jev и переводится на новую, когда она выходит; в ответе, логах и тратах при этом фигурирует реальная модель. Если вы подбирали пороги уверенности под конкретную версию, закрепите `typesafe/jev-1.13` — иначе после обновления пороги придётся перепроверять.

**Какой размер контекста?**
32 000 токенов на `state` и самый длинный вопрос, 64 000 — на `state` и все вопросы вместе. Подробнее — в [лимитах](/docs/guides/overview/decisions/recipes#лимиты).

**Нужен ли аккаунт TypeSafe или их ключ?**
Нет. И прямые запросы к `/api/v1/decisions`, и SDK TypeSafe работают с обычным ключом RouterAI, запросы оплачиваются с баланса RouterAI.

**Работают ли параметры `temperature`, `response_format`, `reasoning`?**
Нет, у decisions-модели их нет. Тело запроса — только `model`, `state` и `questions`. Обращение с этими полями к `/api/v1/chat/completions` или другим эндпоинтам вернёт ошибку: Jev обслуживается только на `/api/v1/decisions`.

## Что дальше

- [Как задавать вопросы](/docs/guides/overview/decisions/questions) — три типа вопросов, структура `state`, как писать критерии.
- [Рецепты и ограничения](/docs/guides/overview/decisions/recipes) — разбор обращений, маршрутизация, защита LLM, пакетная модерация, извлечение данных.
- [Справочник API](https://routerai.ru/docs/reference#tag/decisions) — параметры запроса, формат ответа, ошибки.
