MAATRIX / Блог / Что происходит внутри RAG между вопросом и ответом

Что происходит внутри RAG между вопросом и ответом

MAATRIX

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

Поиск по смыслу: как векторная база находит нужные куски

Вектор вопроса летит в векторную базу (Qdrant, Milvus, pgvector, Weaviate и подобные), где уже лежат векторы всех чанков из индексации. База не ищет точное совпадение — она вычисляет расстояние (обычно косинусное сходство или скалярное произведение) между вектором вопроса и векторами всех чанков и возвращает top-N ближайших — обычно от 3 до 10 кусков, конкретное число подбирается под задачу.

На больших объёмах данных база не перебирает векторы линейно — используется приближённый поиск ближайших соседей (ANN, чаще всего на основе индекса HNSW). Это компромисс: поиск становится в разы быстрее, чем полный перебор, но за счёт того, что найденный «ближайший» сосед может оказаться не абсолютно ближайшим, а близким с некоторой погрешностью. На практике для большинства задач это не критично, но если вы видите, что явно релевантный чанк иногда не попадает в выдачу — стоит проверить настройки индекса (например, параметр ef_construction/ef_search в HNSW), а не сразу винить эмбеддинги.

Дополнительно поиск часто фильтруется по метаданным чанка — по дате документа, по разделу, по тегу доступа. Это отдельный слой логики поверх векторного поиска: сначала сузили выборку по фильтрам, потом внутри неё ищем по векторам (или наоборот, в зависимости от реализации базы). Если вы настраивали свою векторную базу под RAG с нуля, детали конфигурации и частые грабли собраны в статье про то, как поднять векторную базу для RAG на сервере.

Здесь же часто добавляют дополнительный шаг — reranker: модель, которая берёт top-N кандидатов от векторного поиска (грубый, но быстрый отбор) и пересортировывает их точнее, глядя сразу на пару «вопрос + чанк» вместо отдельных векторов. Это заметно повышает точность на неоднозначных запросах, но добавляет ещё один шаг и задержку — когда он оправдан, разобрано в статье про reranker-модели в RAG.

Контекст в промпте: что реально видит модель

Найденные чанки — это ещё не ответ, это просто текст. Дальше система собирает промпт, который реально уйдёт в LLM. Обычно это выглядит примерно так:

Ответь на вопрос пользователя, используя ТОЛЬКО информацию из раздела "Контекст" ниже.
Если в контексте нет ответа — так и скажи, не придумывай.

Контекст:
[Источник 1: инструкция по бэкапам, раздел 3.2]
Резервное копирование PostgreSQL выполняется через pg_dump...

[Источник 2: FAQ по инфраструктуре]
Бэкапы хранятся в S3-совместимом хранилище 30 дней...

Вопрос пользователя: как настроить резервное копирование базы данных?

Здесь скрыто несколько важных деталей. Во-первых, у контекстного окна модели есть предел (у разных моделей и тарифов он разный, и он постоянно меняется — не буду называть конкретные цифры токенов, потому что они устаревают быстрее, чем пишется статья). Если найденных чанков много или они длинные, часть либо не влезет, либо начнёт вытеснять другую — значит, отбор top-N и размер чанка должны быть согласованы с реальным лимитом модели, которую вы используете.

