Перейти к содержимому
6 мин чтения

Реранжирование русско-казахского поиска на парах запросов

Реранжирование русско-казахского поиска проверяют набором трудных пар: матрицей языков, разметкой релевантности, негативами и срезами ошибок.

Реранжирование русско-казахского поиска на парах запросов

Поиск по русским и казахским документам ломается не тогда, когда система совсем не понимает язык. Он ломается раньше: нужный документ находится в кандидатах, но реранжировщик ставит выше текст с совпавшими словами, неверной процедурой или удобным русским пересказом. Средняя метрика по всему корпусу почти всегда сглаживает этот провал.

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

Набор должен измерять порядок, а не сходство переводов

Реранжирование русско-казахского поиска оценивают на маленьких выдачах, где есть один или несколько действительно полезных документов и несколько опасно похожих конкурентов. Пара «русский запрос - его казахский перевод» измеряет семантическую близость. Пара «русский запрос - казахский документ, который решает задачу пользователя» измеряет поиск. Это разные тесты.

Например, пользователь пишет: «как оформить отпуск без сохранения зарплаты». В документе на казахском может быть нужная процедура с условиями, заявлением и исключениями. Рядом должны лежать правдоподобные, но неверные документы: об ежегодном оплачиваемом отпуске, о больничном и о командировке. Если положить рядом случайные материалы, почти любой современный реранжировщик покажет приличный результат, потому что задача станет слишком легкой.

Отдельно держите в голове различие между тремя свойствами:

  • Переводная эквивалентность. Два фрагмента говорят почти одно и то же на разных языках.
  • Поисковая релевантность. Документ помогает выполнить намерение запроса, хотя формулировки могут сильно различаться.
  • Предпочтение в ранжировании. Среди нескольких полезных текстов система ставит первым тот, который точнее отвечает на вопрос и не ведет пользователя к ошибочному действию.

Команды часто смешивают эти свойства. Затем они получают высокий score на двуязычных параллельных предложениях и удивляются, почему RAG отвечает по старой редакции регламента. Модель могла хорошо сопоставить «өтініш» и «заявление», но не различить заявление на отпуск и заявление на изменение персональных данных.

В работе MuSeCLIR авторы отдельно указывают на слабость наборов, где преобладают именованные сущности: их часто можно просто транслитерировать, и такая проверка почти не требует снять неоднозначность смысла. Для русско-казахского набора это особенно неприятно: названия организаций, номера приказов и англоязычные аббревиатуры дадут модели слишком легкие очки.

Сначала зафиксируйте, что значит «релевантен» в вашем домене

Аннотаторы должны оценивать не красоту перевода и не общую тему документа, а возможность пользователя решить исходную задачу. Это правило нужно записать до сбора пар. Иначе один судья отметит документ как релевантный, потому что он «про отпуск», а другой отклонит его, потому что в нем другой вид отпуска.

Для каждого запроса заведите карточку намерения. В ней достаточно пяти полей:

  1. Что пользователь хочет сделать или узнать.
  2. Какой факт, условие или действие должен содержать ответ.
  3. Какие документы допустимы как полное решение.
  4. Какие близкие документы нельзя считать полным решением.
  5. Какая языковая форма запроса ожидается в реальном трафике.

Карточка для запроса «қайтарымды қалай рәсімдеймін» не должна говорить «документ о возврате». Это слишком размыто. Напишите точнее: пользователь хочет оформить возврат товара, нужны сроки, канал обращения и условия возврата. Полностью релевантен регламент возвратов. Частично релевантна страница о гарантийном ремонте. Нерелевантен текст о возврате платежа за услугу, если в нем иной процесс.

Такой контракт убирает привычную ошибку: брать в эталон любой документ, где встретилось нужное слово. В корпоративных базах она часто появляется вокруг слов «өтініш», «келісім», «төлем», «жоба», «есеп». Одно слово может обозначать нужную сущность, но документ описывает другой маршрут, статус или подразделение.

