Как задавать вопросы Jev
Три типа вопросов — noul, choice, score, — структура state и приёмы, от которых зависит качество ответов.
Запрос к Jev состоит из двух частей: state — что оцениваем, и questions — что хотим узнать. Качество ответов почти целиком определяется тем, как сформулированы вопросы. Если вы ещё не отправляли запросов — начните с первого запроса.
Один вопрос — одно суждение
Задавайте вопросы, на которые знающий человек ответит за секунду, взглянув на текст.
| Плохо | Хорошо |
|---|---|
| «Проанализируй обращение и реши, что с ним делать» | «Клиент просит вернуть деньги?» |
| «Оцени резюме кандидата» | Отдельно: глубина опыта в Python, опыт руководства, опыт проектирования систем |
| «Это спам?» | Отдельно: есть ли ссылка на сторонний сайт, обещают ли лёгкий заработок, обращаются ли по имени |
Если решение зависит от нескольких факторов — спросите про каждый отдельно и сложите ответы в коде со своими весами. Изменились приоритеты — меняете коэффициент, а не переписываете промпт. Лишних запросов это не добавляет: все вопросы к одному state уходят одним запросом и обрабатываются параллельно.
Из чего состоит вопрос
"refund_requested": {
"type": "noul",
"instructions": "Клиент просит вернуть деньги?"
}
- Имя вопроса (
refund_requested) — для вашего кода: под ним придёт ответ. Модель его не видит, поэтому весь смысл должен быть вinstructions. type—noul,choiceилиscore.instructions— сам вопрос. Пишите точно и буквально: Jev отвечает на то, что написано, а не на то, что вы имели в виду.criteria— варианты ответа: уchoiceиscoreобязательны, уnoul— по желанию.
Инструкции вроде «ответь только JSON», «не рассуждай вслух», «не используй инструменты» здесь не нужны: модель не пишет текст и не вызывает инструменты. Такие фразы только занимают токены.
Noul — да или нет
Возвращает вероятность «да» от 0 до 1. Около 1 — уверенное «да», около 0 — уверенное «нет», около 0,5 — модель не знает.
"has_pii": {
"type": "noul",
"instructions": "В сообщении есть персональные данные: телефон, адрес, паспорт, номер карты?"
}
Формулируйте так, чтобы высокая вероятность означала «да». Если граница между «да» и «нет» тонкая — уточните её в criteria:
"is_urgent": {
"type": "noul",
"instructions": "Клиент сообщает о срочной проблеме?",
"criteria": {
"true": "Прямо сказано о срочности, сроках или идущих убытках",
"false": "Срочность не упомянута, даже если проблема серьёзная"
}
}
noul — не шкала. Значение 0,5 на вопрос «Кандидат силён в Python?» означает «модель не может решить», а не «средний уровень». Чтобы измерить уровень, используйте score.
Choice — выбор из вариантов
Подходит, когда ответ — одна из известных категорий без порядка между ними: отдел, тема, тип документа, язык.
"department": {
"type": "choice",
"instructions": "Какая команда должна заняться обращением?",
"criteria": {
"returns": "Обмен, возврат, не тот или повреждённый товар",
"shipping": "Статус доставки, задержки, потерянные посылки",
"billing": "Списания, счета, проблемы с оплатой",
"other": "Ничего из перечисленного"
}
}
- Ключ — имя варианта, значение — его описание. Модель видит и то и другое, поэтому описания должны отличать варианты друг от друга. Если имя говорит само за себя, вместо описания можно передать
null. - Давайте полный список, а не выжимку: вариантов может быть до 255, каждый стоит несколько токенов.
- Добавляйте
otherили «ничего из перечисленного», если список может не покрывать все случаи. Иначе модель будет вынуждена выбрать неподходящий вариант.
В ответе choice — победитель, probabilities — вероятности всех вариантов (в сумме 1), confidence — насколько победитель оторвался от остальных.
Score — оценка по шкале
Подходит, когда ответ лежит на спектре и вы можете описать каждую его точку: критичность, раздражение, релевантность, качество.
"bug_severity": {
"type": "score",
"instructions": "Насколько серьёзна описанная проблема?",
"criteria": [
"Косметический дефект, на работу не влияет",
"Функция сломана, но есть обходной путь",
"Работа заблокирована, обходного пути нет"
]
}
Уровни нумеруются с нуля по порядку в массиве — здесь это 0, 1 и 2. score в ответе — положение на этой шкале, и оно может оказаться между уровнями: 1,3 означает «в основном уровень 1, отчасти уровень 2».
Как писать уровни:
- Описывайте ситуации, а не степени. «Функция сломана, но есть обходной путь» модель может сопоставить с текстом. «Умеренно серьёзно» — не с чем сопоставлять.
- Каждый уровень оценивается отдельно, соседей модель не видит. Фразы вроде «хуже предыдущего» и цифры в описаниях не работают.
- Одна шкала — одно измерение. «Пунктуальный, умный и опытный» — это три шкалы. Разделите и сложите в коде.
- От 2 до 10 уровней. Берите столько, сколько можете описать различимо; трёх обычно достаточно.
- Если на краю шкалы есть редкий случай, на который нужна особая реакция, дайте ему свой уровень: после «очень зол» добавьте «угрожает или оскорбляет».
Один и тот же score могут дать разные распределения: 1,0 — это и «точно уровень 1», и «поровну 0 и 2». Смотрите на confidence и probabilities вместе со score. Низкая уверенность обычно значит одно из трёх: уровни перекрываются, вопрос измеряет сразу несколько вещей или в тексте недостаточно данных.
Какой тип выбрать
Берите тот, чей ответ код использует напрямую: noul ложится в if, choice — в ветвление по категориям, score — в порог или сортировку.
| Нужно узнать | Тип |
|---|---|
| Есть ли признак: жалоба, срочность, просьба о возврате | noul |
| К какой категории относится | choice |
| Насколько сильно выражено свойство | score |
| Какое значение из закрытого списка названо в тексте (месяц, валюта, товар) | choice с вариантом «не указано» |
| Причина решения (почему отклонили) | choice вместо noul: вариант и есть причина |
Что класть в state
state — это материал для оценки: строка, объект или массив. Только текст: изображения, аудио и видео не поддерживаются.
| Формат | Когда удобен | Пример |
|---|---|---|
| Строка | Одно сообщение или абзац | "С меня дважды списали деньги." |
| Объект | Несколько связанных частей с именами | {"message": "...", "policy": "..."} |
| Массив | Последовательность сообщений или записей | ["Здравствуйте", "Номер заказа A-104", "..."] |
В большинстве случаев лучше объект: у каждой части есть имя, и в вопросе на неё можно сослаться.
Кладите только то, что нужно для решения. Лишний текст отвлекает модель и снижает точность — отфильтруйте данные в коде до запроса. Актуальные факты (правила возврата, остатки, статус заказа) передавайте в state, а не рассчитывайте на знания модели.
Ссылки на части state
Если state — объект, укажите в вопросе путь к нужной части в обратных кавычках:
{
"model": "~typesafe/jev-latest",
"state": {
"ticket": {
"subject": "Двойное списание",
"messages": [
{ "from": "customer", "text": "С меня дважды списали за заказ A-104. Верните, пожалуйста, лишний платёж." },
{ "from": "support", "text": "Проверяем списания." }
]
},
"order": {
"id": "A-104",
"charges": [
{ "amount_rub": 4900, "status": "captured" },
{ "amount_rub": 4900, "status": "captured" }
]
},
"refund_policy": "Двойные списания возвращаются в полном объёме."
},
"questions": {
"refund_requested": {
"type": "noul",
"instructions": "В `ticket.messages[0].text` клиент просит вернуть деньги?"
},
"policy_supports_refund": {
"type": "noul",
"instructions": "Поддерживает ли `refund_policy` возврат, о котором просят в `ticket.messages[0].text`, с учётом `order.charges`?"
}
}
}
Ответ: refund_requested — 0,98, policy_supports_refund — 0,96.
Ссылка по индексу надёжна, пока элементов немного и они разные по смыслу. Когда в массиве десятки однотипных элементов (ники, отзывы, фрагменты), ссылки вида items[17] разрешаются неточно: на ответ начинают влиять соседние элементы. В таких случаях продублируйте сам элемент в тексте вопроса — см. рецепт пакетной проверки.
Много вопросов одним запросом
Все вопросы к одному state отправляйте вместе. Они обрабатываются параллельно и независимо: время ответа почти не растёт, а ответ на один вопрос не влияет на другой.
Спрашивайте и то, что может не понадобиться. Если обращение окажется не про баг — просто не читайте ответ про критичность бага. Лишний вопрос стоит несколько токенов, а отдельный запрос — ещё раз весь state и служебные токены.
Каждый вопрос даёт ровно один ответ. Перечислить в одном вопросе несколько объектов («оцени ники A, B и C») нельзя — получите одну вероятность на всех. На каждый объект нужен свой вопрос.
Второй запрос оправдан только тогда, когда без первого ответа его нельзя составить: нужно сходить за данными, зависящими от ответа, или варианты следующего вопроса определяются предыдущим (обход дерева категорий уровень за уровнем).
Предел — общий бюджет запроса, см. лимиты.
Когда варианты путаются: структурные описания
Начинайте с коротких строк. Если модель стабильно путает два близких варианта, опишите каждый объектом: что входит, что не входит, примеры. Имена полей вы придумываете сами — модель видит их вместе со значениями, так что называйте понятно и одинаково у всех вариантов.
"topic": {
"type": "choice",
"instructions": "О чём спрашивает клиент?",
"criteria": {
"return_policy": {
"what": "Вопросы о правилах: можно ли вернуть, в какой срок, на каких условиях",
"not_for": "Статус уже оформленного возврата",
"examples": ["Можно ли вернуть обувь после примерки?", "Сколько дней на возврат?"]
},
"return_status": {
"what": "Статус уже отправленного возврата и сроки получения денег",
"not_for": "Общие правила возврата",
"examples": ["Где мой возврат?", "Посылка дошла, когда вернёте деньги?"]
},
"other": null
}
}
На обращение «Отправил товар обратно неделю назад, трек показывает, что посылка у вас. Когда вернутся деньги?» ответ — return_status с уверенностью 1,0.
Так же можно структурировать instructions, уровни score и criteria у noul. Примеры помогают, только когда похожи на ваши реальные данные.
Рост confidence после правки формулировок ещё не значит, что ответы стали правильнее. Проверяйте изменения на примерах с известным правильным ответом.
Памятка
Формулировки
- Один вопрос — одно суждение.
- Пишите буквально: точное условие, граничные случаи — в
criteria. - Избегайте двойных отрицаний и вопросов «про свойство свойства».
- Следите, чтобы
instructionsиcriteriaне противоречили друг другу.
State
- Объект с именованными полями вместо одной длинной строки.
- Только то, что нужно для решения.
- В вопросах — пути к полям в обратных кавычках; для длинных однотипных списков дублируйте элемент в вопросе.
Запросы
- Все вопросы к одному
state— одним запросом. - Спрашивайте с запасом, лишнее игнорируйте в коде.
- Пороги уверенности подбирайте на своих данных.