АвитоLLM24 мин

Разбор готов

A-vibe: как Авито адаптировало Qwen3 под русский язык, обучение и быстрый инференс

Авито нужна языковая модель, которую можно приспосабливать к задачам пользователей и сотрудников. Простые варианты не устраивали команду по разным причинам: проприетарные API ограничивали контроль и гибкость, готовые открытые модели хуже работали с русским языком и неэкономно его токенизировали, а собственное обучение с нуля требовал слишком много данных, вычислений и времени. Поэтому команда выбрала промежуточный путь: сохранить знания сильной открытой модели Qwen3-8B-Base, заменить проблемную часть токенизации, затем дообучить модель на инструкциях, задачах с вызовом функций, математике и предпочтениях по безопасности и качеству ответа. Разберём, как меняли токенизатор, готовили данные, проверяли способности модели и ускоряли её работу под нагрузкой.

Материал полезен ML/LLM-инженерам, которые адаптируют открытую языковую модель под русский язык, меняют токенизатор у уже предобученной сети, строят SFT/RL-пайплайн, готовят function-calling данные или оптимизируют инференс под реальную параллельную нагрузку. Глава особенно полезна как разбор компромиссов: улучшение одной группы метрик здесь не означает улучшение всех остальных.

Статья · на русском

Как мы в Авито сделали свою LLM — A-vibe

Анастасия Рысьмятова · Опубликовано: 27 октября 2025 г.

Почему не стали обучать языковую модель с нуля

LLM-юнит Авито на момент статьи — команда из 25 человек, которая делает общую core-модель и затем адаптирует её под разные бизнес-кейсы. За два года команда пишет о более чем 60 продуктовых сервисах, более 50 A/B-тестах и более 12 внедрённых и масштабированных продуктах. Эти числа описывают деятельность юнита в целом, а не эффект A-vibe. Контекст компании также велик: в статье названы 72+ млн уникальных пользователей, более 220 млн активных объявлений и около 12 тысяч сотрудников.

Первый вариант — использовать проприетарные модели через API. От него отказались из-за вопросов безопасности, ограниченной гибкости и желания развивать экспертизу внутри компании. Второй — брать готовые открытые модели как есть. Они показывали хорошие общие метрики, но на русском языке результаты были слабее английских, а токенизаторы часто неэффективно разбивали русский текст. Третий — делать собственный pretrain, то есть предварительное обучение модели с нуля на огромном корпусе. Этот путь сочли слишком дорогим и долгим.

Команда выбрала адаптацию открытой модели: взять сильную OS-модель, заменить или перестроить её токенизатор, затем провести инструктивное дообучение и обучение по предпочтениям/вознаграждению. Такой выбор сохраняет значительную часть уже выученных представлений и вычислительных инвестиций исходной модели, но позволяет изменить те части, которые мешают русскоязычным задачам.

Как мы обучали модель Avibe

С чего началась адаптация токенизатора

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

Раннее сравнение токенизаторов в статье. Здесь речь ещё идёт о Qwen 2.5 и внутренних версиях ver5/ver6, а не о финальном merge для Qwen3. Правила подсчёта языковых групп не раскрыты; их числа не складываем в размер словаря.
Раннее сравнение токенизаторов в статье. Здесь речь ещё идёт о Qwen 2.5 и внутренних версиях ver5/ver6, а не о финальном merge для Qwen3. Правила подсчёта языковых групп не раскрыты; их числа не складываем в размер словаря.
ТокенизаторКириллические токеныЛатинские токеныДругие токеныСимволов на токен
Qwen 2.53 996187 769111 5663,24
tokenizer_ver5_32k18 51512 2071 2783,65
tokenizer_ver6_60k36 49321 9401 5673,95

Таблицу можно прокрутить по горизонтали.

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

У команды уже был двухэтапный способ подмены токенизатора. Сначала на этапе freeze замораживали всю сеть, кроме слоя эмбеддингов, то есть таблицы векторов токенов. Затем на этапе unfreeze размораживали все параметры и продолжали обучение на тех же данных. После этого шли SFT и DPO. Позже в текущем проекте между SFT и DPO появился отдельный GRPO-этап.

Адаптация токенизатора

Почему полную подмену словаря заменили объединением двух токенизаторов

У старого способа было слабое место: пересечение токенов открытой модели и собственного словаря обычно составляло около двух тысяч токенов. Большинство новых строк таблицы эмбеддингов приходилось инициализировать заново, поэтому они плохо согласовывались с уже обученными слоями модели. Для эксперимента команда взяла Qwen3-8B-Base и решила сохранить сильную английскую и кодовую часть её словаря, а русскую часть получить из собственного ver6_60k.