Для правовых, медицинских и финансовых материалов добавьте к карточке признак актуальности. Старый документ может быть хорошо написан, переведен без ошибок и тематически точен, но его нельзя поднимать выше действующей версии. Ранжирование без учета версии создает не языковую проблему, а операционный дефект, который особенно легко пропустить при двуязычной проверке.

Матрица языков выявляет перекос, который прячет среднее

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

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

Поверх этой матрицы добавьте два самостоятельных среза:

  • Смешанный запрос: «eGov анықтамасын скачать», «жеке кабинетте пароль ауыстыру», «договорға қол қою тәртібі».
  • Смешанный документ: русская основная часть с казахскими полями, двуязычная таблица, регламент с названиями разделов на двух языках.

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

Не делайте пары механическими зеркалами. Если русскому запросу соответствует только казахский дословный перевод, система быстро научится угадывать шаблон. Лучше соберите эквивалентные намерения с разной естественной формой. Для одного и того же действия это могут быть «где получить справку», «справка қайдан алынады», «анықтаманы жүктеу керек» и «личный кабинеттен анықтама таба алмай отырмын». Они не обязаны быть взаимными переводами, но обязаны вести к одному набору релевантных материалов.

Исследование по арабско-английскому RAG показало, что смена языка между запросом и подтверждающим документом дает заметный провал именно на этапе ранжирования, а не только при генерации ответа. Авторы также проверяли все сочетания языков запроса и документа, а не одну удобную кросс-языковую диагональ. Для русско-казахской оценки такой дизайн стоит повторить, даже если домен и языковая пара другие.

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

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

Хорошая единица оценки состоит из запроса, пула кандидатов и разметки каждого кандидата. Не храните только одну положительную пару. Один положительный документ не проверяет порядок, потому что модели нечего выбирать.

Практичный формат JSONL может выглядеть так:

{
  "query_id": "hr_014_kk_ru",
  "query": "демалысқа өтінішті қайда жіберемін",
  "query_language": "kk",
  "intent": "leave_request_submission",
  "candidates": [
    {
      "doc_id": "hr_leave_request_ru_v3",
      "document_language": "ru",
      "text": "Заявление на отпуск сотрудник направляет через кадровый портал...",
      "grade": 3,
      "reason": "Описывает нужный канал подачи заявления"
    },
    {
      "doc_id": "hr_leave_policy_kk_old",
      "document_language": "kk",
      "text": "Еңбек демалысының жалпы тәртібі...",
      "grade": 1,
      "reason": "Тема совпадает, но нет способа подачи и редакция устарела"
    },
    {
      "doc_id": "hr_sick_leave_ru",
      "document_language": "ru",
      "text": "Лист нетрудоспособности передается руководителю...",
      "grade": 0,
      "reason": "Другой тип отсутствия"
    }
  ]
}

Поле reason не декоративное. Оно превращает оценку из таблицы чисел в материал для разбора ошибок. Через месяц вы сможете найти все случаи, где модель путает «документ об общей политике» и «документ с конкретным действием», вместо того чтобы заново читать сотни строк вручную.

Сегментируйте документы так же, как их читает человек. Полный регламент в десять страниц часто содержит и нужное правило, и пять побочных процедур. Если в реальном продукте вы ранжируете пассажи, размечайте пассажи. Если пользователь открывает целый документ, добавьте вторую оценку на уровне документа. Нельзя честно сравнивать пассажный реранжировщик с эталоном, где положительным объявлен весь файл.

Трудные отрицательные примеры определяют ценность теста

Меняйте провайдеров без миграции
Переключайтесь между OpenAI, Anthropic, Google, DeepSeek и другими провайдерами через единый шлюз.

