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

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

В конце июля 2025 года я запустила свой первый вайбкодерский проект: бот, который проверяет рекламные креативы на соответствие ФЗ О рекламе. Я сразу замахнулась делать бота с RAG по решениям ФАС, так как мне показалось, что только так можно добавить ценности к выполнению этой задачи просто с нейронкой в чате. Я делала UX/UI бота и архитектуру RAG-поиска буквально по наитию и совещаниям с тогда ещё Gemini 2.5 Pro,  о чем подробно писала вот здесь

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

RAG в боте устроен абсолютно примитивно: берётся описание креатива, превращается в вектор, сверяется с векторами всех саммери решений ФАС, что есть в базе на дату Х. Сверху снимаются десять лучших по косинусному сходству. Ни реранкера, ни гибридного поиска, ни дополнительных фильтров (кроме указания нейронке на этапе генерации отнестись критично и самой выбрать реально релевантное) — ничего из того, что прилично довешивать к RAGам. И уж тем более никакой иерархичности, карточек-сателлитов для каждого чанка, и прочего RAG-креатива на юридических данных, прорву которого можно найти на архиве (я даже пыталась обзирать что-то из этих статей, но их действительно очень много, а добавленная ценность у придумок — копеечная). 

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

Гипотезы

Если вы когда-нибудь заглядывали в саму базу решений ФАС (она открыта и доступна для некоммерческого использования), вы видели, что для каждого дела есть две разметки: теги по номерам статей ФЗ О рекламе и тематические теги. Мне с самого начала казалось разумным использовать эти метки для реранжирования (отсечки малорелевантных и добора более релевантных дел). Всего в этой связи я проверила 4 гипотезы, а потом бонусом ещё одну немного из другой оперы. 

Говоря «я проверяла», я имела в виду проверку с помощью Opus 5 в Claude Code CLI. На все прогоны ушло буквально полчаса, что, конечно, совершенно нельзя было себе представить год назад, когда я собирала бота. 

Метод проверки всех гипотез был один: каждое дело из базы проверяется против всей базы, становясь запросом + взяли ещё 77 случайных дел из логов. При проверке смотрели, насколько совпадают тематические теги найденных топ-10 дел с тегами запроса и какова доля дел, совпавших с запросом по статье ФЗ О рекламе (кроме ст. 5, поскольку у ФАС есть привычка дописывать ст. 5 к большинству дел даже со специальным составом. Так, ст. 5 покрывает три четверти базы и поэтому ничего не различает).

Если дальше по ходу текста вы поймёте, что не понимаете какие-то термины, вернитесь сюда и почитайте установочную статью everybody talks about RAG.

Гипотеза 1

Добирать дела с теми же тематическими тегами, которые присвоены топ-5 кейсам, определённым косинусным сходством. Логика была какая-то такая: «если наверху дела про умолчание существенных условий и банки, то креатив, скорее всего, про это — значит, нужно ещё таких дел». 

На замере выяснилось, что это и так уже происходит само семантическим поиском. Из пяти мест с шестого по десятое в среднем 4,2 уже заняты делами с теми же тегами, что у топ-5: косинус приносит их и так, просто потому что дела про одно нарушение и написаны похоже. 

Гипотеза 2

Поднимать в выдаче дела по тем же статьям закона, что спрогнозировал препроцессинг. В самом свежем апдейте бота препроцессинг теперь ещё даёт предварительную квалификацию по применимым статьям ФЗ О рекламе (не по своей памяти, а по справочнику в 23 заголовка, до уровня номера статьи). Это нужно, чтобы тянуть в промпт актуальный текст статьи и перестать полагаться на безнадёжно отстающий от реальности гугловский knowledge cut-off. Логичным показалось поднимать в выдаче дела, квалифицированные по тем же статьям.

Оказалось, что стандартная работа эмбеддера и так приносит в топ-10 всё, что нужно: 9,6 дел из 10 совпадают с запросом по статье. У 85% запросов выдача не поменялась, если прикрутить туда поднятие в выдаче по номеру статьи.

Гипотеза 3

А что если препроцессинг будет предсказывать не только номера статей, но и тематические теги? Тематические теги отражают более мелкое деление статей. Это единственная гипотеза, которая дала прирост: 78,8% против 69,8%, плюс 9 пунктов. Ура, берём в прод??