Сначала из Qwen3 выделяли кандидатов, состоящих из латинских символов. Этого недостаточно: латиницей пользуются многие языки. Поэтому токены дополнительно ранжировали по частотности на чистом английском корпусе DCLM и сохраняли область до 99-го перцентиля. Отдельно оставили кодовые токены, статистику которых посчитали на внутренней кодовой базе Авито. В тексте этот этап описан как примерно 80 тысяч английских и кодовых токенов.

Для русского ver6_60k использовали похожий отбор, но частотности считали на fineweb_ru. После pruning получился словарь примерно на 36 тысяч токенов с высокой плотностью на русском. Ver6 был заранее обучен в совместимом с Qwen сетапе: совпадали pre-tokenizer и normalizer. Поэтому итоговый merge технически сводился к добавлению новых элементов в vocabulary и соответствующих правил merges.

Плотность токенизации на двух языках по оригинальным графикам. Это характеристика разбиения текста на токены, а не качество ответов модели.
Плотность токенизации на двух языках по оригинальным графикам. Это характеристика разбиения текста на токены, а не качество ответов модели.
ВариантEN, символов/токенRU, символов/токен
Qwen3 original4,652,91
Ruadapt4,644,26
ver6 original4,384,47
Qwen3 tokenizer EN pruned4,590,61
ver6 tokenizer RU pruned0,404,46
Merged tokenizer4,594,47

Таблицу можно прокрутить по горизонтали.

Итоговый объединённый токенизатор почти сохраняет английскую плотность исходного Qwen3: 4,65 → 4,59 символа на токен. На русском изменение намного заметнее: 2,91 → 4,47. Это и было целью merge: не обменять хороший английский на хороший русский, а сохранить обе целевые области достаточно плотными.

Размеры словарей с исходного графика. Подписи частей merge в источнике арифметически не сходятся с подписанным итогом; значения сохраняются без самостоятельного исправления.
Размеры словарей с исходного графика. Подписи частей merge в источнике арифметически не сходятся с подписанным итогом; значения сохраняются без самостоятельного исправления.
Токенизатор или частьРазмер словаря, токенов
Qwen3 original151 669
Ruadapt146 260
ver6 original60 128
Qwen3 tokenizer EN pruned80 074
ver6 tokenizer RU pruned36 731
Merged tokenizer116 393
Плотность токенизации на английском

Объединённый токенизатор даёт 4,59 символа на токен на английском корпусе; Qwen3_original — 4,65. Это сравнение токенизации, не качества ответов.

Источник: Анастасия Рысьмятова · Авито
Плотность токенизации на русском

Для merged_tokenizer показано 4,47 символа на токен против 2,91 у Qwen3_original на русском корпусе.

Источник: Анастасия Рысьмятова · Авито
Размер словаря

На графике итоговый словарь подписан 116 393; части — 80 074 и 36 731. Не складываем подписи в новый итог: их несогласованность источник не поясняет.

Источник: Анастасия Рысьмятова · Авито
Как мы объединили два токенизатора

Как сохранить старые эмбеддинги и обучить только новые строки

После merge токены, пришедшие из исходного Qwen3, можно инициализировать его же эмбеддингами. Эти векторы уже согласованы с остальными весами сети, поэтому на freeze-этапе их важно не испортить. Новые русские токены, наоборот, должны получить новые векторы и адаптироваться под существующую модель. Та же логика относится к lm_head — выходному слою, который превращает скрытое представление в оценки токенов словаря.

Обычное requires_grad=False здесь не решает задачу. В PyTorch вся таблица эмбеддингов хранится одним параметром-матрицей, а requires_grad переключается для параметра целиком. Команде же нужно оставить обучаемыми новые строки и зафиксировать старые.

Первый механизм — gradient hook: функция, которую PyTorch вызывает во время backward. Hook получает градиент матрицы и зануляет строки с ID старых токенов. Это предотвращает обычное градиентное обновление выбранных строк. В практическом описании hook ставят и на input embeddings, и на lm_head.

# Псевдокод из статьи: идея частичной заморозки строк, не универсальная готовая реализация
def make_row_zero_hook(token_ids):
    def hook(grad):
        idx = to_device(token_ids, grad.device)
        return zero_rows(grad, idx)
    return hook

def install_freeze_hooks(model, token_ids, include_lm_head=True):
    hook = make_row_zero_hook(unique_sorted(token_ids))
    get_input_embeddings(model).weight.register_hook(hook)
    # В рабочей схеме статьи hook также ставится на lm_head.

Но нулевого градиента недостаточно из-за AdamW. Этот оптимизатор отдельно применяет weight decay — усадку веса, не являющуюся частью градиента функции потерь. Поэтому строка может двигаться даже при нулевом gradient.

В записи статьи первый вычитаемый член — обычный шаг оптимизатора, второй — отдельный weight decay. Нулевой gradient устраняет не все изменения веса.

