Перейти к навигации

Decisions API

Decisions API

Большинство моделей возвращают абзац текста, из которого приложению ещё нужно извлечь решение: запросить JSON, разобрать и проверить его, а при отклонении формата повторить запрос. Decisions API убирает этот этап. Вы передаёте текущее состояние приложения и вопросы. Каждый ответ уже имеет тип: вероятность «да», один из заданных вариантов или значение на вашей шкале, с указанием вероятности.

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

POST https://api.beatapi.io/v1/systemone

Это путь эндпоинта TypeSafe с их же структурой запроса и ответа. В клиенте TypeSafe или его SDK достаточно изменить базовый URL и имя модели. POST /v1/decisions также доступен как псевдоним.

МодельКогда использовать
jev-1.14-freeНачните здесь: бесплатный JEV. Ввод и вывод стоят $0, даже при нулевом балансе. Поддерживает изображения и вопросы box. См. раздел Бесплатные вызовы ниже.
jev-1.14Необязательная платная модель. 0.0252за1млнвходныхтокенов(на400.0252 за 1 млн входных токенов (на 40 % ниже базовой цены 0.042); вывод не оплачивается. Поддерживает изображения и вопросы box.
jev-1.13-freeПредыдущая бесплатная версия, доступная на тех же условиях, что и jev-1.14-free. Поддерживает изображения, но не box.
jev-1.13Предыдущая платная версия, доступная по той же цене, что и jev-1.14. Поддерживает изображения, но не box.

jev-1.14 соответствует jev-1.14.0 от TypeSafe: умеренное улучшение относительно 1.13 по нескольким направлениям, особенно для многоязычного ввода. TypeSafe не опубликовала численные показатели, поэтому здесь они не приводятся. Цена осталась прежней.

Это не /v1/chat/completions: модели JEV там недоступны. В запросе нет messages, а в ответе нет choices. Модель принятия решений не генерирует текст.

Быстрый старт

Начните с jev-1.14-free: ввод и вывод стоят $0, даже при нулевом балансе. Используйте ключ в стандартной группе auto. У бесплатных вызовов отдельный лимит: один успешный запрос в минуту до первого пополнения, до десяти в минуту после любого пополнения. См. Бесплатные вызовы.

curl https://api.beatapi.io/v1/systemone \
-H "Authorization: Bearer $BEATAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "jev-1.14-free",
"state": "Task: clean up inactive accounts before the quarterly report.\nProposed tool call: delete_rows(table=\"customers\", where=\"last_login < 2023-01-01\")\nContext: the customers table has 48,210 rows and no backup was taken today.",
"questions": {
"safe_to_run": {
"type": "noul",
"instructions": "Is this action safe to run without a human approving it first?",
"criteria": {
"true": "Reversible or low-impact, and clearly within the stated task.",
"false": "Destructive, irreversible, or broader than the task requires."
}
}
}
}'

Следующий ответ приведён для иллюстрации, это не результат измерения. Разберите JSON, проверьте HTTP-статус и тип ответа, затем проверьте значения перед применением своих порогов.

{
"id": "task_01J...",
"model": "jev-1.14-free",
"answers": {
"safe_to_run": { "type": "noul", "noul": 0.04 }
},
"usage": { "input_tokens": 384, "output_tokens": 22 }
}

0.04 означает оценённую моделью вероятность ответа да: четыре процента. Поэтому агент останавливается и обращается к человеку, ради чего и задавался вопрос.

Типизированное решение без цикла исправления JSON

Укажите BeatAPI, отправьте состояние и вопросы, прочитайте ответы. Псевдоним /v1/decisions работает так же. Это не chat/completions: модели JEV там недоступны.

const response = await fetch("https://api.beatapi.io/v1/systemone", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.BEATAPI_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "jev-1.14-free",
state: {
task: "clean up inactive accounts",
tool: 'delete_rows(table="customers", where="last_login < 2023-01-01")',
rows: 48210,
backup_today: false,
},
questions: {
safe_to_run: {
type: "noul",
instructions: "Is this action safe to run without a human approving it first?",
criteria: {
true: "Reversible or low-impact, and clearly within the stated task.",
false: "Destructive, irreversible, or broader than the task requires.",
},
},
},
}),
});
if (!response.ok) {
// On HTTP 429, wait the Retry-After seconds before retrying.
throw new Error(`JEV HTTP ${response.status}; Retry-After: ${response.headers.get("Retry-After")}`);
}
const decision = await response.json();
const answer = decision.answers?.safe_to_run;
if (answer?.type !== "noul" || !Number.isFinite(answer.noul)
|| answer.noul < 0 || answer.noul > 1) {
throw new Error("Invalid JEV answer; route to human review");
}
// Illustrative threshold: evaluate it on your own cases before use.
const needsReview = answer.noul < 0.9;
console.log({ probability: answer.noul, needsReview });

