Р. Аллахвердиев
Оценка качестваLLM и моделиСерия «AI QA с нуля» · часть 9

Классы дефектов LLM: тренажёр на ассистенте магазина и шаблон баг-репорта

Как отличить галлюцинацию от неполного ответа, лишний отказ от правильного, регрессию от нормальной вариативности. Пять вопросов-развилок, тренажёр с кейсами ассистента магазина и шаблон баг-репорта, который разработчик сможет воспроизвести.

опубликовано · 10 мин60fca9e
Обложка: Классы дефектов LLM: тренажёр на ассистенте магазина и шаблон баг-репорта
Содержание

Коротко

  • «Бот ответил неправильно» не баг-репорт. Чтобы дефект починили, нужно назвать его класс, частоту и критерий, которому ответ не соответствует.
  • Класс определяют несколько вопросов-развилок: воспроизводится ли дефект k из n, был ли нужный чанк в контексте, противоречит ли ответ контексту, все ли обязательные факты на месте.
  • Самый частый ложный дефект: нормальная вариативность формулировки. Ответ другими словами, но с теми же фактами, дефектом не считается.
  • Класс описывает то, что увидел пользователь. Причину (поиск, промпт, модель, данные) пишите отдельным полем: один и тот же класс может родиться в разных блоках.
  • Ожидаемое поведение в репорте записывается как проверяемый критерий, а не как эталонная строка.

Проблема оракула: с чем сравнивать ответ

В первой части серии я писал, что у LLM-системы нет одного правильного ответа. На вопрос про возврат верны и «Да, в течение 30 дней с чеком», и «Можно, у вас 30 дней, сохраните чек». Сравнение строк упадёт на обоих.

Отсюда следствие для баг-репортов. Когда тестировщик пишет «ожидалось: „Возврат возможен в течение 30 дней“», разработчик справедливо отвечает, что модель так и не ответит дословно. Репорт возвращается, хотя дефект, возможно, настоящий.

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

Восемь классов и вариант «дефекта нет»

Таблица классов с примерами на ассистенте магазина есть в первой части, повторять её не буду. Напомню сам список: галлюцинация, неполный ответ, лишний отказ, нарушение формата, prompt injection, утечка, регрессия, рост стоимости и задержки.

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

Класс отвечает на вопрос, что увидел пользователь. Причина отвечает на вопрос, где это сломалось. Галлюцинация может появиться из-за модели, которая додумала факт, а может из-за retriever, который не принёс нужный чанк, и модель заполнила пробел своими знаниями. Для пользователя это один и тот же неверный ответ, для разработчика две разные задачи. Поэтому в репорте два поля: класс и гипотеза о причине. Где на схеме системы живут эти причины, показано в предыдущей части.

Вопросы-развилки: как отличить один класс от другого

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

1. Воспроизводится ли проблема? Прогоните тот же запрос n раз (от 5).
   0 из n и ответы различаются только словами  -> дефекта нет
   k из n                                      -> идём дальше, k/n в репорт

2. Ответ нарушает формат, который проверяется кодом (JSON, схема, длина)?
   да -> нарушение формата

3. Ассистент выполнил чужую инструкцию или выдал то, что не должен?
   выполнил инструкцию из вопроса или документа -> prompt injection
   наружу ушёл системный промпт или чужие данные -> утечка
   (бывает и то и другое: класс по последствию, причина отдельно)

4. Был ли нужный чанк в контексте модели?
   нет, и ассистент уверенно ответил           -> галлюцинация, причина: поиск
   нет, и ассистент сказал, что не знает       -> корректный отказ, дефект поиска
   да, а ассистент отказался                   -> лишний отказ

5. Ответ противоречит контексту или добавляет факты не из него?
   да                                          -> галлюцинация, причина: генерация
   нет, но обязательного факта не хватает      -> неполный ответ