Отсюда нужны две защиты старых строк. Hook обнуляет их градиент и не даёт обычному шагу оптимизатора менять векторы. Отдельно Trainer исключает embedding weights из списка параметров, к которым применяется weight decay. Без второго шага старые строки всё равно постепенно сдвигались бы.

# Псевдокод из статьи: отключение weight decay для эмбеддингов
class NoDecayEmbeddingsTrainer(Trainer):
    def get_decay_parameter_names(self, model):
        decay = base_decay_list(model)
        decay -= all_embedding_weight_names(model)
        return sorted(decay)

old_token_ids = collect_old_token_ids(tokenizer, old_tokenizer)
install_freeze_hooks(model, old_token_ids, include_lm_head=True)
trainer = NoDecayEmbeddingsTrainer(model=model, args=training_args, ...)
trainer.train()

После этого freeze действительно концентрируется на новых токенах, не размывая уже выученные представления старых. Затем на unfreeze-этапе модель можно обучать целиком. Важно, что это решение относится к стадии адаптации весов; оно не является частью выполнения уже готовой модели на пользовательском запросе.

Адаптация токенизатора

Корпус для адаптации токенизатора: объём, языки и тематики

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

Языковой баланс корпуса по оригинальному графику.
Языковой баланс корпуса по оригинальному графику.
СоставДоля
Русский язык31%
Английский язык31%
Код на языках программирования26%
Прочие языки12%

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

Тематический состав корпуса по проверенному графику. Это данные обучения, а не распределение пользовательского трафика.
Тематический состав корпуса по проверенному графику. Это данные обучения, а не распределение пользовательского трафика.
ТематикаДоля
Код на языках программирования26%
Гуманитарные науки18%
Естественные науки16%
Точные науки13%
Общие темы10%
Художественная литература5%
Культура4%
Рекламный контент4%
Новости2%
Прочее2%
Языковой состав данных

31% русского, 31% английского, 26% кода и 12% прочих языков — распределение на исходном графике.

Источник: Анастасия Рысьмятова · Авито
Состав данных по тематикам

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

Источник: Анастасия Рысьмятова · Авито
Данные для адаптации токенизатора

SFT: научить модель следовать инструкции и вести диалог

SFT, supervised fine-tuning, — дообучение на примерах, где модели показывают желаемый ответ. Здесь его задача была базовой: научить A-vibe отвечать на вопросы, вести диалог и понимать system prompt. Набор содержал более 800 тысяч примеров.

Основные источники SFT-данных, перечисленные в статье.
Основные источники SFT-данных, перечисленные в статье.
ИсточникРоль
Открытые наборы данныхОбщие инструктивные примеры
Синтетические примерыРасширение задач и сценариев
Логи общения сотрудников Авито с LLMРеальные форматы внутренних запросов
Школьные и олимпиадные задачиЗадачи на рассуждение и знания
Данные реальных бизнес-кейсовДоменные сценарии

Качество SFT-данных контролировали через LLM as a judge — другую языковую модель, которая по заданным критериям оценивает сгенерированный пример. Особенно много внимания уделили function calling: способности модели не только написать текст, но и сформировать структурированный вызов разрешённого инструмента.

SFT этап

Как генерировали синтетические multi-turn диалоги с инструментами

Для function calling команда не ограничилась одиночными парами «запрос — вызов функции». Целью были многоходовые, или multi-turn, диалоги, где пользователь уточняет задачу, агент выбирает инструменты, получает результаты API и продолжает разговор. Подход опирался на APIGen-MT и на идею разнообразия через большое множество персон.

  1. 01

    Описание инструментов

    Команда парсит описания ручек сервисов Авито.

  2. 02

    Формат tools

    LLM переводит описания ручек в OpenAI-формат описания инструментов.

  3. 03

    Проверка схемы

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

  4. 04

    Персона и набор инструментов

    Сэмплируются несколько tools и персона из набора, использованного авторами идеи 1,000,000,000 Personas.

  5. 05

    Среда и политика

    LLM создаёт домен, роли, возможности инструментов, ограничения их использования, описание данных и правила поведения агента с персонализацией.

  6. 06

    План конкретного диалога

    LLM создаёт system prompt агента, цель пользователя и ожидаемый план развития разговора.

Подготовка конфигурации будущего диалога

Полученную конфигурацию проверяет LLM-судья. Он даёт фидбек, конфигурация переписывается и снова оценивается. Цикл повторяют до пяти раз; если после пятой итерации judge всё ещё считает конфигурацию некачественной, пример отбрасывается.

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

  1. 01

    Модель пользователя

    Получает персону и цель пользователя и генерирует следующую пользовательскую реплику.

  2. 02

    Модель ассистента

    Отвечает и при необходимости формирует вызов инструмента.

  3. 03

    API-эмулятор

    Принимает описание инструмента и запрос, затем возвращает синтетический результат вызова API.

  4. 04

    Модель остановки

    Решает, достигнута ли цель и пора ли завершать диалог.

  5. 05

    Финальный LLM-судья

    После завершения проверяет корректность использования tools и соответствие диалога заранее заданному плану.

