# System One API (Decisions API): что это и как работает

> **System One API** (Decisions API) — это эндпоинт, который отправляет модели не запрос на генерацию текста, а состояние и набор типизированных вопросов, и получает обратно готовые ответы с вероятностями: да или нет, один из вариантов, оценку по шкале.

Обычная языковая модель пишет ответ токенами, а приложение потом пытается вытащить из этого текста решение. Модели класса decisions устроены иначе: они читают входные данные за один проход и возвращают вероятность для каждого допустимого ответа, не генерируя ни одного токена.

## Откуда взялось название {#otkuda-nazvanie}

Деление на две системы мышления — из психологии: быстрые интуитивные суждения и медленное рассуждение. В ИИ так стали называть два класса моделей. System Two — привычные генеративные модели, которые рассуждают и пишут связный текст. System One — модели, которые быстро выдают структурированное решение и на этом заканчивают.

Термин закрепила TypeSafe: в её документации System One описан как класс моделей, а Jev — первая модель этого класса. Liquid AI свои модели того же назначения называет просто decision models. В RouterAI оба названия обозначают одно и то же — эндпоинт, на который можно отправить decisions-модель.

## Как это работает {#kak-rabotaet}

Запрос состоит из двух частей. В `state` лежит то, что нужно оценить: строка, объект или массив, а модели с поддержкой изображений принимают ещё и картинки. В `questions` — именованные вопросы с типом и инструкцией. Имя вопроса придумывает разработчик: модель его не видит, ответ придёт под этим же ключом.

```json
{
  "model": "liquid/d1.1",
  "state": "Клиент пишет, что деньги списались дважды.",
  "questions": {
    "refund_requested": {
      "type": "noul",
      "instructions": "Клиент прямо просит вернуть деньги?"
    }
  }
}
```

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

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

У `choice` варианты задаются объектом «ключ — описание», у `score` — массивом уровней от 2 до 10. Все вопросы к одному `state` уходят одним запросом и обрабатываются параллельно, поэтому добавление вопросов почти не увеличивает время ответа.

Ответ приходит в поле `answers`, по ключу на каждый вопрос:

```json
{
  "answers": {
    "is_urgent": { "type": "noul", "noul": 0.997 },
    "department": {
      "type": "choice",
      "choice": "billing",
      "confidence": 0.55,
      "probabilities": { "billing": 0.697, "technical": 0.298, "sales": 0.005 }
    },
    "frustration": {
      "type": "score",
      "score": 0.59,
      "confidence": 0.36,
      "probabilities": { "0": 0.418, "1": 0.575, "2": 0.007 },
      "legend": { "0": "Спокоен, просто описывает ситуацию", "1": "Раздражён, но вежлив", "2": "Очень зол, резкие выражения" }
    }
  },
  "usage": { "input_tokens": 224, "output_tokens": 0 }
}
```

Два поля стоит читать вместе со значением. `confidence` показывает, насколько модель уверена в победителе — низкое значение означает, что варианты близки. `probabilities` показывает, как распределились голоса: в примере выше биллинг выиграл, но техническая команда получила почти 30%, и такое обращение разумно отправить обоим адресатам.

## Чем отличается от Chat Completions {#otlichiya}

| | Chat Completions | System One (Decisions) |
|---|---|---|
| Вход | массив сообщений | `state` и `questions` |
| Выход | текст, который надо разобрать | типизированные значения и вероятности |
| Откуда вероятность | из вероятностей токенов, нередко после повторного вызова | из модели напрямую, калибрована под варианты |
| Токены на выходе | оплачиваются | не генерируются, `output_tokens` равен нулю |
| Ответ вне списка вариантов | возможен | невозможен: выбор только из ваших вариантов |
| Форма ответа | свободная | одна из заранее заданных |

Главное практическое отличие — предсказуемость. Текстовый ответ может не совпасть со схемой, а decisions-модель физически не может ответить тем, чего нет в списке. Ответ сразу ложится в `if`, порог или сортировку, и разбирать его регуляркой не нужно.

Вторая разница — цена и скорость. Тарифицируются только входные токены, выход бесплатен. Liquid AI на своей странице d1 приводит 200–300 мс на текстовое решение.

Есть и ограничение: decisions-модель нельзя вызвать через Chat Completions — запрос вернёт ошибку.

<figure markdown="0">
  <img src="https://b51bc417-3ee0-451a-a83b-8a681d027a96.selstorage.ru/public/9ae0ff4c409effc401802e323eeac7c5.png" alt="Ошибка 400 при вызове liquid/d1.1 через /api/v1/chat/completions" loading="lazy">
  <figcaption>Попытка обратиться к decisions-модели как к чат-модели: RouterAI отвечает 400 и подсказывает нужный эндпоинт.</figcaption>
</figure>

## Модели в RouterAI {#modeli}

В каталоге RouterAI 29 карточек моделей с выходом `decisions` (считая алиас `~typesafe/jev-latest`). Цены — в рублях за 1 млн входных токенов, по данным каталога.

