Задача звучит скучно: находить противоречия между документами. Вышел новый приказ — какие из сотен внутренних регламентов ему теперь не соответствуют и где именно. Сегодня это недели ручной сверки, которую делают дорогие эксперты. И есть жёсткое условие: данные не покидают периметр компании. Никаких облачных API — только локальные модели на своём железе.
Я собрал и откалибровал такой контур за день — на домашнем стенде, в июле 2026-го. Это рассказ про то, что сработало, где я четыре раза наступил на грабли и почему главный герой этой истории — не нейросеть, а размеченный файл на сорок строк.
Почему не одна большая модель
Первый рефлекс — взять самую сильную доступную модель и спросить её. Проблема в том, что одна модель галлюцинирует уверенно и без предупреждения. В задаче, где цена пропущенного противоречия — предписание регулятора, «обычно отвечает правильно» не работает: нужно знать, когда именно ей нельзя верить.
Литература за последние два года сходится на другой схеме. Панель из трёх маленьких моделей разных семейств с простым голосованием обходит одного большого судью по согласию с человеческими оценками — при цене в семь-восемь раз ниже (PoLL, arXiv:2404.18796). Условие одно, и оно жёсткое: ошибки участников не должны быть скоррелированы. Свежие работы показывают, что у фронтирных моделей это условие всё чаще нарушается — девять судей из семи семейств несут информацию примерно двух независимых голосов (arXiv:2506.07962, arXiv:2605.29800). Модели разных вендоров ошибаются на одних и тех же примерах.
А модные многораундовые схемы — дебаты, рекурсивные рои агентов — на проверке фактов не окупаются: качество выходит на плато после одного-двух раундов, а при равном бюджете вычислений дебаты проигрывают простому голосованию (arXiv:2310.01798, arXiv:2311.17371); вдобавок давление «мнения большинства» заставляет модели сдавать правильные ответы (arXiv:2511.07784). Плюс каждый недетерминированный уровень убивает аудируемость — а в контуре, куда придёт служба безопасности заказчика, воспроизводимый след «кто проголосовал и почему» ценнее двух процентов метрики.
Итоговая схема — конвейер без рекурсии:
Панель — qwen3:4b, gemma3:4b и phi4-mini: три семейства (Alibaba, Google, Microsoft), суммарно около восьми гигабайт — все три сидят в видеопамяти одной консьюмерской карты одновременно. Судья — gpt-oss:20b, в панели не участвует. Каждый вердикт обязан опираться на дословную цитату из документа: не «модель считает», а «вот пункт, вот цитата, вот несовместимость».
День граблей
Дальше началась инженерия, и она честнее любых бенчмарков.
Грабли 1: модели не влезли. Первая версия панели была из моделей на 12–32 миллиарда параметров. На карте с 16 ГБ видеопамяти они не помещаются втроём, и на каждый вердикт среда перегружала модели заново: триста перезагрузок на один документ, таймауты, мусорные голоса. Лечение в два хода: тройка, которая целиком живёт в памяти одновременно, и обход «model-major» — одна модель прогоняет все пары, потом следующая. Три загрузки вместо трёхсот, ноль таймаутов.
Грабли 2: судья судил обрезки. Отчёт панели обрезал текст требований до 120 символов — «для читаемости». Судье доставалось требование без ключевых цифр: он честно сравнивал «180 дней» с «180 дней» и не находил противоречия там, где приказ требовал 90. Полнота на эталоне упала с 5/5 до 2/5. Урок дешёвый в постановке и дорогой в последствиях: валидируй вход каждого звена, а не только выход системы.
Грабли 3: двусмысленный boolean. Судья возвращал поле `confirmed: true/false` — и понимал его то как «подтверждаю противоречие», то как «подтверждаю, что всё в порядке». В тексте обоснования рассуждал правильно, а галочку ставил наоборот. Замена на явный enum `contradiction | no_contradiction` с определением каждого значения прямо в схеме — и стабильность вернулась. С маленькими моделями имя поля — это тоже промпт.
Грабли 4, любимые: я «улучшил» судью и уронил полноту с 0,88 до 0,53. Разобрав промахи, я написал судье новый промпт: алгоритм по шагам, четыре разобранных примера — красота. Прогон по эталону: полнота рухнула на 35 пунктов. Примеры заякорили модель на «скорее всё совместимо», и она начала отклонять настоящие противоречия пачками. Изменение не прошло гейт и не попало в конфигурацию. Это самый важный момент дня: без автоматического замера я бы вкатил эту «оптимизацию» с чистой совестью — она выглядела очевидно правильной.
Золотой стандарт — главный герой
Всё перечисленное поймал один и тот же инструмент — golden set: размеченный экспертом набор пар «требование ↔ пункт» с известными вердиктами. В моём случае он начался с 18 кейсов: 10 настоящих противоречий и 8 ловушек — пары, похожие тематически, но регулирующие разное; пары, где внутренняя норма строже требования (это не противоречие!); одинаковые числа о разных предметах.
Правило, которое я вывел за день: промпт — это тоже модель. Любое изменение — весов, промпта, состава панели, даже имени поля в схеме ответа — проходит прогон по эталону, и если полнота или доля ложных срабатываний хуже базовой линии, изменение не въезжает. Звучит как банальный CI, и это он и есть — только для поведения моделей.
Дальше эталон должен расти сам: каждый вердикт эксперта в бою — новый кейс разметки. А чтобы набор не был игрушечным, я натравил на задачу рой агентов: они сгенерировали 42 кейса-ловушки в четырёх областях — охрана труда, промбезопасность, закупки и ИБ — спроектированные против известных слабостей пайплайна. На этом незнакомом наборе система показала полноту 0,88 при 4 % ложных срабатываний. Не 10/10 с витрины — зато правда, и теперь есть что улучшать измеримо.
Слепое пятно семейства
Самая поучительная находка дня — не про пайплайн, а про разметку.
Кейсы для эталона генерировала одна модель, а проверяли — два независимых агента… того же семейства. Каждый кейс прошёл двойную проверку. Затем я отдал те же 42 кейса на аудит модели другого семейства — и она нашла три ошибки разметки. Все три — одного типа: перепутано направление строгости. Направление строгости в голове генератора плавало: то строгую норму он записывал в противоречия, то мягкую — в совместимые. И оба проверяющих соглашались, потому что думали так же.
Генератор и верификаторы одного семейства разделяют слепые пятна — та самая скоррелированность ошибок из статей, только не в бенчмарке, а в моих собственных данных. Правило с сегодняшнего дня: разметка утверждается только после аудита моделью другого семейства. Стоит копейки, ловит систематическое.
Куда это растёт
Контур измерения и контур улучшения дальше работают в паре. Метрики каждого прогона — доля ошибок, доля разногласий панели, доля вердиктов судьи «против» панели, дословность цитат — уже поймали за день две настоящие проблемы. Ошибки панели, отклонённые судьёй, автоматически становятся контрпримерами для промпта. А самое интересное — следующий уровень: LLM не решает задачу — LLM пишет код, который её решает. Перевод единиц («3 месяца» против «180 дней»), сравнение направленных норм («не реже», «не позднее»), таксономия предметов — всё это детерминированные проверки, которые генерируются из накопленной разметки, покрываются тестами из того же эталона и дальше работают без нейросети вообще: быстрее, дешевле, и аудит правила — это чтение его кода.
Принципы, которые я забираю с собой
- Панель из трёх маленьких разных моделей ≥ один большой оракул — если ошибки не скоррелированы. Больше трёх семейств не нужно.
- Рекурсия и дебаты на проверке фактов не окупаются. Конвейер аудируем, рой — нет.
- Вердикт без дословной цитаты — не вердикт.
- Промпт — это модель. Любое изменение — через прогон по эталону с гейтом.
- Валидируй вход каждого звена. Судья, получивший обрезок, хуже отсутствия судьи.
- Разметку одной модели аудирует модель другого семейства. Всегда.
- Всё, что решается флоучартом, со временем должно решаться кодом, а не нейросетью. LLM — генератор этого кода, а не его замена.
Запустилось — не значит работает. Работает — то, что измерено на эталоне, который вы не показывали системе заранее.