Участники генерации одного синтетического диалога
SFT этап

GRPO: три отдельные цели вместо одного общего «улучшения»

GRPO здесь используется как этап обучения с вознаграждением: модель генерирует несколько ответов, получает вознаграждение (reward) по правилам конкретной задачи, а обучение повышает вероятность более удачных вариантов. Команда отдельно работала с function calling, кодом и математикой.

По наблюдению авторов, GRPO на одном из этих доменов не давал значимого ухудшения метрик в других доменах, поэтому запуски проводили последовательно. Это эмпирическое наблюдение именно их экспериментов; итоговые таблицы всё равно показывают, что отдельные метрики после GRPO и DPO могут снижаться.

GRPO этап

GRPO для function calling: строгий бинарный reward

Из синтетических multi-turn диалогов делали пары «префикс диалога → следующий ответ ассистента». Добавляли irrelevant-примеры, где вызов функции не нужен, чтобы модель училась не только правильно вызывать инструмент, но и вовремя отказываться от вызова.

Reward был rule-based, то есть вычислялся правилами, без LLM-судьи: 1 только при полном совпадении вызова функций с эталоном и 0 при любом несовпадении. Авторы специально не дают частичную награду только за правильный формат или отдельные поля, чтобы модель обучалась полностью корректному действию.

Параметры GRPO-запуска для function calling.
Параметры GRPO-запуска для function calling.
ПараметрЗначение
Вычисления4 × 8 × H100
Обучающие примеры2,5 тыс.
Итерации1,6 тыс.
Gradient accumulation8
Rollouts на пример32
Temperature0,7
Learning rate1e-6
Главные агрегированные результаты function calling после GRPO.
Главные агрегированные результаты function calling после GRPO.
БенчмаркSFTПосле GRPOИзменение
BFCL V3, английский49,68%59,55%+9,87 п.п.
BFCL V3, русский перевод40,22%49,25%+9,03 п.п.

Таблицу можно прокрутить по горизонтали.

Эти два агрегата показывают сильный локальный эффект GRPO, но не означают, что каждая подзадача BFCL выросла. В полном разрезе ниже есть, например, просадки в multi-turn сценариях и разные направления изменений relevance/irrelevance detection.

Function Calling

GRPO для кода: большой запуск без заметного выигрыша

Для кода собрали 120 тысяч алгоритмических задач на Python. В каждой было от 3 до 450 тестов. Сгенерированное решение запускали на тестах: пройденные тесты давали положительную награду, а ошибки, таймауты и непройденные проверки — штраф.

Параметры кодового GRPO.
Параметры кодового GRPO.
ПараметрЗначение
Обучающие задачи120 тыс.
Тестов на задачу3–450
Итерации5 тыс.
Gradient accumulation8
Вычисления4 × 8 × H100

Несмотря на объём запуска, значимого улучшения на выбранных бенчмарках авторы не зафиксировали. Они перечисляют HumanEval, MBPP, MERA Code RuHumanEval, MERA Code CodeCorrectness и MERA Code StruCom. Поэтому этот эксперимент нельзя описывать как успешный GRPO для программирования: отрицательный результат — часть итоговой картины.

GRPO этап — Код

GRPO для математики: почему слишком простые задачи мешают обучению

Для математики использовали 10 тысяч задач на английском и ещё 10 тысяч, переведённых на русский. Reward был бинарным: 1, если финальный ответ совпал с golden answer, и 0 иначе. Кандидатов генерировали через vLLM в colocated-режиме; по словам авторов, это ускорило обучение в несколько раз.

Параметры математического GRPO.
Параметры математического GRPO.
ПараметрЗначение
Данные10 тыс. EN + 10 тыс. RU
Вычисления8 × H100
Итерации4 тыс.
Gradient accumulation8
Learning rate5e-7
Beta1e-3
Temperature0,7
num_generations64

Большие значения temperature делали обучение сильно нестабильным; в диапазоне примерно 0,3–0,7 авторы существенной разницы не увидели. Увеличение num_generations, то есть числа кандидатов на пример, заметно улучшало качество.

Ключевой приём — убрать слишком лёгкие задачи. Для каждой задачи считали pass rate по восьми генерациям. Если модель решала задачу 8 раз из 8, пример исключали. Причина связана с advantage: когда все варианты получают одинаково высокий reward, разница между ними почти нулевая и обучению нечем направленно обновлять политику.

Заявленный результат математического GRPO на русской версии Math500.
Заявленный результат математического GRPO на русской версии Math500.
МетрикаДоПосле
Math500 RU0,600,69

Таблицу можно прокрутить по горизонтали.

Математика

DPO: пары предпочтений для безопасности и качества ответа