Самые полезные отрицательные документы похожи на правильный текст настолько, что человек сначала сомневается. Их не нужно искать искусственно в чужой тематике. Они уже есть рядом с правильными материалами: соседняя процедура, другой статус заявки, иной получатель услуги, устаревшая редакция, исключение из правила.

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

  • Документ с тем же объектом, но другим действием: отмена вместо оформления.
  • Документ с тем же действием, но другим субъектом: для сотрудника вместо клиента.
  • Документ с верным термином и неверным условием: другой срок, лимит или канал обращения.
  • Документ на другом языке, который выглядит как перевод, но описывает смежную процедуру.
  • Документ с корректной старой нормой, которую заменил новый текст.

Негатив «совсем не по теме» нужен только как санитарная проверка. Он показывает, не сломалось ли все сразу. Он почти ничего не говорит о качестве реранжирования.

Опаснее всего ложный друг из двуязычной документации. Предположим, русский документ «возврат денежных средств» относится к отмененной услуге, а казахский фрагмент «тауарды қайтару» говорит о возврате товара. Переводчик может сблизить их, а реранжировщик с поверхностной семантикой поставит рядом. В наборе должна быть такая пара, потому что именно она показывает, улавливает ли модель объект и действие вместе.

Не используйте в качестве единственного отрицательного примера машинный перевод положительного документа с одной заменой слова. Он полезен для изолированного теста на чувствительность, но быстро становится синтетическим шаблоном. Ранжировщик начнет выигрывать на артефакте генерации, а не на понимании доменной речи.

Оценка 0-3 лучше двоичного «нашел или не нашел»

Шкала из четырех оценок дает достаточно различий для поиска и не превращает разметку в диссертацию. Я обычно задаю ее так:

  • 3 - документ позволяет выполнить задачу без другого основного источника.
  • 2 - документ полезен, но не закрывает важное условие или действие.
  • 1 - тема связана с запросом, но читатель не решит задачу по этому тексту.
  • 0 - документ не относится к намерению либо ведет к неверному действию.

Оценка 2 нужна, чтобы не наказывать систему за разумный запасной материал, но и не приравнивать его к прямому ответу. Оценка 1 отделяет тематическое совпадение от реальной пользы. Без нее вы получите спор «релевантен или нет» там, где оба аннотатора правы в разных смыслах.

Два независимых судьи должны размечать сначала небольшой общий пакет, а затем разбирать расхождения по карточкам намерений. Не пытайтесь добиться одинакового вкуса. Добивайтесь одинакового правила. Если один человек дает 3 общему описанию политики, а другой дает 1, проблема обычно лежит в определении полного ответа, а не в их языковой компетенции.

Не скрывайте язык документа от аннотатора, если в продукте пользователь видит оригинал. Пользовательская полезность включает возможность прочитать результат. Но не позволяйте языковому предпочтению автоматически снижать релевантность: документ на другом языке может быть идеальным источником, особенно если интерфейс умеет показывать перевод или краткое объяснение.

Работа Parton, Habash и McKeown о трансъязыковом поиске хорошо показывает, почему это разделение необходимо. Авторы сравнивали релевантность исходных и переведенных результатов и получили падение средней точности из-за ошибок машинного перевода. То есть перевод результата может исказить пользовательскую оценку даже после удачного поиска.

Сначала отделите потерю кандидата от ошибки реранжирования

Сохраните код оценочного контура
Замените base_url и запускайте прежние SDK, код и промпты при смене модели.

Реранжировщик не исправит документ, которого нет в его входном списке. Поэтому храните для каждого запроса два результата: есть ли документ с оценкой 3 среди кандидатов первого этапа и какое место он занимает после реранжирования.

Проверка выглядит просто. Для одного и того же набора запросов сохраните идентификаторы top-100 или другого фактического лимита до реранжирования. Затем посчитайте Recall@K по документам с оценкой 3. После этого считайте nDCG@10 и MRR по переупорядоченному пулу. Если Recall@100 низкий в направлении «казахский запрос - русский документ», задача лежит в эмбеддингах, лексическом поиске, переводе запроса или объединении результатов. Если Recall@100 высокий, но nDCG@10 низкий, виноват реранжировщик или его входная форма.