| Модель | id | Контекст | Цена за 1 млн входных токенов | Для чего |
|---|---|---|---|---|
| Joey 1 | `baseten/joey-1` | 16K | 1,06 ₽ | самый дешёвый вариант для массовых проверок |
| Clef Flash | `cloudflare/clef-flash` | 24K | 1,91 ₽ | быстрая классификация текста и изображений |
| d1.1 | `liquid/d1.1` | 64K | 2,12 ₽ | универсальный вариант: текст и картинки, понятная документация |
| Reflex | `morph/reflex` | 64K | 4,46 ₽ | повторные вопросы по одному состоянию благодаря кэшу |
| Jev 1.14 | `typesafe/jev-1.14` | 256K | 4,46 ₽ | длинные документы, все типы вопросов включая `box` |

Разброс по цене внутри класса доходит до четырёх раз, поэтому модель выборки стоит подбирать под задачу, а не брать первую из списка. Чем крупнее модель и длиннее контекст, тем выше цена за токен.

## Пример запроса {#primer}

Эндпоинт: `POST https://routerai.ru/api/v1/decisions`. Синоним — `/api/v1/systemone`, он работает так же и возвращает тот же ответ. Модель, `state` и `questions` обязательны: без любого из них RouterAI ответит 400, не обращаясь к провайдеру.

```python
import requests

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

answers = resp.json()["answers"]
if answers["is_urgent"]["noul"] > 0.8:
    ticket.priority = "high"
route(answers["department"]["choice"])
```

Тот же запрос через curl:

```bash
curl -X POST "https://routerai.ru/api/v1/decisions" \
  -H "Authorization: Bearer $ROUTERAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d @request.json
```

На живом запросе с `liquid/d1.1` та же фраза дала `is_urgent` 0,997, `department` — `billing` с вероятностью около 0,70 и `frustration` 0,59. Вероятности от прогона к прогону немного меняются, поэтому пороги подбирайте на своих данных.

<figure markdown="0">
  <img src="https://b51bc417-3ee0-451a-a83b-8a681d027a96.selstorage.ru/public/3343d18667588de0129bbd536f407d15.png" alt="Ответ /api/v1/decisions для liquid/d1.1: вероятности по вопросам noul, choice и score">
  <figcaption>Ответ на тот же запрос: срочность, отдел и раздражённость клиента в одном вызове. output_tokens=0 — текст модель не генерирует.</figcaption>
</figure>

## Где применяется {#primenenie}

- **Маршрутизация запросов.** Вопросы «какая это область?», «насколько сложная задача?» — и простые запросы уходят дешёвой модели, сложные — сильной. Логика выбора описана в статье про [маршрутизацию ИИ-запросов](/pages/marshrutizaciya-ii-zaprosov-llm-routing).
- **Классификация.** Тема обращения, отдел, язык, тип документа — задача `choice` с закрытым списком.
- **Guardrails.** Проверка входящих и исходящих сообщений на инъекцию промпта, персональные данные, вредный запрос. Проверка стоит доли копейки рядом с вызовом модели, поэтому её можно ставить на каждое сообщение — см. [Guardrails в ИИ](/pages/ai-guardrails).
- **Выбор действия в агенте.** Когда агенту нужно решить, какой инструмент вызвать дальше, он получает список инструментов и возвращает один вариант — без текстовых рассуждений. Обвязку, в которой такой вызов живёт, разбирает статья про [Agent Harness](/pages/agent-harness).

## Связь с RouterAI {#routerai}

RouterAI отдаёт decisions-модели через тот же ключ и тот же баланс, что и Chat Completions: работа из России без VPN, оплата в рублях, отдельный аккаунт провайдера не нужен. Эндпоинт один, а модели — из [общего каталога](/models), где у каждой карточки указаны контекст и цена.

Полезное свойство каталога: если модель поддерживает только текст, а не изображения, это видно по характеристикам карточки ещё до запроса. Алиас `~typesafe/jev-latest` всегда указывает на актуальную версию Jev, а в ответе поле `model` содержит датированный снимок, который обслужил запрос, — его стоит логировать, если вы подбирали пороги под конкретную версию.

## Ограничения {#ogranicheniya}

- Модель отвечает только вариантами из вашего списка: если ни один не подходит, она всё равно выберет ближайший. Для `choice` добавляйте вариант «ничего из перечисленного».
- `noul` — не шкала: значение около 0,5 означает «модель не может решить», а не «средний уровень». Для уровня используйте `score`.
- Ограничения запроса: 32 000 токенов на `state` и самый длинный вопрос, 64 000 токенов на `state` вместе со всеми вопросами, до 255 вариантов в одном `choice`.
- Некоторые модели на входе принимают `state` любой длины, но фактически читают только начало — например, серии Clef на Workers AI обрезают текст примерно до первых 2K токенов.
- Модель не объясняет решение. Если нужна причина отказа, спросите её отдельным вопросом `choice` с вариантами причин.

## Что почитать {#chto-pochitat}

- [Jev: решения вместо текста](/docs/guides/overview/decisions/overview) — основной гайд RouterAI по decisions-эндпоинту
- [Как задавать вопросы](/docs/guides/overview/decisions/questions) — типы вопросов, `state`, картинки
- [Рецепты и ограничения](/docs/guides/overview/decisions/recipes) — готовые схемы и лимиты
- [System One](https://docs.typesafe.ai/concepts/system-one) — документация TypeSafe о классе моделей
- [Introducing d1](https://www.liquid.ai/blog/d1-decision-model) — как устроена модель d1 от Liquid AI
- [clef-flash на Workers AI](https://developers.cloudflare.com/workers-ai/models/clef-flash/) — описание одной из моделей Cloudflare