Но это потолок при идеальном предсказании. Предсказание было идеальным, потому что мой инструмент замера гипотез знал полный словарь тематических тегов, и какие теги уже фактически присвоены для каждого дела. Даже если дать препроцессингу полный словарь тегов (а их ни много ни мало 132), такой детерминированности от нейросети я всё равно не получу, и вообще эта задача может стать мощным дистрактором от основных целей препроцессинга. Короче, прибавка на 9 пунктов при идеальных условиях как будто совершенно не стоит калибровочных усилий и траты времени на экспертные тесты. Вот прям то самое «лучшее — враг хорошего». 

Гипотеза 4

Не давать выдаче схлопываться в одну тему. Этакий ответ на главный блокер гипотезы 1. Что, если в выдаче будет не больше трёх дел с одним тематическим тегом?

Стало хуже. Совпадение по редкой статье упало с 69,8% до 61,2%. Квота выбивает из выдачи дела, которые совпадали с запросом по содержательной норме, и ставит на их место что-то с двадцатых-сороковых мест.

Гипотеза 5

Добавить поиск по ключевым словам. Отбросив идею с тегами, я подумала — а почему у меня вообще не гибридный поиск-то? Классический BM25 и классический семантический, нечто подобное реализовано в поисковике по практике ФАС. Оказалось, что абсолютно любой микс портит выдачу даже при весе BM25 в 0.1.

Почему наивный семантический поиск без наворотов работает лучше всего?

Как так вышло, что самый просто организованный RAG-пайплайн, не использующий даже библиотек работы с векторными базами данных, работает настолько хорошо для этой задачи? 

Дизайн базы в двух аспектах: 

  1. Нет чанкинга. 2064 вектора на 2064 дела: одна запись — одно дело. Запись — не кусок сырого текста решения, а готовая структурированная выжимка, которую сделала LLM. А все классические сложности RAG, особенно болезненно проявляющие себя на юридических данных — это по большей части последствия нарезки. То чанк лёг поперёк двух тем, и вектор не описывает ни одну, то полчанка занимает вводная часть документа с пересказом примерно одинаковой на тысячу кейсов процессуальной истории. Все инструменты типа оверлэпа и реранкинга со всевозможными их наворотами пытаются ремонтировать именно это, но саммери-подход просто снимает проблему.
  2. База узкая. Весь корпус умещается в коридор шириной 0.15 по косинусу, то есть в пространстве эмбеддингов самые далеко расположенные друг от друга тексты имеют векторы 0.762 и 0.915. Жанр, стиль и предметная область постоянны, вся оставшаяся вариация — тематическая (алкоголь vs кредиты vs БАДы). Эмбеддер ни на что не отвлекается и ищет, в сущности, только по теме (что мне и нужно).

И пайплайн бота в трёх аспектах:

  1. Препроцессинг и приближение текста запроса к тексту базы. В поиск передаётся не полностью сырой пользовательский текст или картинка, а предварительно обработанный текст, то есть «описание рекламы» сопоставляется с «описанием рекламы плюс что с ней не так по мнению ФАС». Это суррогат query rewriting, которое в промышленных стеках добавляют отдельно и специально для повышения качества выдачи. Я же его добавляла с мотивацией делать акценты на том, что действительно важно при оценке рекламного креатива (т.е. что не пропустить на генерации), а хорошо сработало ещё и на ретрив. 
  2. То, что я считала костылем: просьба LLMке на этапе генерации самой определять релевантность и использовать для заключения только наиболее подходящее к креативу. Модельке вообще неважно, в каком порядке выдача, и не попал ли туда мусор: ненужное она просто проигнорирует. А топ-10 дел в целом достаточно для качественного заключения. 
  3. Жестко заданный утилитарный сценарий: мы решаем одну единственную задачу, и только её. Мы только проверяем креативы, и только по двум крупным группам параметров (соответствие закону и соответствие практике), и в таком детерминированном сценарии может хорошо функционировать и достаточно примитивная технология. 

Неужели ты хочешь сказать, что решает не уровень технологии, а обвязка и данные?

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

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

Но этот рецепт годится для индивидуальных юристов и небольших команд, чувствующих в себе техноэнтузиазм. Если вы решили быть ИИ-лигалтех-предпринимателем, то придётся искать более сложные и продуманные технологические решения даже при таких очевидных вводных как неоднородность текстов в базе (где тексты разных тем и разных форматов) и неоднородность пользовательских запросов и ожидаемых сценариев. Впрочем, появились агенты… которые повышают требования к качеству исходных данных. И вот мы закольцевались!)