Во-вторых, порядок чанков в промпте не нейтрален. На практике многие описывают эффект «потерянной середины» (lost in the middle): модели чаще уверенно используют информацию из начала и конца контекста, чем из середины длинного блока. Это не строгая гарантия и зависит от конкретной модели, но как ориентир — если у вас несколько чанков, разумно ставить наиболее релевантный (по оценке reranker'а или скору близости) ближе к началу или к концу контекста, а не просто в порядке, в котором их вернула база.

В-третьих, сама формулировка инструкции («отвечай только по контексту», «не придумывай», «укажи источник») напрямую влияет на то, насколько модель будет придерживаться контекста, а не своей общей «памяти» из обучения. Это не гарантия против галлюцинаций, но заметно снижает их частоту — и это единственный слой из всего пайплайна, который правится не кодом базы данных, а текстом промпта.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Развернуть ИИ на сервере

Чанкинг решает половину качества RAG

Если поиск и генерация — это видимая часть айсберга, то чанкинг — то, что решается ещё до всякого поиска, и чаще всего именно здесь теряется качество. Чанк — это единица, которую модель эмбеддингов превращает в вектор и которую впоследствии увидит LLM как «источник». Плохо нарезанный чанк портит оба шага сразу: его эмбеддинг размыт (в куске намешаны две разные темы — вектор оказывается «между» ними, не близким ни к одному нормальному запросу), а если он всё-таки найден — модель получает обрывок без нужного продолжения.

Основные параметры и компромиссы:

Подход к чанкингуПлюсыМинусы
Фиксированный размер (например, по числу символов/токенов)Просто реализовать, предсказуемый размерРежет посреди предложений и мыслей, не уважает структуру документа
По границам структуры (заголовки, абзацы, пункты списка)Чанк — законченная смысловая единицаРазмер куска непредсказуем, иногда слишком большой или маленький
С перекрытием (overlap) между соседними чанкамиСнижает риск потерять мысль на границе разрезаДублирует часть текста, база и токены расходуются менее эффективно
Семантический чанкинг (разрез там, где меняется тема, по скачку в эмбеддингах)Куски получаются тематически цельнымиДороже по вычислениям, сложнее в реализации и отладке

Универсального «правильного» размера чанка не существует — он зависит от типа документов (техническая документация, база FAQ, юридические тексты режутся по-разному) и от вопросов, которые реально задают пользователи. Здесь стоит ориентироваться не на абстрактное число, а на эксперимент: возьмите реальные вопросы пользователей, прогоните через пайплайн с разными настройками чанкинга и смотрите, в каком варианте нужный ответ реально попадает в top-N найденных кусков. Практический пример такого пайплайна целиком, с кодом для чанкинга, эмбеддингов и поиска, разобран в статье RAG-пайплайн с нуля: практический пример.

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

Почему RAG всё равно ошибается: три источника проблем

RAG снижает количество галлюцинаций по сравнению с «голой» моделью без контекста, но не убирает их полностью — и когда система всё же врёт, причина почти всегда одна из трёх, и их стоит проверять именно в этом порядке.

1. Плохой чанкинг. Чанк обрезан посреди ответа, или в него попал текст из другого раздела, или таблица разбита между двумя чанками и потеряла структуру. Модель добросовестно отвечает по тому, что получила — но получила она обрывок. Диагностика: выведите в лог реальные чанки, которые ушли в промпт, и прочитайте их сами — если текст сам по себе не даёт полного ответа на вопрос, проблема не в LLM.

2. Найден нерелевантный чанк. Поиск вернул top-N кусков, но ни один из них реально не отвечает на вопрос — просто они оказались ближе всего геометрически из того, что было в базе. Причины разные: вопрос сформулирован не так, как документы (специфичный жаргон, синонимы, аббревиатуры), нужного документа вообще нет в базе, порог top-N слишком маленький и правильный чанк был пятым, а взяли только три. Диагностика та же — смотрите, что реально попало в контекст. Если контекст откровенно не по теме, а сама база содержит нужный документ — стоит проверить релевантность поиска отдельно от генерации, возможно, добавив reranker или пересмотрев формулировку вопроса перед эмбеддингом (query rewriting).

3. Модель игнорирует контекст. Самый неприятный случай: чанки в промпте правильные и релевантные, но модель всё равно отвечает по своей «внутренней памяти» из обучения — особенно если контекст противоречит тому, что модель «знает» в общем виде, или если инструкция в промпте недостаточно жёсткая («используй контекст» вместо «отвечай ТОЛЬКО по контексту, и если ответа нет — прямо скажи об этом»). Это лечится усилением системного промпта и явной инструкцией признавать нехватку информации, а не лечится изменением базы данных или эмбеддингов — потому что проблема на этом этапе не в поиске, а в генерации.

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

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Развернуть ИИ на сервере

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Можно ли обойтись без чанкинга и просто отдавать модели целые документы?

Для очень коротких документов — да, если они целиком влезают в контекстное окно модели с запасом. Но для базы из десятков и сотен документов это невозможно и по объёму, и по цене запроса — модели платят за каждый токен контекста, и не отфильтрованный текст резко ухудшает соотношение сигнал/шум для генерации.

Что делать, если модель отвечает не по контексту, а по общим знаниям?

Усилить системный промпт явной инструкцией отвечать только на основе контекста и признавать отсутствие ответа, если контекст его не содержит. Если не помогает — проверить, не смешиваются ли разные системные и пользовательские сообщения в одном запросе, и не «перебивает» ли инструкцию сама формулировка вопроса.

Как понять, что проблема в чанкинге, а не в модели?

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

Нужен ли reranker, если базовый векторный поиск и так возвращает разумные результаты?

Не всегда обязателен — на простых и однозначных запросах top-N от векторного поиска часто уже достаточно хорош. Reranker оправдан там, где вопросы неоднозначны, документы похожи друг на друга по теме или частота ошибочных ответов заметна на практике.

Одинаковый ли RAG для чат-бота и для системы вопрос-ответ по документации?

Механизм тот же самый, но параметры (размер чанка, top-N, жёсткость инструкции «не выдумывать») подбираются отдельно под тип документов и ожидания пользователей — готовых универсальных настроек не существует.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →