RAG для страховых документов: почему стандартный чанкинг не работает и как это починить
Стандартный чанкинг по размеру уничтожает контекст юридических документов. Разбираем, почему RAG ломается на страховых полисах и как решить проблему семантическим чанкингом и иерархическими эмбеддингами.
Проблема, которую не видно на демо-датасетах
Когда строишь RAG-систему на блог-постах или документации, всё работает из коробки. Берёшь RecursiveCharacterTextSplitter, ставишь chunk_size=1000, overlap=200 — и получаешь приемлемые ответы. Проблема начинается, когда на вход приходят документы с внутренней перекрёстной логикой.
Страховой полис — идеальный пример такого документа. Я построил прототип RAG-системы, где пользователь загружает PDF страхового полиса и задаёт вопросы о покрытии на обычном языке. Звучит просто. На практике первая версия с фиксированным чанкингом выдавала ответы, которые были технически корректны, но контекстуально неполны — и это хуже, чем откровенно неправильный ответ.
Почему фиксированный чанкинг ломается на юридических документах
Страховой полис — это не линейный текст. Это система перекрёстных ссылок:
- Пункт в разделе 4 может иметь смысл только при прочтении определения из раздела 1
- Исключение из раздела 9 может полностью отменять покрытие, описанное в разделе 3
- Термин «застрахованное лицо» в одном полисе означает держателя полиса, в другом — любого члена семьи
Фиксированный чанкинг разрезает документ по количеству символов. Ему всё равно, что определение термина оказалось в одном чанке, а условие, использующее этот термин — в другом. При retrieval модель получает фрагмент с условием, но без определения. Ответ получается формально правильным, но вводящим в заблуждение.
Это критично именно в страховании. Человек спрашивает: «Покрывает ли мой полис повреждение от наводнения?» Система находит чанк, где упоминается покрытие стихийных бедствий, и уверенно отвечает «да». Но исключение для наводнений находится в другом чанке, который не попал в контекст.
Первый шаг: структурный анализ документа
Решение начинается до чанкинга. Перед тем как что-либо разрезать, нужно понять архитектуру документа.
Я добавил предварительный проход, который:
- Определяет тип полиса — автострахование, здоровье, имущество, ответственность
- Картирует границы разделов — находит заголовки, нумерацию, структурные маркеры
- Категоризирует каждый раздел по функции: Coverage (покрытие), Exclusions (исключения), Definitions (определения), Claims (процедура обращения), Conditions (условия)
Этот проход можно реализовать как через LLM (отправляем весь документ с промптом для структурного анализа), так и через правила — большинство страховых документов следуют стандартным шаблонам с узнаваемыми заголовками.
Результат — карта документа, которая говорит: «Раздел 1 (строки 1-45) — определения, раздел 2 (строки 46-120) — покрытие, раздел 3 (строки 121-180) — исключения».
Второй шаг: иерархический чанкинг parent-child
Когда структура документа понятна, чанкинг становится осмысленным. Я использовал двухуровневый подход:
Parent chunks (родительские чанки): содержат целые разделы документа, обеспечивают контекст, используются для формирования полного ответа.
Child chunks (дочерние чанки): содержат отдельные пункты и условия, обеспечивают точность при поиске, ссылаются на родительский чанк.
Каждый чанк несёт метаданные: к какому типу раздела он относится, номер раздела, тип полиса. Это не просто текст — это текст с контекстом.
При поиске система сначала находит релевантные child-чанки по запросу, затем подтягивает их parent-чанки для полноты контекста. Если пользователь спросил про франшизу — child-чанк с конкретным пунктом про франшизу найдётся точно, а parent-чанк с полным разделом условий добавит необходимый контекст.
Третий шаг: intent classification перед retrieval
Ещё один слой, который значительно улучшил качество — классификация намерения запроса до обращения к векторному хранилищу.
Когда пользователь спрашивает про франшизу, ему нужны чанки из разделов Conditions и Coverage, но не из Exclusions и Claims. Без intent classification вектор может вернуть семантически похожие, но функционально нерелевантные фрагменты.
Работает так:
- Запрос пользователя классифицируется по категориям: вопрос о покрытии, об исключениях, о процедуре, об условиях
- При retrieval фильтруются чанки по метаданным типа раздела
- Если запрос касается покрытия — обязательно подтягиваются связанные исключения (это критически важно)
Третий пункт — ключевой. Нельзя отвечать на вопрос о покрытии, не проверив исключения. Система должна знать эту доменную логику.
Четвёртый шаг: confidence scoring
Один из важнейших элементов, который я добавил поздно, но рекомендую закладывать сразу — оценка уверенности.
Если извлечённые чанки слабо поддерживают ответ, система должна честно сказать: «У меня недостаточно информации для точного ответа. Рекомендую проверить раздел X вашего полиса или обратиться к агенту.»
В страховании плаузибельный, но неточный ответ может привести к реальным финансовым потерям. Лучше сказать «не знаю», чем соврать убедительно.
Реализация:
- Считаем cosine similarity между запросом и возвращёнными чанками
- Если максимальный score ниже порога — помечаем ответ как low confidence
- Добавляем в ответ дисклеймер и ссылку на конкретный раздел документа
Практический пайплайн
Собирая всё вместе, пайплайн выглядит так:
PDF → Извлечение текста → Структурный анализ → Категоризация разделов → Иерархический чанкинг (parent + child) → Эмбеддинги с метаданными → Индексация в векторное хранилище
Запрос пользователя → Intent classification → Фильтрованный retrieval → Parent context enrichment → Confidence scoring → Генерация ответа
Каждый шаг решает конкретную проблему:
- Структурный анализ — чтобы не терять связи между разделами
- Иерархический чанкинг — чтобы сохранить и точность, и контекст
- Intent classification — чтобы не тащить нерелевантные фрагменты
- Confidence scoring — чтобы не врать, когда данных недостаточно
Какие документы требуют такого подхода
Страховые полисы — не единственный тип документов, где стандартный чанкинг ломается. Та же проблема возникает с:
- Договорами — перекрёстные ссылки, определения в преамбуле, исключения в приложениях
- Нормативными актами — статьи ссылаются друг на друга, подзаконные акты уточняют законы
- Технической документацией — спецификация в одном разделе, ограничения в другом
- Медицинскими протоколами — противопоказания, взаимодействия, условия применения
Общий признак: документ имеет внутреннюю структуру со смысловыми зависимостями между частями. Если разрезать такой документ по размеру — теряется логика.
Выводы
Стандартный чанкинг по размеру — это инструмент для простых текстов. Для документов с внутренними зависимостями он создаёт иллюзию работающей системы: ответы выглядят правильно, но контекстуально неполны.
Три вещи, которые стоит внедрить при работе с юридическими документами в RAG:
- Структурный анализ до чанкинга — поймите архитектуру документа прежде чем его резать
- Иерархические чанки с метаданными — parent для контекста, child для точности
- Intent classification + confidence scoring — не тащите всё подряд и не выдумывайте ответы
RAG — не универсальный молоток. Качество retrieval определяется качеством подготовки данных. И для юридических документов эта подготовка требует понимания доменной логики.