Choice, Score и Noul — три типа вопросов Jev
Любой вопрос к Jev относится к одному из трёх типов. От выбора типа зависит, сможет ли код сразу действовать по ответу. Разбираем каждый тип с примерами, полями ответа и типичными ошибками.
Как устроен любой вопрос
У каждого вопроса четыре части:
- ключ — имя вроде
refund_requested, под которым вернётся ответ. Модель его не видит, он нужен только коду; type—noul,choiceилиscore;instructions— сам вопрос или утверждение, которое модель оценивает. Обычно это строка, но можно передать объект, если вопросу нужны данные;criteria— варианты для Choice, уровни для Score и необязательное пояснение «да» и «нет» для Noul.
| Тип | На какой вопрос отвечает | Что возвращает |
|---|---|---|
| Noul | Это правда? | noul, вероятность «да» от 0 до 1 |
| Choice | Какой из вариантов? | choice, probabilities, confidence |
| Score | Какой уровень на шкале? | score, legend, probabilities, confidence |
Два свойства делают ответы удобными для кода. Ответ всегда лежит внутри ваших вариантов или уровней: значение вне списка модель не вернёт. И ответы независимы, один вопрос не видит ответа другого. Поэтому вопросы можно добавлять и убирать, не меняя остальные результаты. Полный формат запроса и ответа описан в справочнике по API.
Noul — да или нет
Когда использовать: чистые бинарные вопросы. Письмо просит вернуть деньги? В сообщении есть персональные данные? Клиент просит живого оператора?
Что возвращает: одно число noul от 0 до 1, вероятность ответа «да». Около 1 — уверенное «да», около 0 — уверенное «нет», около 0,5 модель колеблется. Отдельного confidence у Noul нет: у распределения всего два исхода, и одно число описывает его полностью.
"repeat_contact": {
"type": "noul",
"instructions": "Клиент уже обращался в поддержку по этому вопросу?",
"criteria": {
"true": "Упоминает прошлое обращение, номер заявки или то, что уже писал",
"false": "Нет признаков предыдущих обращений"
}
}
criteria у Noul необязательны. Добавляйте их, когда граница между «да» и «нет» тонкая, и сравнивайте результат с вариантом без них на своих примерах. TypeSafe советует оставлять ту формулировку, которая лучше работает на ваших документах.
Три правила формулировки:
- Одно условие на вопрос. «Клиент зол и просит возврат?» заставляет модель судить о двух вещах сразу. Задайте два Noul и объедините ответы в коде.
- Высокое значение означает «да». Вопрос «Сообщение без персональных данных?» переворачивает смысл, и код, который прочтёт ответ через полгода, легко перепутает знак. Пишите «В сообщении есть персональные данные?».
- Noul не измеряет степень. Значение 0,5 на вопрос «Кандидат сильный в Python?» означает, что модель не уверена, а не что опыт средний. Для степени нужен Score с описанными уровнями.
Порог выбирайте по цене ошибки. Если дорого ложное «да» (возврат денег, вызов дежурного), поднимайте порог. Если дорого пропустить «да» (сигнал о небезопасном действии), опускайте. Середину, например от 0,3 до 0,7, разумно отдавать человеку.
Choice — выбор из списка
Когда использовать: классификация и маршрутизация, когда варианты равноправны и между ними нет порядка: отдел, тип документа, язык, следующий шаг агента.
Что возвращает: choice (вариант с наибольшей вероятностью), probabilities (вероятность каждого варианта, в сумме 1) и confidence (насколько распределение сосредоточено на одном варианте).
"department": {
"type": "choice",
"instructions": "Какой отдел должен ответить на обращение?",
"criteria": {
"billing": "Оплата, списания, возвраты, счета",
"tech": "Ошибки, сбои, настройка интеграций",
"sales": "Тарифы, покупка, скидки",
"other": null
}
}
Модель видит и названия вариантов, и их описания. Пишите описания так, чтобы они разводили варианты между собой, а null оставляйте там, где название говорит само за себя. Вариант other нужен, если список может не покрыть все входы: без него модель выберет ближайший, даже когда не подходит ни один.
Лимит — 255 вариантов в одном вопросе. Каждый вариант стоит нескольких токенов, поэтому TypeSafe советует давать полный список, а не урезанный. Для больших справочников и деревьев категорий спрашивайте уровень за уровнем: сначала группа, затем вариант внутри неё.
Если модель постоянно путает два варианта, опишите каждый объектом: что вариант охватывает, что к нему не относится, пара примеров. Имена полей вроде what, not_for и examples не зарезервированы, их выбираете вы, а модель читает их вместе со значениями. Подробнее о таких описаниях — в статье о продвинутых вопросах.
Читайте не только победителя. Если второй вариант набрал заметную долю, например больше 0,25, обращение лежит на границе двух отделов: отправьте его в первый и копию во второй. Что показывает confidence и как ставить на него пороги, объясняем в статье об уверенности Jev.
Score — оценка по шкале
Когда использовать: ответ лежит на спектре, и каждую ступень можно описать словами. Срочность, серьёзность бага, раздражение клиента, уровень риска.
Что возвращает: score (позицию на шкале), legend (описания уровней по номерам), probabilities (вероятность каждого уровня) и confidence.
"risk": {
"type": "score",
"instructions": "Насколько рискованно выполнить эту команду без подтверждения?",
"criteria": [
"Только чтение, ничего не меняет",
"Меняет данные, изменения легко откатить",
"Меняет данные, откат сложный",
"Удаляет данные или необратимо меняет систему"
]
}
Номер уровня равен его позиции в массиве, отсчёт с нуля. score — средневзвешенная позиция: номера уровней умножают на их вероятности и складывают. Например, модель распределила вероятность по четырём уровням так: 0,1; 0,6; 0,3 и 0 (числа условные). Тогда score = 1 × 0,6 + 2 × 0,3 = 1,2, то есть ответ между уровнями 1 и 2, ближе к первому.
Как писать уровни:
- Описывайте ситуации, а не степени. С фразой «меняет данные, откат сложный» модель может сопоставить текст. С «умеренным риском» сопоставлять нечего.
- Не полагайтесь на номера. Модель не видит номер уровня и соседние уровни, каждое описание она оценивает отдельно. Поэтому шкала
["0", "1", "2"]с инструкцией «оцени от 0 до 2» работает плохо, а фраза «хуже предыдущего» ничего не значит. - Одно измерение на шкалу. «Опытный, пунктуальный и вежливый» — это три вопроса. Кандидата, сильного в одном и слабого в другом, на такой шкале не разместить.
- Редкую крайность выносите в отдельный уровень. Если на «очень зол» и «угрожает» код реагирует по-разному, добавьте уровень «угрозы или оскорбления».
Уровней может быть от 2 до 10. Делайте их столько, сколько можете описать различимо, трёх часто достаточно. Разные распределения способны дать одинаковый score: 1,0 получится и при всей вероятности на уровне 1, и при половине на уровнях 0 и 2. Поэтому в спорных случаях смотрите probabilities и confidence.
Чтобы сложить шкалы разной длины, сначала поделите каждый score на номер верхнего уровня, и все оценки лягут в диапазон от 0 до 1. Как собрать из них итоговый балл с весами, разобрано в статье о составной оценке.
Как выбрать тип
| Ответ, который нужен коду | Тип | Как код его использует |
|---|---|---|
| «Да» или «нет» | Noul | if с порогом |
| Один из равноправных вариантов | Choice | Ветвление по choice |
| Позиция на упорядоченной шкале | Score | Порог, сортировка, веса |
Если подходят два типа, берите тот, по ответу которого код действует напрямую. Choice между «возврат», «замена» и «справка» сразу превращается в три ветки кода, Score раздражения — в порог, Noul — в if.
Не ждите от разных типов согласованных чисел. В документации TypeSafe один и тот же вопрос в виде Noul дал 0,22, а в виде Choice с вариантами «да» и «нет» — 0,01 для «да». Вопрос и его отрицание, заданные двумя Noul, получили 0,72 и 0,47, в сумме 1,19. Поэтому порог, подобранный для Noul, не переносите на Choice и не рассчитывайте, что вероятность отрицания равна единице минус вероятность утверждения.
Несколько вопросов в одном запросе
Все вопросы запроса Jev считает параллельно. Дополнительный вопрос почти не меняет время ответа и добавляет только свои входные токены, а state оплачивается один раз. Поэтому задавайте в одном запросе всё, что может понадобиться коду, включая «спекулятивные» вопросы. Причина возврата пригодится, только если обращение ушло в отдел возвратов, но спросить о ней заранее почти ничего не стоит. Этот приём TypeSafe называет speculative fan-out, подробнее о нём — в статье о fan-out.
Второй запрос нужен, только когда код не может собрать его без первого ответа. Например, по выбранной категории нужно загрузить из базы новые данные для state или сузить список вариантов.
Частые ошибки
- Составные вопросы. Вместо «Это срочная жалоба про оплату?» задайте три вопроса: срочность, жалоба, оплата. Они считаются параллельно и почти не замедляют ответ.
- Пересекающиеся варианты. Если «Оплата» и «Возврат» частично совпадают, модель будет колебаться между ними, и
confidenceупадёт. Разведите описания или добавьте к каждому поле «что сюда не относится». - Нет варианта «другое». Без него модель вынуждена выбрать ближайший вариант, даже если не подходит ни один.
- Смысл спрятан в ключе. Ключ
is_vip_clientмодель не видит. Если вinstructionsнаписано только «Да?», модели не на что опереться. - Счёт и даты в вопросах. TypeSafe сама пишет, что Jev 1.13 ненадёжно считает, сравнивает даты и хуже справляется с вопросами в несколько шагов. Считайте в коде, а модели оставляйте смысловые суждения. Другие слабые места собраны в статье об ограничениях Jev.
Собрать вопросы и сразу получить код можно в конструкторе запроса.
Частые вопросы
Чем Noul отличается от Choice с вариантами «да» и «нет»?
Noul возвращает одно число — вероятность «да», его удобно сравнивать с порогом. Choice возвращает метку, распределение и confidence. Числа двух типов не совпадают даже для одного вопроса, поэтому для бинарных вопросов берите Noul.
Сколько вопросов можно задать в одном запросе?
В документации TypeSafe отдельного лимита на число вопросов нет, ограничивает бюджет в 64 тысячи токенов на запрос. Polza.AI называет предел в 256 вопросов. Все вопросы считаются параллельно, но каждый добавляет входные токены.
Можно ли в Score использовать числа вместо описаний уровней?
Не стоит. Модель не видит номер уровня и оценивает каждое описание отдельно, поэтому шкала из голых цифр даёт размытый ответ. Описывайте уровни ситуациями.
Почему score получается дробным?
Это средневзвешенная позиция на шкале, то есть номера уровней, умноженные на их вероятности. Если коду нужен один уровень, значение округляют.
Видит ли модель имя вопроса?
Нет. Ключ вопроса нужен только вашему коду и в модель не передаётся, поэтому полный вопрос пишите в instructions.