Минимальный скрипт для такой диагностики не требует специального фреймворка. Он принимает список оценок в фактическом порядке выдачи:

import math

def dcg(grades, k=10):
    return sum(
        (2 ** grade - 1) / math.log2(rank + 2)
        for rank, grade in enumerate(grades[:k])
    )

def ndcg(grades, k=10):
    ideal = sorted(grades, reverse=True)
    denom = dcg(ideal, k)
    return 0.0 if denom == 0 else dcg(grades, k) / denom

def reciprocal_rank(grades):
    for rank, grade in enumerate(grades, start=1):
        if grade == 3:
            return 1 / rank
    return 0.0

observed = [1, 0, 3, 2, 0, 0]
print(f"nDCG@10={ndcg(observed):.3f}")
print(f"MRR@all={reciprocal_rank(observed):.3f}")

Для такого примера вывод будет иметь форму:

nDCG@10=0.590
MRR@all=0.333

Не считайте MRR единственной метрикой. Она полезна, когда у запроса один явно лучший ответ. Но если два документа с оценкой 3 дополняют друг друга или в выдаче важны несколько хороших результатов, MRR перестает описывать пользовательский опыт. В таких случаях nDCG@10 честнее, потому что различает позиции и градации релевантности.

Не смешивайте показатели в один «общий балл» до разбора срезов. Отчет должен отдельно показывать матрицу направлений, смешанные тексты, домены, короткие и длинные запросы, а также случаи с устаревшим документом. Средний nDCG может вырасти, потому что модель стала лучше на легких русских запросах, одновременно просев на редких, но критичных казахских процедурах.

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

Стратегия «переведем запрос на второй язык и запустим обычный поиск» популярна по понятной причине: она быстро использует существующий монолингвальный индекс. Ее обязательно нужно оставить в эксперименте как baseline. Но не называйте ее эталоном до сравнения на ваших трудных парах.

В исследовании Saleh и Pecina по медицинскому кросс-языковому поиску перевод запросов в их условиях в целом обошел перевод документов. Это хороший аргумент за честный тест обеих стратегий, а не за универсальный выбор одной из них. Медицинский корпус, его терминология и качество переводов не совпадают с казахстанскими корпоративными документами.

Для каждого запроса запускайте хотя бы три прогона:

  1. Прямой поиск и реранжирование на исходном запросе.
  2. Поиск по переводу запроса на второй язык.
  3. Объединение кандидатов из обоих прогонов с последующим единым реранжированием.

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

Запрос «қызмет көрсету актісін жіберу» нельзя бездумно превращать в «отправить акт». В разных организациях «акт» может быть актом выполненных работ, актом приема, актом сверки или внутренним подтверждением. Если в английском названии поля и в русской документации закреплен один вариант, а в казахском запросе пользователь говорит более широко, перевод повышает риск ранжировать не ту процедуру.

Вход реранжировщика должен содержать ровно тот контекст, который видит пользователь

Сведите модели к одному API
Запрашивайте модели разных провайдеров через один эндпоинт, не перестраивая оценочный пайплайн.

Модель надо кормить теми же данными, на которых вы ждете от нее решения. Если в продакшене она получает заголовок, фрагмент и метаданные о версии, не оценивайте ее на очищенном полном тексте документа. Если в выдаче пользователю доступен только пассаж, не присваивайте пассажу оценку на основании важного абзаца, который остался за пределами чанка.

Хороший вход обычно включает заголовок, основной текст фрагмента, язык, тип документа, дату или версию, когда они влияют на выбор. Но метаданные нельзя подставлять как секретный ответ. Поле is_current=true допустимо, если его использует и рабочий поиск. Ручная метка «правильный документ» внутри входа, конечно, недопустима.