DPO, Direct Preference Optimization, — дообучение по парам ответов, где один выбран как предпочтительный (chosen), а другой как хуже подходящий (rejected). В A-vibe этим этапом хотели повысить безопасность, структурированность и понятность ответов.

Два типа вопросов для DPO.
Два типа вопросов для DPO.
НаборОбъёмСодержание
SAFE DATA120 тыс. вопросовОбщие вопросы и несложные задания из разных областей
UNSAFE DATAоколо 40 тыс. вопросов на русскомОткрытый англоязычный датасет уровней minor, major, critical, переведённый большой открытой моделью

Таблицу можно прокрутить по горизонтали.

  1. 01

    Восемь ответов целевой модели

    Для каждого вопроса собирали 8 генераций A-vibe с конфигурацией, подобранной под полноту и разнообразие ответов.

  2. 02

    Выбор лучшего и худшего

    Большая открытая модель с отдельным промптом и критериями для каждого набора выбирала chosen и rejected.

  3. 03

    Баланс длин

    Количество пар с длинными и короткими ответами уравновешивали по коэффициенту разницы длин.

Как строили preference dataset

Команда также проверила более простой способ: брать ответ большой открытой модели как chosen, а ответ целевой модели как rejected. Этот вариант, по словам авторов, качественных результатов не дал. Поэтому финальная схема строит предпочтение между несколькими ответами самой целевой модели, а внешняя модель используется как разметчик.

DPO этап

Как оценивали DPO и где уже видна неоднозначность

Помимо общих бенчмарков команда сделала два доменных набора с LLM as a judge: safe и unsafe. Для каждого были свои промпты и подробные критерии качества. Эти наборы использовали для наблюдения за обучением и выбора конфигурации DPO.

В тексте статьи результат RU_ARENA сформулирован как рост с 77,7 до 79,2 на arena-hard-v0.1 с судьёй gpt-4-1106-preview. Однако ниже в таблице для того же судьи указано 79,9 у avibe_dpo_final. Источник не объясняет расхождение, поэтому обе версии нужно сохранять: 79,2 — в описании этапа DPO, 79,9 — в итоговой таблице.

ОЦЕНКА МОДЕЛИ

Как читать бенчмарки A-vibe

Замеры проводились с жадными параметрами генерации. Qwen3-8B и RuadaptQwen3-8B-Hybrid измеряли в режиме no_think. Авторы отдельно предупреждают: методика конкретного бенчмарка может отличаться от чужих публикаций из-за версии данных, формулировки system prompt и числа few-shot примеров.

Для воспроизводимости команда прикладывает часть данных для GPQA Diamond RU, Math500 RU, RuDROP и RU BFCL, а также изменения в lm-evaluation-harness. Поэтому сравнивать числа лучше внутри одной таблицы статьи и одной методики, а не механически с внешними лидербордами.

Ни одна из следующих таблиц сама по себе не доказывает внедрение соответствующей способности во все продукты Авито. Это офлайн-измерения моделей. В частности, наличие внутренних бенчмарков по объявлениям, поиску и параметрам товара не является заявлением об отдельном бизнес-эффекте A-vibe.

Метрики качества Avibe

Общие знания и внутренние задачи Авито: рост не монотонный

Общие знания. Значения приведены в логике исходной таблицы; более высокий score трактуется как лучший в этих колонках.
Общие знания. Значения приведены в логике исходной таблицы; более высокий score трактуется как лучший в этих колонках.
МодельMMLU RUMMLU ENGPQA Diamond RUGPQA Diamond ENShlepaBaby MMLU
A-vibe SFT0,7210,7510,3640,3740,4840,766
A-vibe GRPO0,7180,7500,3430,3540,4890,765
A-vibe DPO final0,7180,7520,3430,3180,4860,766
Qwen3-8B0,7010,7300,3180,3690,4540,682
RuadaptQwen3-8B-Hybrid0,6980,7290,3990,2680,4440,456

Таблицу можно прокрутить по горизонтали.

Здесь post-training не даёт равномерного роста. Например, GPQA Diamond RU снижается с 0,364 после SFT до 0,343 после GRPO и остаётся там после DPO; GPQA Diamond EN падает до 0,318. Baby MMLU в статье считается через перплексию: question и варианты ответов объединяются, а выбирается вариант с минимальной perplexity.

Внутренние бенчмарки Авито. OKK — определить нарушение в описании объявления числом 0–6; relevance — релевантность пары «запрос — выдача»; I2P — выбрать релевантный параметр товара по описанию.
Внутренние бенчмарки Авито. OKK — определить нарушение в описании объявления числом 0–6; relevance — релевантность пары «запрос — выдача»; I2P — выбрать релевантный параметр товара по описанию.
МодельOKKRelevanceI2P
A-vibe SFT0,7240,9620,720
A-vibe GRPO0,7120,9620,763
A-vibe DPO final0,7190,9590,766
Qwen3-8B0,7170,9080,668
RuadaptQwen3-8B-Hybrid0,6720,5750,677

Таблицу можно прокрутить по горизонтали.

На внутренних задачах видно тот же компромисс: I2P растёт заметно, но OKK после SFT проседает на GRPO и не возвращается к исходному значению после DPO; relevance после DPO немного ниже 0,962. Поэтому корректный вывод — улучшение части целевых способностей при сохранении отдельных регрессий, а не «каждый этап делал модель лучше по всем метрикам».

Общие знания о мире

STEM, DOoM и русский язык: что улучшилось, а что просело

В STEM-таблице pass@10 — метрика кодовых задач, а avito_stash измеряется через perplexity (ppl): авторы маскировали часть внутреннего кода и оценивали способность модели предсказывать скрытый фрагмент. Для perplexity меньшее значение лучше.

Математические и кодовые способности.
Математические и кодовые способности.
МодельGSM8K ENMath500 RUMath500 ENRuHumanEval pass@10Avito stash, ppl ↓
A-vibe SFT0,9010,6020,7200,4452,211
A-vibe GRPO0,9200,6920,7140,3902,335
A-vibe DPO final0,9100,6860,7140,3782,385
Qwen3-8B0,9270,5460,7360,4203,811
RuadaptQwen3-8B-Hybrid0,9020,6900,7440,4182,879

Таблицу можно прокрутить по горизонтали.

Математический GRPO действительно отражается в Math500 RU: 0,602 → 0,692. Но кодовая картина обратная: RuHumanEval падает 0,445 → 0,390 → 0,378, а perplexity внутреннего stash ухудшается относительно SFT 2,211 → 2,335 → 2,385. Это согласуется с отдельным выводом авторов, что code-GRPO не дал значимого выигрыша.

DOoM: математика и физика без reasoning.
DOoM: математика и физика без reasoning.
МодельTotalRussian mathRussian physics
A-vibe DPO final0,2800,3670,194
RuadaptQwen3-8B-Hybrid0,2530,3220,184
Qwen3-8B0,2400,2860,194

Таблицу можно прокрутить по горизонтали.

Для DOoM в статье нет промежуточных SFT/GRPO значений A-vibe, поэтому по этому набору нельзя приписать прирост конкретному этапу post-training. Видно только сравнение финальной модели с двумя внешними базами в рамках этой методики.

Работа с языком.
Работа с языком.
МодельRU FactsRuBLiMPRuDROP
A-vibe SFT0,7270,9320,407
A-vibe GRPO0,7090,9300,426
A-vibe DPO final0,7180,9300,394
Qwen3-8B0,7240,9160,318
RuadaptQwen3-8B-Hybrid0,7370,8530,232

Таблицу можно прокрутить по горизонтали.

RuDROP растёт после GRPO 0,407 → 0,426, а затем падает после DPO до 0,394. RU Facts у финальной A-vibe — 0,718, то есть ниже как SFT-версии, так и исходного Qwen3-8B 0,724. Эти просадки важны для честного чтения итогов.

Математические и кодовые способности моделей (STEM)

Function calling: высокий общий прирост и слабые места multi-turn

BFCL V3 на английском.
BFCL V3 на английском.
МодельBFCL V3 EN
A-vibe SFT49,68%
A-vibe GRPO59,55%
A-vibe DPO final58,63%
Qwen3-8B60,20%
RuadaptQwen3-8B-Hybrid (reasoning)54,91%

GRPO прибавляет почти десять процентных пунктов относительно A-vibe SFT, но финальная DPO-версия немного откатывается, а исходный Qwen3-8B в этой таблице всё ещё выше финальной A-vibe. Поэтому корректное утверждение — GRPO сильно улучшил FC относительно собственного SFT-бейзлайна.

BFCL V3, переведённый на русский. Проценты — значения отдельных подметрик из исходной таблицы.
BFCL V3, переведённый на русский. Проценты — значения отдельных подметрик из исходной таблицы.
ПодметрикаQwen3-8BRuadapt reasoningA-vibe SFTA-vibe GRPOA-vibe DPO
Overall Acc50,72%46,35%40,22%49,25%49,00%
Non-Live AST Acc74,58%74,04%67,62%73,17%73,19%
Non-Live Simple AST69,83%70,17%70,50%72,67%73,75%
Non-Live Multiple AST81,00%81,50%74,00%82,50%82,00%
Non-Live Parallel AST79,50%81,00%68,50%75,00%75,00%
Non-Live Parallel Multiple AST68,00%63,50%57,50%62,50%62,00%
Live Acc65,62%62,99%53,84%68,46%68,15%
Live Simple AST76,36%73,26%64,73%68,22%67,83%
Live Multiple AST61,16%58,40%60,97%60,68%58,78%
Live Parallel AST75,00%56,25%87,50%87,50%87,50%
Live Parallel Multiple AST70,83%54,17%66,67%66,67%66,67%
Multi Turn Acc10,62%1,50%4,88%3,50%3,12%
Multi Turn Base14,00%1,50%5,50%4,50%4,00%
Multi Turn Miss Func10,50%2,00%8,00%2,50%2,50%
Multi Turn Miss Param7,50%1,50%2,50%5,00%3,50%
Multi Turn Long Context10,50%1,00%3,50%2,00%2,50%
Relevance Detection94,44%94,44%94,44%94,44%88,89%
Irrelevance Detection74,07%70,93%39,76%81,62%82,26%

