Parent Document Retriever: где теряется контекст ответа
Документ на 40 страниц режется на фрагменты по 300–500 токенов с перекрытием — иначе поиск не находит точных совпадений. Верхние 3–5 фрагментов уходят в генератор, и ответ собирается из обрывков: условие в одном куске, исключение к нему — в другом, а определение термина осталось за границей выборки. Формально каждый фрагмент релевантен запросу, фактически вывод неверен.
Полнота извлечения падает не из-за модели, а из-за нарезки. Релевантность фрагмента не равна полноте исходного раздела: абзац с ключевым словом в выборку попадает, а соседний, без которого он читается неправильно, — нет. На длинных документах — регламентах, договорах, инструкциях — так теряется до 19% нужных сведений, и это уже не редкий случай, а системное свойство схемы.
Цена сбоя проявляется не сразу. Первые ответы выглядят убедительно, проверять их никто не идёт, а потом юрист или инженер сверяет вывод с исходником и находит пропущенное исключение. После двух-трёх таких находок системой перестают пользоваться — и внедрение откатывается к ручному поиску по документам.
Закрывается это сменой единицы поиска. Parent Document Retriever ищет по мелким фрагментам, но в контекст отдаёт родительский раздел целиком: точность совпадения сохраняется, связность не рвётся, а длинные документы перестают распадаться на смысловые обрывки. Разбираем, как это устроено и какие параметры нарезки придётся пересобрать.
Что даёт иерархический поиск: эффект и границы применимости
Больше релевантного контекста в ответе
Схема Parent Document Retriever разделяет поиск и подачу: сравнение идёт по мелким фрагментам, а в модель уходит родительский блок целиком — с заголовком, таблицей и оговорками. Доля релевантных фрагментов в выдаче растёт на 15–19% на корпусах технической документации. Мелкий фрагмент точно ловит совпадение по смыслу, крупный не теряет условий, без которых ответ формально верен и по сути пуст.
Задержка ответа остаётся в том же классе
Векторный индекс строится по мелким фрагментам, поэтому число сравнений не меняется. Дополнительный шаг — чтение родительского блока из хранилища ключ-значение — тонет на фоне обращения к модели: разница видна на замерах, но не в работе пользователя.
Объём хранилища растёт только по тексту
Векторная часть считается один раз по мелким фрагментам и остаётся прежней. Растёт лишь текстовая часть: родительские блоки лежат в исходном виде, итоговый прирост обычно укладывается в 1.5–2 раза и дальше масштабируется вместе с корпусом, а не быстрее него.
Где иерархическое извлечение не выигрывает
Коротким однородным документам без структуры — регламент на страницу, карточка товара — группировать нечего: родительский блок совпадает с самим фрагментом. Приём раскрывается на длинных текстах с явной иерархией заголовков, где контекст рассеян по разделам.
Обычный Retriever против иерархии: сравнение подходов
Одноуровневый поиск режет документ на чанки и возвращает top-k ближайших по эмбеддингу. Схема предсказуема, пока ответ лежит внутри одного блока — этого хватает для FAQ и коротких статей. Когда ответ собирается из нескольких разделов, ограничения нарезки видны сразу: таблицы и списки рвутся по границам, соседние блоки теряют связь.
Иерархия документов меняет уровень поиска: находим мелкие блоки, а в контекст отдаём родительский фрагмент целиком. Гибридные схемы дополняют вектор лексическим поиском и реранкером. Ниже — чем эти варианты отличаются по применимости и что у каждого на выходе.
| Вариант | Когда подходит | Ограничения |
|---|---|---|
| Одноуровневый поиск по чанкам | Однородные короткие документы, ответ укладывается в блок | Теряет связи между разделами, проседает на составных вопросах |
| Иерархия с родительскими блоками | Регламенты, договоры, инструкции, где ответ разбросан по документу | Два уровня хранения; при правке переиндексируется вся ветка |
| Гибрид: вектор + лексика + реранкер | Терминология, артикулы, редкие названия, смешанные запросы | Нужны настройка весов и набор вопросов для замера, дольше отклик |
| Иерархия и реранк по родителям | Длинные документы с разнородными разделами | Самый тяжёлый вариант, требует контроля размера контекста |
Выбор определяется замером, а не описанием подхода: собираем вопросы, ответы на которые разбросаны по документу, и считаем context recall до и после смены схемы. На практике иерархия и гибрид дают прирост там, где одноуровневый поиск стабильно возвращает соседние, но не нужные фрагменты.
Этапы внедрения: от аудита корпуса до передачи
Порядок работ здесь задаёт не удобство, а качество результата: Parent Document Retriever хранит два уровня — мелкие фрагменты, по которым идёт поиск, и родительские документы, которые уходят в ответ. Если начать с настройки вместо разбора корпуса, родители получатся разнородными, и полнота контекста просядет на самых частых типах запросов.
-
Аудит корпуса. Считаем объём и языки, долю сканов без текстового слоя, дубли и устаревшие редакции, размечаем типы документов. На выходе — карта: что разбивать, что чистить, что исключить из поиска.
-
Схема уровней. Фиксируем иерархию «фрагмент → раздел → документ», наследование метаданных и уровень, который подаётся в ответ. Согласуем до настройки — иначе разбиение придётся переделывать вместе с переиндексацией.
-
Настройка поиска. Подбираем размер фрагмента и перекрытие, веса гибридного поиска, число кандидатов до и после переранжирования. Параметры выносим в конфигурацию, а не в код.
-
Проверка на отложенной выборке. Готовим вопросы, не участвовавшие в настройке, и прогоняем их на финальных параметрах. Так видно, где ответ опирается на найденный документ, а где модель отвечает по памяти.
-
Разбор провалов. Каждую ошибку относим к причине: родитель не найден, найден частично, помешали метаданные, вопрос вне корпуса. Дальше — правка разбиения, пополнение корпуса или явный отказ в ответе.
-
Передача решения и обучение команды. Отдаём конфигурации, схему уровней, порядок переиндексации, набор проверочных вопросов и регламент обновления корпуса. Разбираем всё с вашими инженерами, чтобы дальше работа шла без нас.
Что остаётся у команды: схемы, политики, регламенты
Внедрение заканчивается не «работающим поиском», а набором артефактов, по которым контур можно менять, откатывать и передавать другой команде. Ниже — что именно фиксируется в репозитории и рядом с ним, чтобы качество выдачи не держалось на одном инженере, который помнит, почему параметры выставлены именно так.
- Конфигурация поискового контура — в версионируемых файлах: размер чанка и окно перекрытия, правило сборки parent-блока, сколько кандидатов отдаётся на реранжирование, веса при смешивании лексического и векторного поиска. Любая правка проходит обязательное ревью и попадает в историю изменений.
- Схема связей блоков — как чанк ссылается на parent-документ, какие метаданные хранятся рядом (источник, раздел, дата актуальности), какие поля участвуют в фильтрах. С примерами запросов, чтобы связи не приходилось восстанавливать по логам.
- Политики доступа — разграничение по ролям на уровне чанков и parent-документов: кто видит закрытые разделы, кто только публичные. Проверка встроена в сам запрос к индексу, а не в постобработку готовой выдачи.
- Регламент обновления индекса — периодичность переиндексации, поведение при удалении и правке исходного документа, точка отката на предыдущую версию. Отдельно — метрика расхождения между источником и индексом.
- Набор проверочных вопросов — фиксированный список с эталонными ответами и разметкой нужных фрагментов. На нём после каждой правки конфигурации измеряется полнота выдачи, а не «стало вроде лучше».
- Регламент разбора ошибок — какие запросы считаются провалом поиска, как они попадают в проверочный набор, кто и с какой периодичностью его пересобирает. Плюс ответственный за решение по каждой категории сбоев.
- Документация и передача — описание схемы контура, точек расширения, инструкция по локальному запуску, журнал решений с обоснованием выбранных параметров. Новый инженер выходит на рабочий запуск без сопровождения автора.
Кейс: полнота извлечения контекста выросла на 19%
Корпус — нормативная документация: регламенты, методики, приказы с приложениями и перекрёстными ссылками. До внедрения поиск шёл по фрагментам фиксированной длины: нужный пункт находился, но терялись оговорки, область применения и исключения из соседних абзацев. Полнота извлечения держалась на 0,68 — примерно каждый третий запрос требовал ручной докрутки.
Перешли на двухуровневое извлечение: поиск идёт по мелким фрагментам, а в контекст модели подставляется родительский раздел целиком — подход Parent Document Retriever. Корпус пересобрали в иерархию «пункт → раздел → документ» с метаданными о редакции и области действия: фильтры по ним отсекают отменённые редакции, они больше не подмешиваются в выдачу.
Размер дочернего фрагмента подбирали под структуру текста, а не под фиксированное число знаков: пункт с подпунктами остаётся цельным. Верхнюю границу контекста ограничили, чтобы время ответа не росло линейно с длиной документа.
Метрики до и после
Полнота извлечения выросла с 0,68 до 0,81 — плюс 19% к базовому уровню на том же наборе запросов. Доля ответов со ссылкой на отменённый или несуществующий пункт упала почти втрое.
Поведение пользователей изменилось заметнее самих метрик: уточняющих вопросов внутри сессии стало на треть меньше, а доля случаев, когда сотрудник открывал первоисточник после ответа, сократилась вдвое. Ссылку проверяют, когда сомневаются, — падение этой доли и есть признак доверия к выдаче.
На коротких справочных запросах прирост почти незаметен — там хватает обычного поиска. Выигрыш виден на составных вопросах: ответ собирается из нескольких разделов, и важен каждый пропущенный пункт.
Что делать, если корпус уже разбит на мелкие фрагменты
Пока поиск работает, полная переиндексация не обязательна. Векторы мелких фрагментов остаются валидными — меняется только слой метаданных: к каждому фрагменту дописываем идентификатор родителя (путь документа, заголовок раздела, порядковый номер блока). Такой проход пересчитывает связи, а не векторы, и занимает часы вместо суток.
Родительский текст подтягивается в момент выдачи: запрос попадает в точный мелкий фрагмент, а в контекст модели уходит весь раздел, из которого он вырезан. Это Parent Document Retriever в прикладном виде — поиск по мелкому, ответ по крупному. Если поля родителя в схеме ещё нет, его добавляют миграцией метаданных без остановки поиска.
Миграция без простоя строится на двух параллельных индексах. Старый обслуживает трафик, новый наполняется связями в фоне, переключение идёт по совпадению выдачи на контрольном наборе запросов. Откат — возврат указателя: прежний индекс живёт до конца проверки.
Гибридная схема нужна, когда корпус неоднороден. Новые документы сразу индексируются парами «фрагмент — родитель», архив достраивается обходом по расписанию, поэтому свежие поступления не ждут обработки всего массива.
Полная переиндексация оправдана только при битом разбиении на фрагменты: куски рвут предложения, перекрытия нет, метаданные не несут структуры. Контроль результата — context recall на отложенной выборке реальных запросов и доля ответов, где родительский блок целиком уложился в окно контекста.
Частые вопросы инженеров: что ломается на практике
Метод закрывает конкретный класс сбоев: в индекс попадает короткий фрагмент, и ответ теряет детали, которые в документе были. Parent Document Retriever ищет по мелким фрагментам, а в контекст подаёт родительский документ целиком — в наших замерах полнота найденного контекста выросла на 19%. Цена у схемы тоже есть, и вопросы про неё инженеры задают первыми.
Не начнём ли мы подавать один и тот же текст несколько раз?
Да, если в выдачу попали разные фрагменты одного документа. Снимается дедупликацией по идентификатору родителя до сборки контекста: оставляем один экземпляр с наибольшим весом, а освободившееся место добираем документами, которые в выборку не попали. Ограничение метода — фиксированный бюджет контекста, поэтому дедупликацию и усечение считают вместе, а не по отдельности.
Насколько вырастет объём хранения?
Растёт, потому что рядом с фрагментом лежит его родитель. Относительно схемы с одними фрагментами это увеличение в один-два раза, точная величина зависит от длины родителя и перекрытия при нарезке. Управляется конфигурацией: родитель хранится в одном экземпляре, фрагменты ссылаются на него, редкие документы выносятся в холодное хранилище и не занимают быстрый индекс.
Как измерять качество, если эталонной разметки нет?
Полный набор вопросов с ответами стоит дорого и быстро устаревает. Берём косвенную метрику: генерируем вопросы из самих документов и смотрим долю случаев, где нужный фрагмент попал наверх выдачи, а его родитель доехал в контекст. Дополнительно сверяем на небольшой ручной разметке — нескольких десятков пар хватает, чтобы поймать системный провал, но мало, чтобы утверждать, что метод держится на редких запросах.
Обсудим задачу: как мы приходим к плану внедрения
План внедрения строится не на общих словах, а на вашей фактуре: без неё любая оценка сроков превращается в гадание. Поэтому обсуждение задачи начинаем с короткого перечня того, что нужно от вашей стороны, и сразу фиксируем, что вернём взамен.
- Выборка реальных запросов пользователей за последний период и документов, на которые эти ответы должны были опираться, — на ней проверяем, возвращает ли поиск родительский фрагмент целиком, а не обрывок фразы.
- Список мест, где сейчас уходит не туда: обращения в поддержку, ручные правки операторов, повторные уточняющие вопросы.
- Правила доступа к разделам базы — кому что видно и что нельзя выносить за пределы вашего контура.
- Эксперт с вашей стороны, который подтвердит корректность ответов на проверочном наборе и снимет спорные случаи.
- Числовые критерии приёмки: доля запросов, где нужный фрагмент попал в подборку, и доля ответов со ссылкой на источник.
- Ограничения по среде: контур размещения, объём базы, частота её обновления и способ синхронизации.
В ответ вы получаете оценку сроков по этапам, план внедрения и порядок пилотного запуска: сначала прототип поиска на вашей же выборке, затем пилот на одном подразделении с замером метрик, и только после этого — расширение на остальные команды. Каждый этап заканчивается проверяемым результатом и точкой возврата к предыдущему состоянию индекса, поэтому решение можно остановить без потери уже работающего поиска.
Опишите задачу — вернёмся с оценкой и планом.







