Как задавать вопросы Jev

Три типа вопросов - noul, choice, score - структура state и приёмы, от которых зависит качество ответов

Как задавать вопросы Jev

Запрос к Jev состоит из двух частей: state - что оцениваем, и questions - что хотим узнать. Качество ответов почти целиком определяется тем, как сформулированы вопросы. Если вы ещё не отправляли запросов, начните с первого запроса.

Один вопрос: одно суждение

Задавайте вопросы, на которые знающий человек ответит за секунду, взглянув на текст.

ПлохоХорошо
«Проанализируй обращение и реши, что с ним делать»«Клиент просит вернуть деньги?»
«Оцени резюме кандидата»Отдельно: глубина опыта в Python, опыт руководства, опыт проектирования систем
«Это спам?»Отдельно: есть ли ссылка на сторонний сайт, обещают ли лёгкий заработок, обращаются ли по имени

Если решение зависит от нескольких факторов, спросите про каждый отдельно и сложите ответы в коде со своими весами. Изменились приоритеты - меняете коэффициент, а не переписываете промпт. Лишних запросов это не добавляет: все вопросы к одному state уходят одним запросом и обрабатываются параллельно.

Из чего состоит вопрос

"refund_requested": {
  "type": "noul",
  "instructions": "Клиент просит вернуть деньги?"
}
  • Имя вопроса (refund_requested) - для вашего кода: под ним придёт ответ. Модель его не видит, поэтому весь смысл должен быть в instructions.
  • type: noul, choice или score.
  • instructions: сам вопрос. Пишите точно и буквально: Jev отвечает на то, что написано, а не на то, что вы имели в виду.
  • criteria - варианты ответа: у choice обязательны, у score обязательны, у noul - по желанию.

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 с вариантом «не указано»

Что класть в state

state - это материал для оценки: строка, объект или массив. Только текст: изображения, аудио и видео не поддерживаются.

ФорматКогда удобенПример
СтрокаОдно сообщение или абзац"С меня дважды списали деньги."
ОбъектНесколько связанных частей с именами{"message": "...", "policy": "..."}
МассивПоследовательность сообщений или записей["Здравствуйте", "Номер заказа A-104", "..."]

В большинстве случаев лучше объект: у каждой части есть имя, и в вопросе на неё можно сослаться.

Кладите только то, что нужно для решения. Лишний текст отвлекает модель и снижает точность, отфильтруйте данные в коде до запроса. Актуальные факты (правила возврата, остатки, статус заказа) передавайте в state, а не рассчитывайте на знания модели.

Ссылки на части state

Если state - объект, укажите в вопросе путь к нужной части в обратных кавычках:

{
  "model": "typesafe/jev",
  "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,97.

Много вопросов одним запросом

Все вопросы к одному state отправляйте вместе. Они обрабатываются параллельно и независимо: время ответа почти не растёт, а ответ на один вопрос не влияет на другой.

Спрашивайте и то, что может не понадобиться. Если обращение окажется не про баг, просто не читайте ответ про критичность бага. Лишний вопрос стоит несколько токенов, а отдельный запрос - ещё раз весь state и служебные токены.

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

Предел: общий бюджет запроса, см. ограничения.

Когда варианты путаются: структурные описания

Начинайте с коротких строк. Если модель стабильно путает два близких варианта, опишите каждый объектом: что входит, что не входит, примеры. Имена полей вы придумываете сами, модель видит их вместе со значениями, так что называйте понятно и одинаково у всех вариантов.

"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 после правки формулировок ещё не значит, что ответы стали правильнее. Проверяйте изменения на примерах с известным правильным ответом.

Памятка