Таблицу можно прокрутить по горизонтали.

Overall растёт 40,22% → 49,25% после GRPO, а irrelevance detection почти удваивается. Но multi-turn accuracy падает 4,88% → 3,50% → 3,12%, Multi Turn Miss Func после GRPO тоже резко ниже, а relevance detection после DPO снижается до 88,89%. Один общий процент скрывает разные режимы поведения.

Работа с функциями

MERA и RU_ARENA: разные судьи дают разную картину

MERA: текст, код и два индустриальных домена.
MERA: текст, код и два индустриальных домена.
МодельMERA textMERA CODE totalMERA CODE privateIndustrial: медицинаIndustrial: с/х
A-vibe SFT0,6160,3040,3670,6090,513
A-vibe GRPO0,6240,3140,3510,6120,521
A-vibe DPO final0,6180,3120,3670,5650,483
Qwen3-8B0,5100,2800,3360,5170,300
RuadaptQwen3-8B-Hybrid0,4980,3220,3800,2600,437

Таблицу можно прокрутить по горизонтали.

После GRPO растут MERA text, общий CODE и оба индустриальных столбца, но private CODE снижается. После DPO text и общий CODE немного откатываются, медицина падает 0,612 → 0,565, сельское хозяйство 0,521 → 0,483, а private CODE возвращается к 0,367. Это ещё один пример немонотонного post-training.

RU_ARENA arena-hard-v0.1, судья gpt-4-1106-preview. Судью нельзя смешивать с результатами следующей таблицы.
RU_ARENA arena-hard-v0.1, судья gpt-4-1106-preview. Судью нельзя смешивать с результатами следующей таблицы.
МодельScoreScore with length controlСредняя длина, токенов
gpt-4-1106-preview90,981,4541
gpt-4o-mini83,975,4448
T-Tech-T-pro-it-1.083,873,8502
gigachat_max_26.20_uncen82,772,5514
gigachat_max_with_censor80,068,9515
A-vibe DPO final79,965,5831
vikhr-nemo-12b-instruct-r-21-09-2479,865,5627
A-vibe SFT77,763,0755
A-vibe GRPO77,663,5792
Qwen3-8B76,763,0894
gemma-2-9b-it-sppo-iter373,659,0509
RuadaptQwen3-8B-Hybrid72,056,2821
T-Tech-T-lite-it-1.071,059,3544

Таблицу можно прокрутить по горизонтали.

Для этого судьи итоговая таблица показывает рост A-vibe SFT 77,7 → DPO 79,9. Но в прозе раздела DPO автор отдельно пишет 77,7 → 79,2. Это внутреннее расхождение исходника; выбирать одну цифру вместо другой без объяснения нельзя.

RU_ARENA arena-hard-v0.1, отдельный запуск с судьёй gpt-4o-mini.
RU_ARENA arena-hard-v0.1, отдельный запуск с судьёй gpt-4o-mini.
МодельScoreScore with length controlСредняя длина, токенов
A-vibe GRPO93,386,5792
A-vibe DPO final89,781,5831
Qwen3-8B89,281,7894
RuadaptQwen3-8B-Hybrid88,880,8821

Таблицу можно прокрутить по горизонтали.

С другим судьёй картина меняется: GRPO-версия A-vibe получает 93,3 и заметно выше DPO 89,7. Поэтому нельзя говорить, что DPO «улучшил RU_ARENA вообще» — вывод зависит от judge и конкретной методики измерения.

MERA

Почему A-vibe быстрее Qwen3 ещё до спекулятивного декодинга

Скорость измеряли на примерах из SFT-датасета, состоящего преимущественно из русскоязычных текстов. У A-vibe более плотный русский токенизатор, поэтому один и тот же текст требует меньше входных и выходных токенов. Кроме того, размер модели после изменения словаря указан как 7,9B параметров против 8,2B у Qwen3-8B.

На одинаковых русскоязычных примерах авторы сообщают в среднем 15–25% ускорения относительно исходной Qwen3-8B; на отдельных задачах — до двух раз. Это результат их данных и конфигурации, а не универсальная скорость модели на любом трафике.

Бенчмарки скорости Avibe

EAGLE-3: ускорение зависит от размера параллельной нагрузки

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