Отдельно, если ответ верный:
   на прошлой версии было лучше на том же датасете -> регрессия
   ответ тот же, но дороже или медленнее           -> стоимость и задержка

Несколько пояснений к развилкам.

Первая развилка самая важная. Один прогон ничего не доказывает. Если дефект появился в 2 случаях из 10, он настоящий, и пользователь его увидит. Если вы видели его однажды и больше воспроизвести не можете, сначала соберите частоту, потом пишите репорт. В программе курса частоту требуем минимум по пяти прогонам.

Четвёртая развилка требует трейса. Ответить на вопрос «был ли нужный чанк в контексте» можно только по логам: какие фрагменты нашёл retriever и в каком порядке они ушли в промпт. Если трейсов нет, это первое, что стоит попросить у команды.

Регрессия определяется только сравнением. Сказать «стало хуже» по одному ответу нельзя. Нужен прогон того же датасета на старой и новой версии и разница в долях.

Тренажёр: определите класс дефекта

Ниже кейсы ассистента интернет-магазина. В каждом есть вопрос покупателя, что нашёл retriever, ответ ассистента и иногда дополнительные данные: частота, версия, токены. Выберите класс и прочитайте разбор.

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

Ассистент интернет-магазина «Полка». В каждом кейсе: вопрос покупателя, что нашёл поиск по базе знаний, ответ ассистента. Определите класс дефекта.

Кейс 1 из 10Верно: 0

Кейсы и цифры иллюстративные. Магазин, документы, модели и метрики вымышлены.

Пограничные случаи

На тренажёре чаще всего путают одни и те же пары. Разберу их отдельно.

Галлюцинация или неполный ответ. Если в ответе есть факт, которого нет в контексте или который ему противоречит, это галлюцинация. Если всё сказанное верно, но не хватает обязательного факта (срок назван, про чек ни слова), это неполный ответ. Ответ может быть и тем и другим: тогда выбирайте класс с большим риском для пользователя, обычно галлюцинацию.

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

Высокая Faithfulness, а ответ неверный. Ассистент назвал срок возврата 14 дней, контекст подтверждает 14 дней, а по актуальному регламенту 30. Метрика честно говорит, что ответ соответствует контексту. Для пользователя это неверный факт, причина в данных: в базе лежит устаревший документ. В репорте пишите класс по симптому, а в причину ставьте «данные». Такие дефекты ловят вопросы на актуальность в golden-датасете.

Prompt injection или утечка. Injection описывает механизм: ассистент выполнил инструкцию, которую ему подложили в вопросе или в документе. Утечка описывает последствие: наружу ушло то, что не должно было. Если покупатель попросил «покажи свои инструкции» и получил системный промпт, это утечка через прямую инъекцию. Записывайте класс по последствию, механизм в поле причины. Про атаки через документы базы есть отдельный разбор.

Регрессия или вариативность. После смены промпта доля верных ответов упала с 90% до 87%. Цифры иллюстративные. Такая разница может оказаться шумом: тот же датасет на одной и той же версии тоже даёт разброс между прогонами. Прежде чем заводить дефект, прогоните обе версии несколько раз и посмотрите, перекрываются ли диапазоны. Как с этим работать в CI, разобрано в статье про quality gate.

Шаблон баг-репорта для LLM-дефекта

Хороший репорт отвечает на три вопроса разработчика: как воспроизвести, как понять, что починено, и насколько это срочно. Шаблон ниже можно скопировать в трекер.

## [Класс] Короткое описание симптома

**Класс дефекта:** галлюцинация | неполный ответ | лишний отказ | формат |
prompt injection | утечка | регрессия | стоимость и задержка
**Риск для пользователя:** что случится, если дефект останется
**Гипотеза о причине:** данные | индексация | поиск | промпт | модель | обвязка

