Тестирование query rewrite: как не потерять смысл запроса
Тестирование query rewrite помогает понять, когда переписанный запрос улучшает выдачу, а когда уводит смысл. Разберем метрики, тесты и ошибки.

Где rewrite помогает, а где мешает
Rewrite полезен, когда убирает шум, а не меняет задачу. Если пользователь пишет "айфон 15 про мах", система может спокойно исправить "мах" на "max". Смысл не меняется. Но если она перепишет "айфон 15" в "айфон 15 Pro", это уже не помощь, а подмена намерения.
Лучше всего это видно на коротких запросах. В двух-трех словах мало контекста, поэтому модель начинает додумывать. Запрос "кредит" может означать условия, калькулятор, рефинансирование или жалобу на отказ. Чем короче фраза, тем осторожнее нужно переписывание.
С длинными запросами проще, но риск все равно остается. Если человек пишет "как уменьшить задержку у OpenAI-совместимого API в Казахстане", rewrite и правда может помочь: убрать лишние слова, привести фразу к форме для поиска, сохранить регион и тип API. Ошибка начинается там, где модель сокращает слишком агрессивно и выкидывает "в Казахстане" или "OpenAI-совместимого". Тогда поиск найдет больше документов, но не те.
Рост кликов сам по себе тоже ничего не доказывает. Пользователь может чаще кликать просто потому, что выдача стала шире или спорнее. Он открывает три результата вместо одного, тратит больше времени и все равно не получает ответ. Поэтому оценивать переписывание запроса лучше по тому, быстрее ли человек нашел нужный материал и стало ли у него меньше лишних переходов.
Самый опасный сбой выглядит почти разумно. Модель добавляет смысл, который "похож" на исходный. В поиске по каталогу моделей это случается постоянно. Запрос "дешевая модель для суммаризации" rewrite легко превращает в "лучшая модель для суммаризации". Одно слово меняет все. "Дешевая" задает ограничение по цене, а "лучшая" уводит поиск в сторону качества.
Здесь работает простое правило:
- смело исправляйте опечатки, раскладку и явный мусор;
- осторожно трогайте короткие запросы;
- не добавляйте новые ограничения, объекты и цели;
- не считайте рост кликов признаком того, что релевантность выросла.
Хороший rewrite побеждает не там, где запрос звучит умнее. Он побеждает там, где поиск сохраняет исходный смысл и быстрее приводит человека к ответу.
Что считать хорошим переписыванием
Хорошее переписывание не делает запрос красивее. Оно сохраняет намерение пользователя и помогает поиску поднять нужные документы выше. Если смысл сдвинулся хотя бы немного, rewrite уже сомнителен, даже если выдача выглядит более полной.
Простой пример: человек пишет "лимит на переводы ИП". Нормальный rewrite может аккуратно раскрыть сокращение и форму слова, например превратить запрос в вариант про лимиты переводов для индивидуального предпринимателя. Плохой rewrite уводит в сторону и делает из него общий поиск по тарифам для бизнеса. Слов стало больше, а точность упала.
Хороший результат виден не по самому тексту rewrite, а по выдаче. После переписывания наверху должны оказаться документы, которые отвечают на исходный вопрос быстрее и точнее. При этом система не должна прятать редкие, но точные результаты: страницу с конкретным кодом ошибки, номером приказа или названием внутреннего продукта.
Ошибки особенно часто появляются там, где запрос короткий, но очень точный. Коды ошибок, артикулы, названия моделей, аббревиатуры, номера договоров и редкие термины лучше не трогать без сильной причины. Один лишний синоним может испортить весь поиск.
Для оценки полезно держать в голове несколько простых признаков. Смысл после rewrite должен остаться тем же. Нужные документы должны подняться выше, а не просто перемешаться. Точные редкие совпадения не должны пропасть с первой страницы. И еще один важный признак: система должна уметь оставить запрос как есть, если переписывание не нужно.
Случаи без изменений обязательно стоит включать в тест. Это не пустая категория. Если пользователь уже написал запрос точно, лучший rewrite - не делать ничего.
В офлайн тестах полезно помечать каждый пример так: rewrite помог, не повлиял или навредил. Такая разметка быстро показывает, где система реально улучшает поиск, а где просто переписывает ради самого факта переписывания.
Как собрать набор запросов для проверки
Тестовый набор должен быть похож на живой поток поиска. Если взять только понятные запросы, переписывание почти всегда покажется полезным. Ошибки обычно прячутся в коротких, редких и шумных формулировках.
Собирайте запросы из нескольких групп. Частотные покажут, помогает ли rewrite там, где пользователи ищут каждый день. Редкие и длинные нужны для хвостовых случаев. Отдельно вынесите проблемные формулировки: одно слово, двусмысленный смысл, смесь русского и английского, внутренний жаргон, номера продуктов, сокращения.
Стоит добавить и "грязные" примеры. Пользователи пишут с опечатками, меняют порядок слов и используют синонимы, которые команда сама бы не придумала. Для банка это может быть "кредит без справки" и "займ без подтверждения дохода". Для телеком-сервиса - "роуминг турция" и "связь за границей в турции". Если rewrite умеет исправлять такие вещи, это видно быстро.
Что хранить для каждого запроса
Одной строки текста мало. Для каждого запроса лучше сохранить:
- исходный текст без правок;
- короткую пометку, что пользователь, скорее всего, хотел найти;
- 1-3 ожидаемых документа или категории результатов;
- пометку "rewrite можно" или "rewrite нельзя";
- причину, если формулировку нельзя менять.
Последний пункт часто спасает от ложных побед. Есть запросы, где даже мягкое переписывание ломает смысл. Это названия законов, артикулы, номера тарифов, коды ошибок, имена компаний, точные модели и фразы из внутренних регламентов. Запрос "deepseek v3.2" нельзя бездумно превращать в "последняя модель deepseek", а "приказ 102" не стоит менять на общий запрос про нормативные акты.
Не затирайте исходник переписанной версией. Храните обе строки рядом. Иначе потом трудно понять, rewrite реально помог или просто подменил задачу на более легкую.
Для первого цикла обычно хватает 150-300 запросов, если они хорошо подобраны. Лучше маленький, но честный набор, чем большая таблица из однотипных формулировок. Если поиск работает в нескольких доменах, например по базе знаний, товарам и внутренним документам, распределите запросы между ними вручную. Тогда тест покажет не среднюю температуру, а конкретные слабые места.
Как провести тест шаг за шагом
Тестирование query rewrite легко испортить, если менять сразу несколько вещей. Для честной проверки зафиксируйте индекс, ранжирование, фильтры, период данных и размер выдачи. Меняться должен только rewrite. Тогда вы увидите его реальный эффект, а не шум от соседних изменений.
- Сначала снимите базовую выдачу без rewrite. Прогоните один и тот же набор запросов и сохраните top-10 или top-20 документов по каждому.
- Потом запустите тот же набор с rewrite. Используйте те же параметры поиска, тот же индекс и тот же лимит результатов.
- Сравните результаты по каждому запросу, а не только средний score по всему набору. Отметьте, где нужный документ поднялся выше, где остался на месте, а где исчез.
- Провалы разберите вручную. Цифры показывают общую картину, но смысловые ошибки почти всегда заметны именно глазами.
На практике удобно держать простую таблицу: исходный запрос, переписанный запрос, позиции релевантного документа до и после, короткий комментарий. Уже через 30-50 примеров начинают повторяться одни и те же поломки.
Хороший тест смотрит не только на рост метрик, но и на сохранение смысла. Например, пользователь ищет "кредит для ИП без залога", а rewrite превращает запрос в "кредит для бизнеса". Формально текст стал чище и, может быть, даже популярнее. По факту система потеряла ограничение "без залога" и подняла не те документы.
При ручном разборе удобно ставить короткие метки:
- потеря намерения пользователя;
- лишнее расширение запроса;
- подмена термина или сущности;
- удаление важного ограничения;
- косметическое переписывание без пользы.
Если запросов много, не пытайтесь читать все подряд. Сначала возьмите случаи, где релевантный документ сильно просел или пропал из выдачи. Потом посмотрите запросы, где rewrite дал заметный рост. Так проще понять обе стороны: где переписывание помогает, а где уводит смысл.
Какие метрики смотреть вместе
Одна метрика редко дает честную картину. Переписывание запроса может поднять одну цифру и тихо сломать другую. Поэтому лучше смотреть на набор сигналов, которые ловят разные типы ошибок.
Recall@k отвечает на простой вопрос: попал ли нужный документ в верхние k результатов. Если до rewrite ответ не находился, а после стал появляться хотя бы в топ-10, это уже плюс. В офлайн тестах recall особенно полезен на редких и длинных запросах, где система часто промахивается полностью.
NDCG@k показывает уже не сам факт находки, а порядок результатов. Документ в топ-1 и тот же документ на девятом месте дают разный пользовательский опыт. Если recall@10 вырос, а NDCG@10 почти не изменился, rewrite, скорее всего, расширил запрос, но не помог поднять нужный ответ выше.
Доля нулевой выдачи ловит сломанные запросы. Она быстро показывает, что rewrite удалил важный термин, испортил фильтр или сделал запрос слишком узким. Но эту метрику легко "улучшить" плохим способом. Если переписывание делает запрос более общим, пустых результатов станет меньше, а релевантность просядет.
Поэтому рядом нужна метрика дрейфа смысла. Дрейфом стоит считать случаи, когда rewrite меняет намерение, сущность, ограничение или время. Запрос "тариф для ИП без абонплаты" нельзя без потерь превращать в "тарифы для бизнеса". Система найдет больше документов, recall вырастет, нулевая выдача упадет, но исходный смысл уже сместился.
Дрейф можно считать вручную на небольшой выборке или через judge-модель, но спорные случаи лучше проверять руками. Больше всего ошибок обычно дают числа, отрицания, даты, названия ролей и короткие запросы из двух-трех слов.
Нормальная картина выглядит так:
- recall@k растет или хотя бы не падает;
- NDCG@k растет вместе с ним;
- доля нулевой выдачи снижается без скачка дрейфа смысла;
- дрейф остается низким на запросах с числами, датами и точными ограничениями.
Если одна метрика пошла вверх, а две другие просели, rewrite рано выпускать. Самый неприятный случай выглядит даже красиво на графике: пустых результатов меньше, найденных документов больше, а пользователь читает ответ уже не на свой вопрос.
Один живой сценарий
Возьмем базу знаний банка. Пользователь пишет: "перевыпуск карты после утери". Намерение здесь довольно узкое. Человек потерял карту и хочет понять, как выпустить новую, сколько это займет и что делать первым делом.
Rewrite меняет запрос на "восстановить доступ к карте". На первый взгляд разница небольшая. По смыслу это уже другой запрос. Слова "доступ" и "восстановить" часто тянут поиск в сторону входа в приложение, PIN-кода, разблокировки и проверки личности.
После такого rewrite поиск начинает поднимать не те документы. В топе оказываются статьи вроде "Как восстановить PIN", "Что делать при блокировке карты" или "Как снова войти в мобильный банк". Они могут быть полезны в соседних случаях, но не решают исходную задачу.
Ручная оценка ловит такую подмену очень быстро, если смотреть не только на похожие слова, но и на цель пользователя. Для проверки обычно хватает трех действий:
- показать рядом исходный запрос и его rewrite;
- открыть top-5 результатов до и после переписывания;
- попросить оценщика выбрать, какая выдача лучше решает исходную задачу.
Отдельно стоит помечать, не появился ли в rewrite новый смысл, которого в запросе не было. В этом примере ошибка заметна сразу. Статья про блокировку карты может быть уместна как дополнительный шаг, потому что потерянную карту часто нужно срочно заблокировать. Но если такие материалы вытесняют инструкцию по перевыпуску, rewrite уже навредил.
Удобно ставить простую оценку: 2 балла, если результат ведет прямо к перевыпуску; 1 балл, если помогает частично; 0 баллов, если уводит в PIN, доступ или другие соседние темы. Тогда подмена смысла видна не на уровне впечатления, а в карточке оценки.
Такие кейсы хорошо отрезвляют. Обычная текстовая близость еще не означает, что rewrite сработал. Если он меняет намерение пользователя, офлайн метрики могут выглядеть терпимо, а в живом продукте человек просто не найдет ответ.
Где чаще всего ошибаются
Самая частая ошибка в тестировании query rewrite - брать только частотные запросы. На них система и так часто справляется неплохо, поэтому метрики растут даже тогда, когда rewrite портит редкие и сложные формулировки. Команда видит красивый отчет, а пользователи с длинными или нечеткими запросами получают хуже результат.
Это особенно заметно в запросах с узким смыслом. Пользователь пишет "кредит для ИП без залога", а rewrite упрощает это до "кредит для бизнеса". CTR может не упасть: люди все равно кликают по верхним документам. Но смысл уже съехал, и поиск отвечает на другой вопрос.
Вторая частая ошибка - смотреть только на CTR, будто он сам все объясняет. CTR полезен, но он не показывает, сохранился ли смысл, стала ли выдача точнее и не исчезли ли нужные ограничения. Если rewrite делает запрос шире, кликов иногда даже больше, потому что выдача выглядит более общей. Для поиска это ловушка, а не успех.
Обычно ломается сразу несколько вещей:
- пропадают важные уточнения: регион, время, цена, роль пользователя;
- редкий термин заменяется на более популярный, но менее точный;
- аббревиатура раскрывается неверно;
- запрос переписывается слишком смело и меняет намерение человека.
Еще одна типовая проблема - смешивать rewrite и ранжирование в одном тесте. Если вы одновременно поменяли переписывание запроса, формулу ранжирования и набор источников, вы уже не поймете, что дало рост или просадку. Такой тест почти бесполезен. Сначала проверяют rewrite на одном и том же индексе и с одним и тем же ранжированием, и только потом двигают следующий слой.
Многие команды забывают хранить плохие примеры. Это дорого обходится. Один и тот же провал возвращается через месяц после нового релиза, потому что его никто не закрепил в наборе проверок. Нужен не только список удачных запросов, но и архив ошибок: исходный текст, версия rewrite, почему это плохо и какой документ должен был найтись.
Небольшая база провалов часто полезнее, чем еще тысяча легких запросов. Она быстро показывает, теряете ли вы смысл. В этом и проходит граница между rewrite, который помогает поиску, и rewrite, который просто делает запросы удобнее для отчета.
Чек-лист перед запуском
Переписывание запроса легко выглядит умнее, чем есть на деле. Если у вас нет понятной базы для сравнения, rewrite может поднять одни метрики и тихо сломать смысл в важных сценариях.
Перед запуском проверьте пять вещей:
- сохраните базовую версию поиска без rewrite;
- подготовьте небольшой, но живой набор запросов с ожидаемыми результатами;
- заранее решите, когда rewrite надо пропускать;
- отправьте спорные случаи на ручную проверку;
- зафиксируйте порог отката до релиза.
Обычно rewrite стоит реже применять к точным и навигационным запросам: названиям, артикулам, номерам договоров, именам людей и фразам в кавычках. Информационные запросы можно переписывать смелее.
Иначе запрос "приказ 37" внезапно превращается в "внутренние нормативные документы", а релевантность падает, хотя слов стало больше. Формально rewrite сработал, по смыслу - нет.
Ручная проверка не требует сотен примеров. Часто хватает 30-50 спорных случаев, где офлайн тесты расходятся с ожиданием. Смотрите на две вещи: что именно модель изменила и помогло ли это найти нужный ответ быстрее.
Если вы используете несколько моделей или промптов для оценки переписывания, не меняйте все сразу. Сначала проверьте само правило rewrite, потом трогайте модель и настройки. Так проще понять, что дало прирост, а что уводит запрос в сторону.
Что делать после пилота
Пилот редко дает один общий ответ для всего трафика. Обычно rewrite помогает только части запросов. Если он стабильно поднимает качество для коротких, разговорных или слишком расплывчатых формулировок, оставьте его именно там. А для точных запросов с артикулами, именами компаний, кодами услуг и цитатами лучше сохранить исходный текст.
После пилота работа не заканчивается. Теперь у вас есть первые данные, и на их основе можно включать переписывание не везде, а только там, где оно приносит пользу без потери смысла.
По каждому классу запросов удобно принять одно из четырех решений:
- включить rewrite по умолчанию;
- включать его только при слабом результате базового поиска;
- не менять исходный запрос;
- отправить класс запросов на доработку правил или промпта.
Отдельно собирайте журнал ошибок, где переписывание меняет смысл. Он окупается очень быстро. Для каждой записи хватит четырех строк: исходный запрос, переписанный вариант, что именно потерялось или добавилось, и какой документ поиск показал первым.
Такой журнал помогает заметить повторяющиеся сбои. Например, пользователь пишет "кредит без залога для ИП", а модель сжимает запрос до "бизнес-кредит". Текст стал короче и чище, но важное условие исчезло. Подобные примеры стоит держать в постоянном regression-наборе и прогонять через него каждую новую версию rewrite.
Если вы сравниваете несколько LLM для rewrite, важно быстро менять модель и не переписывать интеграцию каждый раз. Для таких прогонов может подойти AI Router на airouter.kz: это OpenRouter-совместимый API-шлюз, где можно переключать модели через один OpenAI-совместимый эндпоинт и оставить прежние SDK, код и промпты. В контексте оценки это просто удобный способ гонять один и тот же набор запросов через разные модели без лишней обвязки.
Хороший итог пилота выглядит просто: у вас есть список классов запросов, где rewrite уже можно включать, отдельный журнал смысловых ошибок и набор тестов для следующего прогона. Тогда rewrite не превращается в "магию" внутри поиска. Он остается обычной частью системы, которую команда проверяет и держит под контролем.