Запрос

ПолеТипОбязательноОписание
modelstringДаjev-1.14 или jev-1.14-free для бесплатного вызова. jev-1.13 и jev-1.13-free также принимаются. Запрос без модели отклоняется.
statestring | object | arrayДаВсё, что приложение знает сейчас: строка, JSON-объект или массив текстовых значений. Для изображения задайте его в assets и сошлитесь на него в тексте (см. Изображения). Аудио, видео и другие двоичные данные сначала нужно преобразовать в текст.
questionsobjectДаОдин или несколько именованных вопросов. Выбранное вами имя станет ключом ответа.
assetsobjectНетИзображения, упомянутые в тексте. Ключом служит выбранная строка ссылки, например "<door>". Обязательно для вопросов box. См. Изображения.

Каждый вопрос содержит type, instructions и, в зависимости от типа, criteria.

ПолеТипОбязательноОписание
typestringДаnoul, choice, score или box.
instructionsstring | object | arrayДля noul и boxВопрос на обычном языке, как для коллеги. Обязателен для noul, чтобы задать смысл «да/нет», и для box, чтобы указать искомый объект. Для choice и score может хватить criteria.
criteriaсм. нижеЗависит от типаЗначение вариантов ответа. Для box не принимается.

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

noul: калиброванная вероятность

Для решения «да/нет» по заданному порогу, а не случайного выбора. Ответ представляет собой число от 0 до 1.

"refund_ok": {
"type": "noul",
"instructions": "Should this refund be approved automatically?",
"criteria": {
"true": "Clear billing error, under $100, first request from this account.",
"false": "Disputed usage, over $100, or a repeat request."
}
}
"refund_ok": { "type": "noul", "noul": 0.83 }

criteria здесь необязателен, но описание обеих сторон уточняет ответ. Выберите свои пороги: например, автоматически одобрять выше 0.9, передавать на следующий уровень ниже 0.6, а промежуточные случаи ставить в очередь на проверку.

noul — настоящее имя типа, не опечатка вместо bool. Неверное имя приводит к ошибке 400 с перечнем допустимых типов.

choice: один из заданных вариантов

criteria — объект, сопоставляющий имя каждого варианта с его смыслом. Ответ содержит выбранный вариант и полное распределение вероятностей.

"route": {
"type": "choice",
"instructions": "Which queue should this ticket go to?",
"criteria": {
"billing": "Payment, invoices, refunds, or charges.",
"technical": "The product is broken or erroring.",
"sales": "Pre-purchase questions about plans or pricing."
}
}
"route": {
"type": "choice",
"choice": "billing",
"probabilities": { "billing": 1, "technical": 0, "sales": 0 },
"confidence": 1
}

score: положение на вашей шкале

criteria — массив описаний шкалы от меньшего значения к большему. Ответ — непрерывное значение по индексам массива, начиная с 0.

"urgency": {
"type": "score",
"instructions": "How urgent is this ticket?",
"criteria": [
"1 - can wait a week",
"2 - answer within a few days",
"3 - answer today",
"4 - answer within an hour",
"5 - wake someone up"
]
}
"urgency": {
"type": "score",
"score": 1.98,
"legend": { "0": "1 - can wait a week", "1": "2 - answer within a few days",
"2": "3 - answer today", "3": "4 - answer within an hour",
"4": "5 - wake someone up" },
"probabilities": { "0": 0, "1": 0.17, "2": 0.67, "3": 0.16, "4": 0 },
"confidence": 0.72
}

1.98 находится между legend["1"] и legend["2"], почти у второго значения: «ответить сегодня». Округляйте для выбора категории, сохраняйте дробную часть для сортировки очереди.

box: положение объекта на изображении

Используйте для поиска одного объекта. box поддерживают только jev-1.14 и jev-1.14-free. В instructions укажите, что искать; criteria не допускается. В запросе должно быть хотя бы одно изображение (см. Изображения), иначе вернётся ошибка 400. Модели jev-1.13 и jev-1.13-free отклоняют вопросы box.

"where": {
"type": "box",
"instructions": "The red rectangle in <picture>."
}
"where": {
"type": "box",
"box": { "top": 0.12, "bottom": 0.43, "left": 0.56, "right": 0.89 },
"confidence": 0.93
}