Проверьте также порядок и длину полей. Если вы склеиваете текст так:

[ru] Заголовок: Порядок возврата товара
Тип: Регламент
Текст: ...

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

Набор должен включать документы, где ответ лежит не в первом предложении. Иначе вы проверите способность сравнивать заголовки. Это полезно для поиска по FAQ, но не заменяет проверку регламентов, договоров, инструкций и базы знаний с плотными условиями.

Прогон без журнала ошибок не дает материала для улучшения

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

Типы ошибок удобно держать стабильными: пропущенное условие, неверный объект, неверное действие, неоднозначный термин, старая версия, потеря смешанного фрагмента, слабый кандидатный поиск, плохая сегментация. Через несколько итераций вы увидите, где тратить время. Если половина ошибок связана с отсутствием нужного кандидата, бессмысленно неделями менять prompt реранжировщика.

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

Если инфраструктура уже использует единый OpenAI-совместимый шлюз, такой прогон удобно запускать как отдельную офлайн-задачу: сохранять версию модели, шаблон входа, список кандидатов и итоговую выдачу в аудит-лог. В AI Router для этого можно использовать единый API-доступ к разным моделям, не меняя клиентский код при сравнении вариантов.

Проверочный набор не доказывает, что вы «поддержали два языка». Он показывает более приземленную вещь: при русской, казахской и смешанной формулировке система поднимает документ, по которому человек сможет сделать нужное действие. Пока в отчете нет этого различия, высокий средний score остается удобной цифрой, а не основанием доверять поиску.

Часто задаваемые вопросы

Чем тест реранжирования отличается от проверки машинного перевода?

Нет. Перевод проверяет, передал ли текст смысл на другом языке. Реранжирование проверяет более узкую вещь: поставит ли система релевантный документ выше правдоподобных, но неверных документов в конкретной выдаче. Один и тот же перевод может быть достаточен для человека и недостаточен для ранжировщика.

Нужно ли включать одноязычные пары в русско-казахский набор?

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

Как тестировать запросы, где русский и казахский смешаны?

Да, если так пишут ваши пользователи или документы. Не пытайтесь заранее очистить корпус до идеального языка: смешанная формулировка часто несет уточнение, название продукта, юридический термин или сокращение, от которого зависит релевантность.

Как понять, виноват поиск кандидатов или сам реранжировщик?

Не сразу. Сначала измерьте, попал ли релевантный документ в пул кандидатов, например в top-100. Если он туда не попал, реранжировщик не мог его поднять, и исправлять нужно первый этап поиска или стратегию расширения запроса.

Сколько уровней релевантности достаточно для ручной разметки?

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

Какие негативные документы полезнее случайных?

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

Какие метрики нужны для оценки реранжировщика?

Смотрите nDCG@10 как главную метрику качества порядка, Recall@K для проверки пула кандидатов и MRR, если у запроса обычно один очевидный лучший документ. Затем обязательно разрезайте результаты по направлению языка и типу смысла, иначе среднее скроет провал на казахских запросах.

Можно ли размечать пары запросов и документов с помощью LLM?

Только после стабильной ручной разметки. LLM может быстро предложить кандидатов, варианты запросов и спорные отрицательные примеры, но не должна в одиночку назначать эталонные оценки, которыми вы затем будете доказывать качество той же модели.

Стоит ли переводить запрос на второй язык перед поиском?

Это бывает полезно, но не бесплатно. Перевод запроса меняет термины, может убрать кодовое переключение и вносит ошибку до ранжирования. Сравните прямой многоязычный реранжировщик, перевод запроса и объединенный подход на одном наборе, а не выбирайте по красивым демо.

Какой размер набора нужен для первого полезного прогона?

Не обязательно. Для первой версии важнее закрыть направления языков и типовые ошибки вашего домена, чем собрать огромную таблицу. Небольшой набор с трудными, проверенными судьями парами обычно полезнее тысяч однотипных параллельных предложений.