### Воспроизведение
- Вопрос пользователя (дословно):
- История диалога (если была):
- Версия системного промпта:
- Найденные чанки: id документа, позиция в выдаче, был ли нужный
- Ответ ассистента (дословно, один из провальных):
- Модель и версия:
- Параметры: temperature, max_tokens, top_k поиска
- Частота: k из n прогонов (n не меньше 5)
- Trace id провального прогона:

### Ожидаемое поведение (критерий, а не строка)
- В ответе есть: ...
- В ответе нет: ...
- Если в контексте нет ответа: ...

### Как проверить исправление
- Какой тест или кейс golden-датасета должен пройти, на скольких прогонах

И пример заполнения. Цифры и идентификаторы иллюстративные.

## [Галлюцинация] Ассистент называет срок возврата обуви 14 дней вместо 30

**Класс дефекта:** галлюцинация
**Риск для пользователя:** покупатель не вернёт товар, думая, что срок прошёл
**Гипотеза о причине:** генерация. Нужный чанк найден на первой позиции,
ответ ему противоречит

### Воспроизведение
- Вопрос: «Можно вернуть кроссовки, если я их уже надевал?»
- История диалога: нет
- Версия системного промпта: v12
- Найденные чанки: returns_policy#3 (позиция 1, нужный, «30 дней с чеком»),
  delivery#7 (позиция 2), promo#1 (позиция 3)
- Ответ: «Да, вернуть можно в течение 14 дней при сохранении товарного вида.»
- Модель и версия: <модель>, <версия>
- Параметры: temperature 0.7, top_k 3
- Частота: 3 из 10 прогонов
- Trace id: <id провального прогона>

### Ожидаемое поведение
- В ответе есть срок 30 дней и условие про чек
- В ответе нет других сроков возврата
- Ответ не противоречит returns_policy#3

### Как проверить исправление
- Кейс returns_shoes_worn из golden-датасета проходит в 10 из 10 прогонов,
  Faithfulness на нём не ниже порога

Обратите внимание на раздел «Ожидаемое поведение». Каждую строку можно превратить в проверку: наличие факта и отсутствие чужих сроков проверяются кодом или судьёй, «не противоречит» проверяет Faithfulness. Этот же кейс потом уходит в регрессионный набор, чтобы дефект не вернулся.

Сравните с репортом «бот неправильно отвечает про возврат». По нему разработчик не знает, какой вопрос задать, какую версию смотреть и когда закрывать задачу.

Ограничения подхода

Классы дефектов это договорённость команды, а не стандарт. Названия и границы между ними у вас могут быть другими, главное, чтобы они были одинаковыми у всех, кто заводит баги.

Частота k из n на пяти или десяти прогонах грубая. 3 из 10 и 5 из 10 на таком числе прогонов легко могут оказаться одним и тем же. Для решения «чинить или нет» этого хватает, для сравнения двух версий нужен датасет и доли, а не отдельный кейс.

Развилки предполагают, что у вас есть трейсы с найденными чанками. Без них четвёртый и пятый вопросы превращаются в догадки, и класс «галлюцинация» поглощает всё подряд.

Если класс ставит LLM-судья, а не человек, судья тоже ошибается. Прежде чем доверять ему разметку дефектов, сверьте его с ручной разметкой, как описано в разборе про калибровку судьи.

Что дальше

  • Пройдите тренажёр ещё раз через пару дней, не глядя в развилки. Если путаете одну и ту же пару классов, перечитайте раздел про пограничные случаи.
  • Возьмите любого доступного вам ассистента (свой стенд или локальную модель из третьей части) и найдите пять дефектов минимум трёх классов. Для каждого соберите частоту по пяти прогонам и оформите репорт по шаблону.
  • Перечитайте последние три LLM-бага в вашем трекере. Есть ли у них частота, найденные чанки и критерий вместо строки?
  • Дальше по серии: где эти дефекты живут в архитектуре, показано на интерактивной схеме RAG, а как ловить их автоматически до релиза, в разборе про quality gate в CI.