LLM API и интеграция
RAG: как сделать поиск по своим документам
Самая частая задача, с которой приходят к моделям: «пусть отвечает по нашим документам». И самое частое заблуждение при этом — что для этого нужно дообучение. Нет: модель не должна знать ваши данные, она должна получить нужный кусок в контексте прямо перед ответом. Это работает сразу, обновляется мгновенно и не требует ни размеченной выборки, ни бюджета на обучение. Но собрать такую систему сложнее, чем кажется по описанию, и ломается она почти всегда в одном и том же месте — причём не там, где её чинят. Разбираем цепочку целиком, от нарезки документов до подстановки в промпт.
Из чего состоит цепочка
Четыре шага, и каждый следующий зависит от предыдущего.
Разбиение. Документы режутся на фрагменты. Модель не может получить всю базу, значит нужны куски разумного размера.
Поиск. По запросу пользователя из всех фрагментов отбираются похожие. Обычно это сравнение числовых представлений, посчитанных заранее.
Упорядочивание. Кандидаты сортируются по релевантности точнее, чем это сделал быстрый поиск.
Подстановка. Лучшие фрагменты кладутся в контекст вместе с вопросом, и модель отвечает.
Ключевое свойство конструкции: ошибка на раннем шаге не исправляется на позднем. Если нужный фрагмент не попал в кандидаты, никакое упорядочивание его не создаст, и никакая сильная модель не ответит по тому, чего не видит.
Почему ломается разбиение
Здесь причина большинства неудачных внедрений, и чинят обычно не это.
Типичный подход: резать документ на куски фиксированной длины. Просто, быстро и почти всегда плохо. Причины три.
Разрыв смысла. Ответ на вопрос оказывается разрезан пополам: начало в одном фрагменте, конец в другом. Ни один из них по отдельности не отвечает на вопрос, и ни один не выигрывает поиск.
Потеря контекста. Фрагмент из середины договора не содержит информации о том, к чему он относится. «Срок составляет тридцать дней» — срок чего, в каком документе, для какой стороны.
Смешение тем. Кусок фиксированной длины захватывает конец одного раздела и начало другого. В поиске он выглядит похожим на многое и не отвечает ни на что.
Что помогает: резать по смысловым границам — разделам, пунктам, абзацам, — а не по числу символов. И добавлять к каждому фрагменту заголовок раздела и название документа: пара строк контекста радикально улучшает и поиск, и ответ.
Проверить, ваша ли это проблема, просто: возьмите двадцать вопросов, для которых знаете правильный ответ, и посмотрите, есть ли нужный фрагмент среди кандидатов. Если нет — чинить надо разбиение, а не модель.
Что делает упорядочивание
Третий шаг стоит объяснить, потому что его часто пропускают.
Быстрый поиск сравнивает запрос и документы по представлениям, посчитанным независимо друг от друга. Это позволяет искать среди миллионов фрагментов мгновенно, но сравнение получается грубым.
Переранжировщик смотрит на пару «запрос и фрагмент» вместе и оценивает, отвечает ли этот кусок именно на этот вопрос. Дороже по вычислениям, поэтому применяется не ко всей базе, а к отобранным кандидатам.
Схема получается такой: поиск отбирает полсотни из миллионов, переранжировщик точно сортирует эти полсотни, в модель уходят первые три.
В нашем каталоге есть модели переранжирования с нулевой ставкой — то есть этот шаг можно добавить, не увеличивая счёт. Более того, он счёт уменьшает: подставляя три уверенных фрагмента вместо десяти на всякий случай, вы платите меньше за входные токены основной модели.
Сколько подставлять
Практический вопрос с контринтуитивным ответом.
Соблазн понятен: положить побольше, пусть модель разбирается. Так делать не стоит по двум причинам.
Лишний контекст разбавляет нужный. Модель, получившая три релевантных фрагмента и десять посторонних, отвечает хуже, чем получившая только три. Механику разбирали в материале про то, сколько контекста реально нужно.
Каждый лишний фрагмент оплачивается при каждом запросе. На объёме разница между тремя и десятью фрагментами — это разница в счёте втрое.
Разумный ориентир — три-пять фрагментов. Если для ответа нужно больше, скорее всего проблема в разбиении: смысл размазан по слишком мелким кускам.
Почему не дообучение
Раз это первое, что приходит в голову, разберём сравнение честно.
Подстановка выигрывает по скорости изменений. Документ поправили — фрагменты пересчитали, и через минуту модель отвечает по новому. Дообучение требует повторения цикла.
Подстановка выигрывает по проверяемости. Видно, какие именно фрагменты легли в контекст, и можно показать пользователю источник ответа. У дообученной модели знание размазано по весам, и на вопрос «откуда ты это взял» ответа нет.
Подстановка выигрывает по стоимости входа. Не нужна размеченная выборка, не нужно железо, не нужен специалист по обучению.
Дообучение выигрывает там, где нужен стиль, а не факты. Устойчивая манера изложения, отраслевой жаргон, формат, который трудно описать словами. Это не подставляется в контекст — это свойство модели.
Практический вывод: если задача формулируется как «отвечай по нашим документам», нужна подстановка. Если как «отвечай в нашем стиле» — возможно, дообучение. Смешивать не стоит: мы разбирали лестницу от промпта к обучению, и в ней подстановка стоит существенно раньше.
Что логировать
Практическая мелочь, которая превращает отладку из гадания в работу.
Минимальный набор на каждый запрос: текст запроса пользователя, список фрагментов-кандидатов с оценками, что из них попало в контекст, и итоговый ответ.
Без первых трёх полей вы не сможете ответить на вопрос «почему модель ответила неправильно», потому что не будете знать, что она вообще видела. А это самый частый вопрос при эксплуатации таких систем.
Отдельно стоит хранить, какая версия базы использовалась. Документы меняются, и ответ, верный неделю назад, сегодня может быть неверным — не потому что модель испортилась, а потому что источник обновился.
И считать расход по строкам: сколько токенов ушло на подставленные фрагменты, сколько на системный промпт, сколько на историю. Обычно выясняется, что фрагменты составляют большую часть входа, и именно их сокращение даёт основную экономию.
Чего эта конструкция не даёт
Границы, чтобы не разочароваться.
Она не заменяет знания модели. Подстановка добавляет факты, но рассуждает модель своим умом. Слабая модель с идеальным поиском не станет сильной.
Она не гарантирует, что модель использует поданное. Модель может проигнорировать фрагмент или додумать сверх него. Требование отвечать только по поданным материалам помогает, но не абсолютно.
Она не решает вопрос свежести сама по себе. База обновляется вашим кодом; если документ изменился, а фрагменты не пересчитаны, модель ответит по старому.
Она добавляет поверхность для атаки. Всё, что попало в контекст, модель читает как часть задачи. Если в базу может писать кто-то извне — пользователи, партнёры, автоматический сбор с сайтов, — это готовый канал для инструкций, и защита строится не на фильтрации текста, а на ограничении прав того, что модель может сделать после его прочтения — почему именно так.
Как понять, что не работает
Диагностика по шагам, от дешёвого к дорогому.
Проверьте, есть ли нужный фрагмент в базе вообще. Поиском по тексту, без всяких моделей. Отсутствие — проблема сбора данных.
Проверьте, попадает ли он в кандидатов. Если нет — проблема разбиения или поиска.
Проверьте, попадает ли он в первые три после упорядочивания. Если нет — нужен переранжировщик или он настроен неудачно.
Проверьте, использует ли его модель. Если фрагмент подан, а ответ мимо — проблема в промпте или в выборе модели.
Порядок важен: три четверти неудач обнаруживаются на первых двух шагах, а чинят обычно четвёртый, меняя модель на более дорогую. Это самый затратный способ не решить проблему.
Что держать в голове
Ответы по своим документам — это не про обучение модели, а про то, чтобы вовремя подложить ей нужный кусок текста.
Цепочка ломается почти всегда на разбиении, а чинят её обычно на последнем шаге. Начинайте диагностику с вопроса «есть ли нужный фрагмент среди кандидатов» — и в большинстве случаев дальше идти не придётся.
Как мы отделяем проверенное от предполагаемого — на странице о проекте. Модели переранжирования и весь каталог — по одному ключу: keydealer.ru/login.
Частые вопросы
Зачем нужен поиск, если можно дообучить модель?
Потому что данные меняются, а дообучение — проект с бюджетом и сроком. Подстановка нужных фрагментов в контекст работает сразу и обновляется мгновенно.
Из чего состоит такая система?
Из четырёх частей: разбиение документов на фрагменты, поиск кандидатов по запросу, упорядочивание кандидатов и подстановка лучших в контекст модели.
Где она чаще всего ломается?
На разбиении. Если фрагменты нарезаны неудачно, ни один следующий шаг это не исправит: нужного куска просто нет среди кандидатов.
Сколько фрагментов подставлять?
Обычно три-пять. Больше разбавляет релевантное и удорожает каждый запрос, не улучшая ответ.
Нужен ли отдельный переранжировщик?
Он заметно улучшает отбор. В каталоге KeyDealer есть модели переранжирования с нулевой ставкой после квалификации.