TL;DR
RubricArmor — это способ составить рубрику (список критериев, по которым судят ответ) через цикл «атака → починка». Модель не просто пишет критерии. Она сама пытается написать плохой ответ, который эти критерии пропустят. Если получилось, критерии дописывают, и круг повторяется, пока взломать список не удаётся.
Главная проблема в том, что рубрика, которую LLM написала «с первого раза», дырявая. Она проверяет очевидное («назван Париж») и пропускает остальное (Париж «в Азии»). Или формулирует критерий так расплывчато, что проходит неверный ответ. Просить модель «найди слабые места в своём списке» тоже мало. Она придумывает дефекты, которых на деле нет, и начинает штрафовать нормальные ответы. Работает только конкретный контрпример: готовый плохой текст, который прошёл все пункты.
Суть метода в нескольких ролях и повторах. Атакующий пишет ответ с одним скрытым дефектом. Проверяющий убеждается, что ответ набирает полный балл. Независимый судья подтверждает, что дефект реальный и портит ответ. Ремонтник правит рубрику, проверяющий убеждается, что плохой ответ теперь проваливается, а ревьюер следит, чтобы старые критерии не сломались. Останавливаются после 7 раундов или после 3 раундов подряд без правок.
Схема метода
ШАГ 0: Составитель → черновая рубрика из запроса (Direct)
ЦИКЛ (до T=7 раундов; стоп, если P=3 раунда подряд без правок):
АТАКА
ШАГ 1: Атакующий → плохой ответ с ОДНИМ скрытым дефектом,
который проходит текущую рубрику (смотрит в память H)
ШАГ 2: Проверяющий → оценивает ответ по рубрике. Не 100% → атака провалена
ШАГ 3: Независимый судья → дефект реально есть и реально портит ответ?
Нет → атака не засчитана
РЕМОНТ
ШАГ 4: Ремонтник → правит/добавляет критерии, не ломая старые
(критерии атомарные, самодостаточные, непересекающиеся)
ШАГ 5: Проверяющий → плохой ответ теперь теряет хотя бы один пункт?
ШАГ 6: Ревьюер → дефект покрыт, хорошие критерии целы?
Да → правка принята
ПАМЯТЬ H: все прошлые атаки, исходы и принятые правки
ВЫХОД: последняя принятая рубрика
Ролей несколько, их можно разнести по разным чатам или симулировать в одном промпте. В статье это полностью автоматическая мультиагентная система.
Пример применения
Задача: Вы руководитель поддержки маркетплейса и внедряете ИИ-бота, который отвечает про возвраты. Нужен чек-лист приёмки ответов бота, чтобы LLM-судья проверял сотни ответов в неделю. Простой чек-лист от ChatGPT вроде «упомянут срок возврата» пропустит ответ, где срок верный, а условия выдуманы.
Промпт:
Ты проведёшь адверсарную доработку рубрики для проверки ответов бота поддержки.
ЗАПРОС КЛИЕНТА: «Хочу вернуть кроссовки, купил 10 дней назад, коробку выбросил. Как это сделать и сколько ждать деньги?»
ФАКТЫ ПОЛИТИКИ: возврат обуви надлежащего качества — в течение 14 дней; сохранены товарный вид и ярлыки (коробка не обязательна); возврат через пункт выдачи или курьера бесплатно; деньги возвращаются в течение 3–10 рабочих дней на способ оплаты.
Сначала составь черновую рубрику (5–7 критериев). Затем сделай до 5 раундов:
1. АТАКА: напиши ответ бота, который проходит ВСЕ текущие критерии, но содержит ОДИН реальный дефект, серьёзно портящий ответ. Назови дефект.
2. ПРОВЕРКА: пройди по критериям и честно покажи, что ответ получает полный балл. Если не получает — атака не засчитана.
3. СУД: как независимый судья оцени, что дефект действительно есть и серьёзно вредит клиенту. Если нет — атака не засчитана.
4. РЕМОНТ: поправь рубрику, чтобы дефект ловился. Критерии должны быть атомарными (одна проверка), самодостаточными (все факты и числа внутри критерия), непересекающимися. Не удаляй и не ослабляй удачные старые критерии.
5. ПЕРЕПРОВЕРКА: покажи, что плохой ответ теперь теряет хотя бы один пункт, и что прошлые плохие ответы тоже ловятся.
Веди «память»: список прошлых атак и принятых правок. Остановись, если 2 раунда подряд атаки не удались. В конце выдай финальную рубрику.
Результат: Модель покажет черновую рубрику, затем по раундам: плохой ответ (например, с верным сроком, но придуманным требованием), оценку по критериям, вердикт судьи, правку рубрики. В конце придёт финальный список критериев, куда вошли проверки, которых не было в черновике: условия возврата, бесплатность, срок выплаты. Список можно вставить в промпт LLM-судьи.
Почему это работает
Слабость: когда LLM пишет критерии «по запросу», она описывает идеальный ответ со стороны и не проверяет, как его можно обойти. Получаются критерии, которые проходят и хорошие, и плохие тексты. Ещё хуже, если попросить модель «покритиковать» свой список. Она описывает дефекты абстрактно и может выдумать несуществующие. Правки тогда бьют по нормальным ответам.
Сильная сторона: LLM хорошо пишет конкретный текст по заданным ограничениям («верно в пунктах А, Б, но неверно в В»). Она также неплохо проверяет текст по пунктам и подтверждает «да, это ошибка».
Как метод это использует: реальный плохой ответ, прошедший проверку, служит доказательством дыры. Его можно перепроверить, и по нему можно целенаправленно чинить. В абляции (отключении частей метода) каждая половина дала мало: «критика без конкретного ответа» и «ответ без умышленного дефекта» улучшили результат лишь слегка. Вместе они дали заметно больше суммы частей.
Рычаги управления: - Число раундов (T) → меньше для простых задач, больше для сложных. Выигрыш в статье растёт с раундами, но число критериев тоже растёт. - Терпение (P) → сколько неудачных атак подряд считать концом. - Независимый судья → убери его, и атаки станут «выдуманными дефектами». Лучше другой чат или другая модель. - Свойства критериев (атомарность, самодостаточность, непересечение) → можно заменить своими требованиями. - Память → без неё ремонтник «чинит одно, ломает другое».
Шаблон промпта
Ты ведёшь адверсарную доработку рубрики (списка критериев оценки ответа).
Роли: Составитель, Атакующий, Проверяющий, Независимый_судья, Ремонтник, Ревьюер.
Запрос: {запрос_или_тип_задачи}
Факты и требования: {факты_политика_стандарты}
T = {макс_раундов}
P = {терпение_раундов_без_правок}
R = Составитель.build_rubric(Запрос) # черновик, {число_критериев} критериев
H = [] # память: атаки, исходы, принятые правки
u = 0
for t in 1..T:
# АТАКА
(y, defect) = Атакующий.attack(Запрос, R, H)
# Напиши ответ, который проходит ВСЕ критерии R, но содержит ОДИН
# реальный дефект, серьёзно снижающий качество. Назови дефект.
full = Проверяющий.grade(y, R) # прошёл ли по всем критериям?
real = Независимый_судья.confirm(y, defect, Запрос)
# дефект действительно есть в y и действительно портит ответ?
R_new = R
if full == True and real == True:
# РЕМОНТ
R_new = Ремонтник.repair(R, y, defect, H)
# Критерии: атомарные, самодостаточные (все факты/числа внутри),
# непересекающиеся. Не ломать удачные старые критерии.
ok_score = Проверяющий.grade(y, R_new) < полный_балл
ok_review = Ревьюер.review(R_new, defect, R)
# дефект покрыт? старые валидные критерии и три свойства целы?
if not (ok_score and ok_review):
R_new = R # правка отклонена
H.append(y, defect, исход, принятая_правка)
u = 0 if R_new != R else u + 1
R = R_new
if u >= P: break
Что подставлять: {запрос_или_тип_задачи} — запрос или класс задач (ответ бота, текст КП, ревью кода). {факты_политика_стандарты} — внутренние правила и числа, чтобы критерии были самодостаточными. {макс_раундов} — 3–7. {терпение_раундов_без_правок} — 2–3.
🚀 Быстрый старт — вставь в чат:
Вот шаблон RubricArmor. Адаптируй под мою задачу: [опиши, ответы какого типа
нужно оценивать и для чего]. Задавай вопросы, чтобы заполнить поля.
[вставить шаблон выше]
LLM спросит, какой запрос или класс запросов вы оцениваете, какие факты и правила считаются истиной и что для вас «серьёзный дефект». Без фактов критерии получатся расплывчатыми, а метод строится именно на конкретных проверках. Она возьмёт структуру из шаблона и соберёт готовый промпт под вашу задачу.
Ограничения
⚠️ Исходная цель — обучение: авторы строят рубрики для обучения с подкреплением (RL). Вы можете взять рубрики для LLM-судьи, но в статье это не проверялось на бизнес-задачах напрямую.
⚠️ Проверка на парах: качество рубрики мерили тем, выбирает ли она лучший из двух ответов. Это хороший, но косвенный показатель.
⚠️ Дорого по токенам и времени: до 7 раундов и 5–6 вызовов на раунд. Для разовой простой проверки это избыточно.
⚠️ Одна модель во всех ролях: атакующий и судья одной модели имеют общие слепые зоны. Если судья ошибается вместе с атакующим, фиктивный дефект попадёт в рубрику.
⚠️ Субъективное и стиль: сильнее всего прирост на проверяемых задачах (математика, знания, рассуждения, код). На «чатовых» и стилевых запросах выигрыш скромнее, а обученные генераторы там конкурентны.
⚠️ Рост рубрики: с раундами критериев становится больше. Все равны по весу, поэтому раздутая рубрика может размыть важное.
Как исследовали
Команда взяла девять тестовых наборов из трёх бенчмарков (JudgeBench, RM-Bench, RewardBench) по знаниям, рассуждениям, математике, коду, чату и безопасности. В каждом примере есть запрос, хороший и плохой ответ. Рубрика считалась качественной, если по ней хороший ответ набирал больше баллов. Сравнивали с «прямой» генерацией (Direct), четырьмя известными методами улучшения списков (TICK, RocketEval, ChasingTail, RRD-Uniform) и тремя дообученными генераторами рубрик на 8B. Оценщиком везде был GPT-4.1-mini, а базовой моделью, генерирующей рубрики, — Gemini-3.1-Flash-Lite.
Результат: RubricArmor — 77,09 в среднем против 70,11 у Direct и 73,97 у сильнейшего конкурента. Он лучше Direct на всех девяти наборах. Дообученные генераторы оказались слабее Direct в среднем (64–68), поскольку привязаны к своим доменам. Интереснее всего абляции. Если атакующий лишь описывает дефекты без написания ответа, это 71,69. Если пишет ответ без умышленного дефекта и без подтверждения судьёй, это 72,80. Полный метод даёт 77,09, больше суммы двух вкладов. Идея в том, что цель «ищи дефект» определяет что искать, а конкретный ответ делает дефект проверяемым.
Метод проверили на пяти моделях (Gemini, GPT-4.1, DeepSeek, Qwen, GLM) и три набора. На всех он улучшил Direct, в том числе у слабых и сильных моделей. Например, у Gemini на JudgeBench точность выросла с 62,10 до 73,63 (+18,6% отн.).
Адаптации и экстраполяции
🔧 Техника: один раунд вместо цикла → дешевле. Оставь шаги «Атакующий → Судья → Ремонтник» один раз и убери память H. Для быстрых проверок хватает. Выигрыш будет меньше, чем в статье, но это не проверялось.
🔧 Техника: независимый судья → в отдельном чате. Вынеси Независимый_судья в новый чат или другую модель, передай только запрос, ответ и заявленный дефект. Это снижает шанс, что атакующий «уговорит» судью.
Экстраполяция: критерии приёмки в файл инструкций агента (CLAUDE.md, AGENTS.md). Эту идею нет в статье, это перенос принципа:
Перед тем как вернуть результат, составь критерии приёмки к задаче «{задача}».
Затем попробуй написать результат, который формально проходит все критерии,
но на деле плохой (одна реальная проблема). Если получилось — дополни критерии
и повтори. В конце проверь свой результат по финальным критериям.
Ресурсы
- RubricArmor: Adversarial Evolution Improves LLM-Based Rubric Generation
- Авторы: Haocheng Yang (NUS), Yuchao Zhang, Licheng Pan, Jiajun Fan, Maolin Wang, Kangning Zhang, Shuai Shao, Shijian Wang, Yuan Lu, Chunyuan Zheng, Hao Wang (MBZUAI) и др.
- Код: https://github.com/yhc-666/RubricArmor.git
- Упомянутые бенчмарки: JudgeBench, RM-Bench, RewardBench. Методы-конкуренты: TICK, RocketEval, ChasingTail, RRD-Uniform