Четыре значения — доли от 0 до 1 от высоты (top, bottom) и ширины (left, right), отсчитываемые от верхнего и левого края. Умножьте их на размеры изображения в пикселях, чтобы получить координаты. confidence может быть null; проверяйте это перед сравнением с порогом.

Изображения

Модели 1.13 и 1.14 обрабатывают изображения. Задайте каждое один раз в объекте assets верхнего уровня, а строку ссылки поместите в текст state, в instructions вопроса или в значение criteria. Изображение вставляется в месте ссылки.

ПолеТипОбязательноОписание
typestringНетimage, можно опустить.
formatstringДаpng, jpg или jpeg.
data_base64stringДаБайты изображения в стандартном base64 без префикса data:.
curl https://api.beatapi.io/v1/systemone \
-H "Authorization: Bearer $BEATAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "jev-1.14-free",
"state": "Check the uploaded image.",
"assets": {
"<picture>": { "type": "image", "format": "png", "data_base64": "iVBORw0KGgo..." }
},
"questions": {
"red": { "type": "noul", "instructions": "Is there a red rectangle in <picture>?" },
"where": { "type": "box", "instructions": "The red rectangle in <picture>." }
}
}'

В этом вызове использовалось белое изображение 280×280 пикселей с красным прямоугольником. Ниже приведён измеренный результат. id — идентификатор запроса BeatAPI, добавленный к полям поставщика.

{
"id": "task_01J...",
"model": "jev-1.14-free",
"answers": {
"red": { "type": "noul", "noul": 0.92 },
"where": { "type": "box",
"box": { "top": 0.12, "bottom": 0.43, "left": 0.56, "right": 0.89 },
"confidence": 0.93 }
},
"usage": { "input_tokens": 501, "output_tokens": 36 }
}

Правила ссылок:

  • Ссылка сопоставляется как обычный текст. Подойдёт любая строка, например "<door>" или "[img-1]". Ключи объектов не просматриваются; размещайте ссылку в значении.
  • Каждая заданная ссылка должна использоваться хотя бы раз, иначе запрос отклоняется с ошибкой 400.
  • Отправляйте только JSON. Multipart не поддерживается. Поле part в ресурсе вызывает ошибку 400 с просьбой использовать data_base64.
  • Заголовок TypeSafe-Danger-Zone-Images, обязательный в API TypeSafe, отправлять не нужно: BeatAPI добавит его сам. Передать его всё же можно.

Ограничения изображений:

  • Только статические PNG или JPEG. Анимированные изображения отклоняются.
  • До 16 MiB на изображение после декодирования и до 32 MiB на всё тело запроса.
  • Каждая сторона — от 280 до 1568 пикселей. При размере ниже минимума вернётся Invalid image dimensions.
  • Автоматического изменения размера нет. Уменьшите слишком большое изображение перед отправкой.
  • Одно некорректное изображение приводит к отказу всего запроса.

Несколько вопросов в одном вызове

Задавайте все вопросы об одном состоянии одновременно. Ответы независимы, а общее состояние учитывается один раз, не для каждого вопроса. Вызовы jev-1.14-free остаются бесплатными.

curl https://api.beatapi.io/v1/systemone \
-H "Authorization: Bearer $BEATAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "jev-1.14-free",
"state": {"ticket": "charged twice this month", "plan": "pro", "tenure_days": 412},
"questions": {
"refund_ok": {"type": "noul", "instructions": "Approve the refund automatically?"},
"urgency": {"type": "score", "instructions": "How urgent?", "criteria": ["low", "medium", "high"]}
}
}'

Ответ

ПолеОписание
modelМодель, которая ответила.
answersПо одной записи на вопрос под заданным вами именем. В записи есть type и поля из таблицы ниже.
usageinput_tokens и output_tokens этого вызова.
idИдентификатор запроса BeatAPI; указывайте его при обращении в поддержку. Добавляется к трём полям поставщика. Клиенты, не описывающие это поле, могут его игнорировать.

Поля ответа по типу вопроса:

typeПоля
noulnoul: вероятность да, от 0 до 1.
choicechoice: выбранный вариант; probabilities: значение для каждого варианта; confidence.
scorescore: значение по индексам criteria; legend; probabilities; confidence.
boxbox: top, bottom, left, right, доли от 0 до 1 высоты или ширины, отсчитываемые от верхнего и левого края; confidence может быть null.

Ответ синхронный: успешный HTTP-ответ означает, что решение готово. Не нужно опрашивать статус или скачивать файлы результата.

