Рубрика як артефакт: як ми вчимо LLM-суддю не брехати

ДОСЛІДЖЕННЯ9 хв читання10 квітня 2026 р.

Коли ми починали merged, перша версія "оцінки PR-а" виглядала так: беремо diff, кидаємо в Claude, просимо поставити оцінку від 1 до 10 з поясненням. Працювало. Поки ми не перевірили.

Узгодженість з людським ревʼюером — 41%. Модель ставила різні оцінки одному й тому ж PR-у в сусідніх запитах. Найгірше: вона впевнено пояснювала свої оцінки навіть тоді, коли вони були випадковими.

Сьогодні наш LLM-суддя дає 87% узгодженості з сеньйором. Без перенавчання моделі. Усе — в промпті і рубриці.

Рубрика — це схема, а не чек-лист

Перша помилка — думати про рубрику як про список критеріїв. Це спокусливо, бо так воно виглядає на папері. Але для моделі критично, як саме декомпозовані критерії і які дані вона може повернути.

Ось як виглядає один критерій у нашій рубриці (фрагмент):

id: test_coverage_behavior
weight: 0.15
question: |
  Чи тести перевіряють поведінку, а не реалізацію?
  Сигнали "так": перевірка зовнішнього контракту, edge-кейси,
  regression-тест на баг. Сигнали "ні": перевірка приватних
  методів, mock-верифікація замість асерту на результат.
output_schema:
  score: { type: int, min: 0, max: 4 }
  evidence: { type: string, min_words: 15 }
  counter_evidence: { type: string, required: true, min_words: 10 }
  confidence: { type: enum, values: [low, medium, high] }

Критичне — counter_evidence. Ми вимагаємо від моделі написати, що саме свідчить проти її ж оцінки. Цей єдиний field дав нам +14 процентних пунктів узгодженості.

Чому contrastive reasoning працює

Коли ми просимо LLM "поясни оцінку 4", вона пояснює оцінку 4. Коли ми просимо "поясни, чому оцінка не 5, але й не 3" — вона змушена зважувати. Модель не може одночасно бути впевненою і писати contrastive evidence у протилежних напрямках — це викликає внутрішній dissonance на етапі генерації.

Це не magic prompting. Це робить структура задачі для моделі іншою: вона перетворюється з задачі "оціни" на задачу "класифікуй у межах шкали з обґрунтуванням границь".

Архітектура LLM-судді

Наш pipeline виглядає так:

          ┌──────────────┐
PR diff → │ normalizer   │ → canonical form
          └──────────────┘
                 │
          ┌──────▼───────┐
          │ per-criterion│  × N criteria (parallel)
          │  LLM call    │
          └──────┬───────┘
                 │
          ┌──────▼───────┐
          │ aggregator   │ → final score + detailed report
          └──────────────┘

Normalizer

Перший виклик переводить diff у канонічну форму: нормалізує імпорти, прибирає trailing whitespace, згортає дуже довгі рядки, витягує метадані комітів. Це прибирає близько 30% варіативності відповідей моделі на однаковому семантичному контенті.

Per-criterion calls

Замість одного виклику "оціни PR" ми робимо N паралельних викликів — по одному на критерій. Кожен виклик бачить тільки релевантний контекст для свого критерію. Оцінка commit_message_quality не бачить коду — тільки git log. Оцінка test_coverage не бачить commit-повідомлень — тільки тести і код, який вони покривають.

Це різко знижує inter-criteria leakage: коли модель ставить 4/5 за коміти і потім "докручує" оцінку тестів, бо "ну він же старався".

Aggregator

Окремий виклик бачить усі per-criterion результати і не має права змінювати оцінки — тільки агрегує по вагах і пише підсумковий наратив. Це архітектурне обмеження.

Де ми системно помиляємось

За 14 місяців продакшну зібрали каталог регресій:

  1. Culture/style bias. Модель вище оцінює код у стилі, на якому вона більше навчалась. Функціональний TS з lodash — так. Імперативний Go з явними for-циклами — нижче, хоча це часто ідіоматичніше.
  2. Verbose commits advantage. Коміти з повними реченнями отримують вищий commit_message_quality, навіть коли fix: handle UA VAT edge case — точніше за абзац маркетингового тексту.
  3. Refactor-blindness. Pure refactor PR-и (переіменування, витягання функцій) отримують нижчу оцінку за "фокус диффу", бо таких змін багато за обʼємом. Ми додали refactor_intent як окрему гілку рубрики.

Для кожного з цих багів — калібрувальний сет на ~200 PR-ів, розмічений двома сеньйорами, і периодичний regression-прогін.

Що саме комерційно нетривіальне

Це не RAG. Це не fine-tune. Це не agent framework з 14 tools. Це одна ключова ідея: якщо ти хочеш від LLM надійне судження — роби його схемою, а не текстом. Дай моделі структурований output. Вимагай contrastive reasoning. Сегментуй контекст. І ніколи не вір першій цифрі, яку вона поверне.


У наступній статті — як ми калібруємо задачі під різні рівні (middle / senior / staff) і чому задача для сеньйора не повинна бути складнішою, а має бути іншою за природою.

#рубрика#llm-as-judge#оцінка#evaluation#bedrock

Поділитись

Спробувати merged

Технічний скринінг без співбесід

Калібрована задача, автоматична рубрика, звіт за ~2 хвилини. Закрита бета, Q2 2026.