В статье используется EAGLE-3. По объяснению авторов, в отличие от EAGLE-2 он прямо генерирует токены вместо предсказания будущих hidden states и объединяет признаки нескольких слоёв target-модели — ранних, средних и поздних. Для исполнения команда использовала реализации в vLLM и SGLang; на момент написания статьи EAGLE-3 у них быстрее работал через SGLang.

Draft-модель обучали на примерно 100 млн токенов из SFT-данных, преимущественно русских и близких основной модели по домену и стилю. Средняя acceptance length — число корректно предложенных draft-моделью токенов — составила 2,4 против 2,1 у EAGLE-2.

Режимы ускорения нельзя объединять в один множитель.
Режимы ускорения нельзя объединять в один множитель.
РежимРезультатУсловие
Одиночный запросдо 2,7× быстрееВнутренние замеры A-vibe + EAGLE-3
Нагрузка 12 rpsдо 1,9× быстрее12 rps — максимум Qwen3-8B без накопления очереди на данных SFT
Batch size = 64дополнительный выигрыш ≈ 0Эффект speculative decoding постепенно исчезает с ростом batch
Оригинальная работа EAGLE-3до ~6,5×Внешний результат, не результат Авито; в статье также упоминается ~1,4× относительно EAGLE-2

Таблицу можно прокрутить по горизонтали.

Среднее полное время ответа при нагрузке 12 запросов/с. Это не TTFT — не время до первого токена.
Среднее полное время ответа при нагрузке 12 запросов/с. Это не TTFT — не время до первого токена.
Модель / режимСреднее полное время ответа
A-vibe + EAGLE-33,9 с
A-vibe7,3 с
Qwen3-8B9,1 с

Авторы отдельно отмечают расхождение между своими максимумами до 2,7× и внешним заявлением до ~6,5×. Их гипотеза: очень большие ускорения легче получить в узком домене со специально переобученной draft-моделью и высокой acceptance length. Внутренний замер Авито показывает, что с ростом батча дополнительный проход небольшой модели перестал давать ускорение. Это наблюдение данного сценария, а не универсальный предел спекулятивного декодирования.

Среднее время ответа при 12 rps

На примерах преимущественно русского SFT-датасета: Avibe + Eagle3 — 3,9 с, Avibe — 7,3 с, Qwen3-8B — 9,1 с. Это среднее полное время ответа, не задержка первого токена и не универсальная гарантия.

Источник: Анастасия Рысьмятова · Авито
Спекулятивный декодинг

Итог: от адаптации Qwen3 до применения готовой A-vibe

A-vibe появилась не из идеи обучить LLM с нуля, а из более узкого выбора: сохранить pretrain Qwen3 и исправить неэффективную работу с русским. Полная подмена токенизатора плохо сохраняла уже выученные представления, поэтому команда объединила английские и кодовые токены Qwen3 с русской частью своего словаря. Старые embeddings и lm_head на freeze-этапе защищали двумя способами: зануляли их градиенты hook-ом и отдельно исключали embeddings из AdamW weight decay.

  1. 01

    Запрос

    Сервис получает текст, system prompt и при необходимости описания доступных инструментов.

  2. 02

    Токенизация

    Объединённый словарь переводит вход в токены; на русском их обычно меньше, чем у исходного Qwen3.

  3. 03

    Применение модели

    Готовая A-vibe обрабатывает вход и определяет продолжение с использованием уже подготовленных весов.

  4. 04

    Получение продолжения

    При обычной генерации модель выдаёт токены последовательно; при EAGLE-3 небольшая модель предлагает токены, а A-vibe проверяет их. Выигрыш зависит от нагрузки.

  5. 05

    Результат

    Вызывающий сервис получает ответ или function call и применяет собственную прикладную логику.

Упрощённое применение уже обученной модели

Обучение происходило заранее. Токенизатор адаптировали на 150 млрд токенов, затем провели SFT на более чем 800 тыс. примеров, включая синтетические multi-turn диалоги с API-эмуляцией и LLM-судьёй. GRPO отдельно улучшал function calling и математику, но кодовый запуск не дал значимого выигрыша. DPO строился на chosen/rejected парах для безопасных и небезопасных вопросов; упрощённый вариант с ответом большой модели как chosen не сработал.

Результаты остаются компромиссными: часть русских, FC и математических метрик растёт, но отдельные языковые, кодовые, multi-turn и индустриальные показатели проседают. RU_ARENA зависит от судьи, а текст статьи расходится с таблицей: 79,2 против 79,9. На русскоязычных SFT-примерах A-vibe быстрее Qwen3; EAGLE-3 даёт до 2,7× на одиночном запросе и около 1,9× при 12 rps, но теряет выигрыш к batch 64. Отдельного A/B-бизнес-эффекта именно A-vibe статья не измеряет.

Бенчмарки скорости Avibe