Ограничения

  • Контекст: 64K токенов суммарно для state и всех вопросов. state вместе с самым длинным вопросом должны помещаться в 32K токенов. См. ограничения моделей.
  • Расширенный контекст пока не поддерживается. У TypeSafe есть заголовок для увеличения контекста до 262K токенов, но BeatAPI его не поддерживает. Действует лимит 64K, включая токены изображений.
  • Изображения: только JSON, без multipart, с ограничениями раздела Изображения.
  • Нет параметров сэмплирования. temperature, top_p, seed и аналогичные параметры не принимаются. Формат ответа детерминирован по замыслу.
  • Нет потоковой выдачи. Ответ — значение, а не последовательность токенов.
  • Для choice нужен непустой объект criteria, для score — непустой массив criteria. Иначе вернётся ошибка 400 с пояснением. instructions необязателен, но пустое значение отклоняется.

Ошибки

Ошибки используют стандартный формат BeatAPI и всегда содержат retryable. Полная таблица и правила повторных запросов приведены в разделе Ошибки.

HTTPКодЗначение
400bad_requestНекорректный JSON, модель, тип, критерии или изображение; сообщение указывает поле. Например: неиспользуемая или неверная запись assets, поле part, box без изображения, неподдерживаемое изображение или размер вне диапазона.
401missing_api_key / invalid_api_keyКлюч отсутствует, неверен, просрочен, отключён или его квота исчерпана.
402insufficient_creditsНулевой баланс или недостаточно средств для вызова.
403forbiddenКлюч или аккаунт не имеет доступа к модели, например если она недоступна за подарочные кредиты.
404not_foundНеизвестная модель.
429rate_limit_exceededДостигнут лимит запросов, включая бесплатную модель. Подождите число секунд из Retry-After.
500internal_errorНеожиданная внутренняя ошибка; запрос можно повторить.
503processing_unavailable или другой код processing_*Не удалось завершить запрос. Повторяйте с увеличением интервала.

Неуспешные вызовы не расходуют кредиты.

Оплата

Платные jev-1.14 и jev-1.13 тарифицируются только по входным токенам в state, questions и изображениях: **0.0252за1млнвходныхтокенов∗∗,на400.0252 за 1 млн входных токенов**, на 40 % ниже базовой цены 0.042. Вывод не оплачивается: это типизированное значение, а не сгенерированный текст. Точный расход каждого вызова указан в usage.input_tokens, что позволяет сверить затраты.

Изображения оплачиваются по той же ставке за входные токены, без отдельной платы за изображение. Ориентиры: 280×280 — около 320 токенов, 512×512 — около 840, 1024×768 — около 2 200. Фактическое число указано в usage.input_tokens.

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

Бесплатные вызовы

При "model": "jev-1.14-free" вызов ничего не стоит. Для jev-1.13-free действуют те же условия. Это те же модели, что и платные аналоги, с теми же структурами запроса и ответа. В ответе сохраняется запрошенное имя модели. Они работают при нулевом балансе, поэтому интеграцию можно начать до пополнения.

  • Доступ через стандартную группу auto, назначаемую каждому новому ключу. Ключ, закреплённый за отдельной группой, не имеет доступа.
  • У бесплатных вызовов фиксированный отдельный лимит на аккаунт: один успешный запрос в минуту до первого пополнения, затем не более десяти в минуту после пополнения на любую сумму. Счётчик независим от обычного лимита аккаунта и не расходует квоту платных вызовов.
  • При превышении вернётся 429 rate_limit_exceeded с Retry-After. Подождите указанное время.
  • Платный jev-1.14 доступен по ставке выше и использует обычный лимит аккаунта, растущий с общей суммой пополнений. Для его выбора измените только имя модели.

Более высокие лимиты запросов

После любого пополнения jev-1.14-free остаётся бесплатным и допускает не более 10 успешных вызовов в минуту. Платные вызовы jev-1.14 используют общую квоту аккаунта. Текущий лимит и следующий порог пополнения указаны в панели управления; отдельной квоты JEV нет.

Пополните баланс, затем явно измените model с jev-1.14-free на jev-1.14, если нужны платные вызовы. Эндпоинт, state и questions остаются прежними. Ввод стоит **0.0252за1млнтокенов∗∗(базоваяцена0.0252 за 1 млн токенов** (базовая цена 0.042, скидка 40 %), вывод бесплатный. Пополнение не переключает модель автоматически. Для платных вызовов по-прежнему нужен достаточный баланс. При 429 соблюдайте Retry-After.