Точность отвечает не на тот вопрос
Точность отвечает на вопрос «как часто модель права». Прод ломается по другим поводам: модель уверенно ошибается там, где её уверенность взяли как сигнал; отказывается от безобидного вопроса; выдаёт JSON, который парсер не съедает; отвечает верно на формулировку из теста и мимо — на ту же мысль, словами пользователя. В доле правильных ответов ни одна из этих бед не видна: доля усредняет разные режимы отказа в одну цифру и не подсказывает, что чинить.
Если задача сводится к классификации, обычные precision и recall по классам никто не отменял. Но генеративный вывод ими не покроешь, а именно он — большинство прикладных сценариев с LLM. Отсюда множественное число в «метриках качества»: их нужно несколько, потому что ломается по-разному.
И сразу наш рабочий фильтр, он пригодится ниже: метрика существует, чтобы ответить «стало ли лучше после конкретного изменения». Число, из которого не следует решение, — украшение.
Тупик: погоня за одним числом
Первое, что пробуют, — найти один балл, описывающий качество. Кандидатов два, и оба соблазняют отсутствием разметки.
Перплексия. Считается без разметки: прогнал модель по тексту — получил число. Но перплексия меряет, насколько модель предсказывает распределение текста вообще, а ваша задача — про ответ на конкретный вопрос по конкретным документам. Дообученная на ваших диалогах модель закономерно ухудшает перплексию на «текстах вообще» и улучшает ответы. Связь между числом и полезностью есть, пока задача похожа на «предсказать следующее слово», и рвётся, как только появляется retrieval, постобработка или жёсткий формат вывода.
Публичный бенчмарк. Тоже одно число, плюс можно сравнивать модели между собой. Но бенчмарк меряет распределение чужих вопросов, а ваш трафик — другой: другие формулировки, другая длина, другой уровень пользователя. Бенчмарк пригоден как грубый фильтр при выборе модели из кандидатов и непригоден как метрика вашей системы.
Второй заход в тот же тупик — доля правильных на маленькой размеченной выборке. Против неё арифметика: чтобы отличить два варианта системы, разница между ними должна быть больше разброса, который вносит случайность — неоднозначные вопросы, дрожание сэмплинга, разногласия разметчиков. На маленькой выборке разброс почти всегда больше разницы, цифра ходит туда-сюда по причинам, не связанным с качеством, а решения по ней всё равно принимают.
Итог тупика: одна цифра не работает не потому, что её трудно получить, а потому, что она смешивает разные режимы отказа. Чинить нужно конкретный режим — значит, и мерить надо режимы по отдельности.
Что мерить и как
У каждой метрики ниже — процедура руками и условие, при котором она нужна.
Калибровка: совпадает ли уверенность с правотой
Для каждого ответа зафиксируйте уверенность модели: logprobs, если API их отдаёт, словесную оценку уверенности, если не отдаёт, или устойчивость как замену (следующий пункт). Разложите ответы по корзинам уверенности — пороги корзин ваши. В каждой корзине сравните заявленную уверенность с фактической долей правильных ответов. Расхождение по корзинам и есть мера: если там, где модель говорит «почти наверняка», правильных ответов ощутимо меньше, уверенность врёт системно.
Зачем это руками: уверенность — главный кандидат на сигнал «когда подстраховаться человеком или другой моделью». Некалиброванная уверенность делает этот сигнал бесполезным, и узнаётся это только измерением.
Когда нужно: если уверенность участвует в решениях — эскалация на человека, отказ от ответа, маршрутизация между моделями. Если ответ всегда уходит дальше независимо ни от чего — калибровка подождёт.
Устойчивость: один вопрос, разные формулировки
Возьмите вопросы из реальных логов, не из головы, и к каждому напишите несколько переформулировок, сохраняющих смысл — сколько сможете, не вылизывая. Прогоните все варианты и посчитайте, как часто ответы совпадают между собой и с ответом на оригинал. Расхождение на переформулировке при верном оригинале — будущий продовый сбой: пользователь сформулирует именно так.
Когда нужно: вход свободной формой от людей — обязательно. Жёсткий шаблон внутри системы — почти не нужно, переформулировок там не бывает.
Формат: съедает ли парсер
Если вывод модели уходит в код, метрика тривиальна: прогоняйте ответы через тот же парсер, что работает в проде, и считайте отказы. Ловушка одна — проверять «аккуратным» тестовым парсером, который прощает лишнюю запятую. Мерить надо продовым парсером, иначе тест врёт в вашу пользу.
Когда нужно: всегда, когда вывод машинный. Это самая дешёвая метрика из перечисленных, и ловит она сбои раньше пользователя.
Отказы: в обе стороны
Соберите два набора вопросов: на которые отвечать можно и на которые нельзя. «Нельзя» — это запретные темы и, например, вопросы вне зоны ответственности системы, где честный ответ «я этим не занимаюсь». Прогоните оба набора и ведите два числа отдельно: ложные отказы и пропущенные отказы. Отдельно — потому что лечение торгуется: ужесточение фильтров уменьшает пропуски и наращивает ложные отказы, а одна сумма эту торговлю прячет.
Когда нужно: пользовательские продукты с политикой. Внутреннему инструменту в узкой предметной области обычно важен только ложный отказ.
Опора на источник: для систем с retrieval
Если модель отвечает по найденным фрагментам, мерить надо не ответ целиком, а утверждения внутри него. Процедура: выборка продовых ответов, из каждого выписываете проверяемые утверждения и проверяете каждое по фрагментам, которые модель видела при ответе. Метрика — доля утверждений с опорой. Считать по утверждениям важно: ответ с выдуманными утверждениями внутри всё равно проходит как «частично правильный», и оценка целыми ответами этого не видит.
Когда нужно: любая система, где модель обязана опираться на ваши документы. Разметка дорогая, поэтому выборка — но из продовых ответов, а не из удобных примеров.
Задержка и стоимость: хвост, а не среднее
Логируйте по каждому запросу длину входа и вывода и время ответа, квантили считайте сами — верхняя часть распределения, а не среднее: пользователь чувствует именно хвост. Стоимость складывается из длин: токены входа и вывода на вашу цену дают стоимость ответа, на объём трафика — стоимость владения. Общих чисел тут быть не может, они у каждого свои.
Когда нужно: прод с трафиком или с бюджетом на задержку. Прототипу — не нужно.
Регрессии: одна сетка на все изменения
Заморозьте набор вопросов с эталонами и прогоняйте его после каждого изменения системы: правка промпта, смена модели, пересборка индекса, другая температура — всё это релизы. Сетку смотрите по режимам отказа, а не по общему баллу: общий балл — та самая одна цифра из тупика. Для агентов единица измерения другая — не ответ, а траектория; про оценку агента до релиза у нас отдельный материал.
Как собрать из этого процедуру
Начните с самого дешёвого измерения, которое ловит сбой, уже случившийся или самый дорогой по последствиям из ожидаемых. Опишите каждую метрику так, чтобы она считалась скриптом, без ручного вглядывания: ручная разметка годится для настройки методики, но не для регулярного прогона. Рядом с каждой метрикой запишите решение, которое она кормит; решений нет — вычёркивайте. Эталонный набор держите настолько маленьким, чтобы прогон занимал минуты: сетка, которую гоняют раз в квартал, регрессии не ловит, она их констатирует.
Про разметку: правила для спорных случаев пишите до того, как они возникнут, а то, что осталось спорным, выносите в отдельную корзину «неоднозначно» — вместе с вопросами, у которых правильных ответов несколько. Заставлять разметку быть однозначной там, где задача неоднозначна, — способ получить шумные метки и поверить в них.
Позиция редакции
Мы считаем, что для прикладной системы на LLM калибровка, устойчивость и формат говорят о готовности к продакшену больше, чем очередная итерация замера точности. Точность на собственном наборе рано или поздно упирается в потолок различимости — варианты системы перестают отличаться на вашей выборке, — а прод продолжает ломаться: уверенные ошибки, переформулировки, отказы, битый формат. Источник инцидентов — эти режимы, и мерить нужно их.
Мы ошибаемся, если ваша система устроена иначе: узкий шаблонный вход, детерминированная постобработка, которая ловит битый формат и переспрашивает, дешёвые и заметные ошибки, низкие ставки. Тогда доли правильных на узком наборе действительно достаточно, а полный набор метрик — накладные расходы без отдачи.
С чего начать, если непонятно: возьмите сбой из логов, который уже случился, и постройте измерение, поймавшее бы его до релиза. И открытый вопрос, честного ответа на который у нас нет: как дёшево мерить то, что не размечается скриптом, — уместность тона, полезность длинного рассуждения. Пока тут помогает только выборка и живой человек.