Когда речь заходит о RAG, обычно представляют простую схему: разбить документы на фрагменты, посчитать эмбеддинги, загрузить в векторную базу и подключить LLM. На демо это работает. Иногда срабатывает и в корпоративной среде. Но с нормативными документами такой подход быстро даёт сбой.
Мы столкнулись с этим на практике, создавая систему для работы с нормативной базой. Сначала задача казалась стандартной: есть документы, есть вопросы — нужен RAG. Но вскоре стало ясно: главная проблема не в генерации. Она в представлении документа так, чтобы retrieval не разрушал его структуру и смысл.
В итоге мы отказались от плоской индексации в пользу иерархических узлов, групп соседних пунктов, отдельного слоя терминов и графа обязательных связей между фрагментами.
- Нормативные документы плохо ложатся в модель «плоский набор чанков».
- Слишком маленькие фрагменты теряют контекст, слишком большие — дают шумный retrieval.
- Одного семантического поиска недостаточно: нужны точечный поиск по пунктам, работа с таблицами, терминами и кросс-ссылками.
- Один вопрос может требовать нескольких разных поисковых стратегий.
- Найденный пункт — это не всегда ответ: к нему нужно подтянуть связанные нормы.
- Поэтому документ пришлось моделировать не как текст, а как иерархический граф узлов и связей.
Где ломается обычный RAG
Сначала мы шли по привычному пути: разбивали документ на элементы, каждый делали единицей хранения, строили поверх векторный поиск.
Логика была проста: чем меньше фрагмент, тем точнее ссылка и меньше мусора в ответе. Но вскоре выяснилось, что минимальная единица хранения и минимальная единица смысла в нормативных документах часто не совпадают.
Проблема проявлялась так:
- пользователь спрашивает о конкретном требовании;
- semantic search находит похожий по формулировке пункт;
- но рядом есть подпункт, примечание или ссылка, без которых ответ становится некорректным;
- модель видит только один фрагмент и строит ответ, как будто этого достаточно.
На простых вопросах система справлялась. На сложных — где нужна доказуемая цепочка из нескольких норм — начинались сбои. Retrieval находил релевантный фрагмент, но не подтягивал обязательный контекст.
Тогда стало ясно: проблема не в «недоумной» модели. Проблема в слишком плоском представлении документа.
Первый поворот: документ перестал быть просто текстом
Вместо «документ = длинная строка» мы начали строить структурную модель. Основная идея: документ состоит не из абзацев, а из адресуемых элементов.
В нашей модели появились:
- документ как корневая сущность;
- узлы: разделы, пункты, подпункты;
- таблицы как отдельные элементы;
- формулы как отдельные элементы;
- связи между узлами;
- термины и их определения.
Сначала документ превращается в иерархию, а потом уже индексируется.
Это был первый важный шаг. Мы перестали индексировать «результат парсинга» и начали работать с нормализованной моделью документа.
Почему одного узла оказалось недостаточно
Даже с иерархией retrieval не стал автоматически хорошим.
Сначала мы рассматривали каждый элемент — пункт, подпункт, таблицу — как отдельный блок. Но оказалось, что одного фрагмента часто недостаточно. Он слишком узкий и теряет локальный контекст.
Например, вопрос не «где про эвакуацию», а «какими должны быть минимальные эвакуационные выходы». Семантический поиск может найти релевантный пункт, но для полного ответа нужно:
- увидеть соседний подпункт;
- подтянуть примечание;
- учесть таблицу;
- проверить ссылки на другие документы или пункты.
Retrieval должен возвращать не просто близкий фрагмент, а минимально достаточный контекст.
Мы не стали делать generic chunking по токенам. Вместо этого группировали соседние элементы с общим родителем и уровнем вложенности. Так система понимала, что это логически связанные пункты в одном разделе.
Размер фрагментов мы подбирали экспериментально. Идеального числа нет: слишком маленькие группы теряют смысл, слишком большие — снижают точность. Поэтому лучше использовать структурную группировку с настраиваемым лимитом по размеру.
Эффект был заметен: меньше стало ответов, где модель цеплялась за один «почти правильный» пункт и упускала окружение. В индекс попадали не только атомарные узлы, но и устойчивые смысловые блоки.
Почему семантического поиска оказалось недостаточно
Для свободных вопросов векторный поиск помогает. Но в нормативной сфере пользователи часто спрашивают конкретику:
- покажи пункт 6.2.2;
- найди таблицу 7.1;
- что означает термин «X»;
- где описан параметр Y;
- какие нормы связаны с этим пунктом.
Для этого одного vector search недостаточно. Нужны разные режимы retrieval:
- точечный поиск по номеру пункта;
- поиск по таблице;
- лексический поиск;
- гибридный поиск;
- терминологический поиск;
- поиск по кросс-ссылкам.
Retrieval перестаёт быть одной функцией и превращается в набор специализированных инструментов.
Почему один вопрос может запускать несколько поисков
Система не обязана ограничиваться одним способом поиска. Наоборот, один запрос может содержать несколько намерений:
- упоминание конкретного пункта;
- ссылка на таблицу;
- часть запроса в свободной форме;
- использование термина, требующего проверки в терминологическом слое.
В этом случае retrieval может:
- выполнить точечный поиск по пункту;
- параллельно проверить таблицу;
- запустить keyword search;
- обратиться к терминологическому слою;
- объединить результаты в единый контекст.
Это делает поисковый агент: он распознаёт намерения, приоритизирует их, запускает несколько поисков и собирает результаты.
Эта композиция резко повышает шанс найти не просто «что-то похожее», а действительно полезную комбинацию источников.
Зачем понадобился слой терминов
Пользователь и документ часто говорят об одном и том же по-разному: официальные термины, разговорные формулировки, сокращения, разрозненные определения. Без отдельного терминологического слоя система теряет часть релевантных ответов.
Мы добавили отдельную обработку терминов:
- извлекаем термины из документа;
- привязываем к каждому определение;
- индексируем термины отдельно;
- используем их как дополнительный сигнал в retrieval.
Это особенно важно, когда пользователь спрашивает не по номеру пункта, а по понятию из предметной области.
Момент, когда стало ясно, что без графа не обойтись
Даже иерархия и термины не решили всех проблем.
Некоторые ответы зависят от жёсткой цепочки связей. Например:
- пункт требует учёта другого пункта;
- ссылка на внешний документ;
- таблица актуальна только с описывающим её разделом;
- ограничение находится не в основном тезисе.
Семантический поиск может найти основной фрагмент, но не гарантирует подтягивание обязательного контекста. Иногда он слишком далёк по смыслу, чтобы попасть в top-k. Иногда формально не релевантен, но без него ответ ошибочен.
Поэтому нам понадобился граф связей.
В нём у узлов есть отношения:
- mandatory — узел обязательно учитывается вместе с другим;
- cross — кросс-ссылка на другой фрагмент или документ;
- связи документ → узел;
- связи узел → таблица;
- связи между документами.
Эти связи участвуют в retrieval. Если найден релевантный узел, система проверяет, нужно ли принудительно подтянуть связанные элементы.
В этот момент система перестаёт быть «чатом поверх документов» и становится инженерным инструментом.
Что это изменило на практике
Главное изменение — не в «умности» ответов, а в их устойчивости.
Система стала лучше справляться с задачами, где нужно:
- ссылаться на конкретный пункт;
- не терять уточняющий контекст;
- подтягивать обязательные связанные нормы;
- работать с таблицами;
- отличать термин от похожей фразы;
- строить ответ по цепочке, а не по одному фрагменту.
Для нормативных документов это важнее, чем «красивая» генерация. Даже убедительный ответ без обязательного контекста — плохой ответ.
Что мы из этого вынесли
Главный урок: в сложных документных доменах качество определяется не моделью и не промптом, а представлением данных.
В нашем случае это означало:
- документ нельзя рассматривать как плоский текст — нужна адресуемая структура;
- chunking должен учитывать смысловую иерархию, а не только длину;
- retrieval должен быть многослойным: vector search полезен, но не заменяет точечный поиск, лексические сигналы, термины, связи и композицию стратегий;
- обязательные связанные нормы должны быть выражены в данных явно — нельзя надеяться, что модель «сама догадается».
Мы начинали с идеи «построим RAG по документам». Но стало ясно: для нормативки этого недостаточно.
Чтобы система работала, пришлось пройти несколько этапов:
- от плоских фрагментов к иерархическим узлам;
- от одиночных чанков к структурным группам;
- от одного векторного поиска к гибридному retrieval;
- от простого индекса к терминологическому слою;
- от «похожего текста» к графу обязательных и кросс-ссылок.
В какой-то момент это перестаёт быть «LLM поверх базы» и становится полноценной инженерной системой, где retrieval — не вспомогательная деталь, а ядро качества.
Именно это, как нам кажется, и отделяет демонстрационный RAG от рабочего инструмента по нормативным документам.