RAG · Retrieval-augmented generation · урок 4 · 13 мин
RAG online: retrieve → rerank → generate → check
RAG online: retrieve, rerank, generate, check
Зачем
После урока ты пройдёшь вслух online-половину RAG — что происходит на каждый вопрос пользователя — и объяснишь, где стоят фильтры, зачем нужен reranker и что делать, когда ответа в найденном нет. Это вторая половина ответа «walk me through a RAG pipeline» и место, где собеседующий проверяет, понимаешь ли ты, что гарантии живут в коде, а не в prompt.
Шесть шагов на каждый вопрос
Offline-половина заранее подготовила библиотеку: chunks (куски документов) с metadata (карточкой: продукт, язык, версия, даты действия, доступ), векторный индекс и индекс по словам. Online-половина работает на каждый вопрос:
- Query rewrite — переписать вопрос для поиска.
- Filter — отсеять по metadata кодом.
- Retrieve — быстро достать 50–150 кандидатов.
- Rerank — внимательно пересортировать и оставить 5–20.
- Generate — написать ответ только по ним, со ссылками.
- Check — проверить ответ до того, как его увидит клиент.
Пройдём их на одном вопросе: клиент в чате пишет «а сколько за досрочку по моему автокредиту?».
Query rewrite: вопрос, пригодный для поиска
Query rewrite (переформулировка запроса) превращает разговорную фразу в запрос, по которому поиск что-то найдёт. «Досрочка» раскрывается в «досрочное погашение, early settlement fee»; «мой автокредит» — в продукт из профиля клиента; местоимения из прошлых реплик («а по нему?») заменяются тем, о чём шла речь. Запрос проходит ту же normalization (приведение цифр, Unicode и написаний к одному виду), что и документы.
Переписывает обычно LLM (large language model, большая языковая модель), и это новая точка отказа: переписанный запрос пишут в trace (журнал запроса), чтобы потом видеть, не исказил ли он вопрос.
Filter: гарантии живут в коде
Metadata filter (отсев по карточке) применяется здесь, в коде поиска, до ranking (упорядочивания). Три фильтра:
- Доступ. Группы пользователя берутся из token входа (подписанного удостоверения сессии), никогда из текста, который пишет модель. В выдачу попадают только chunks с подходящим ACL (access control list, списком допуска).
- Дата действия. Нужна версия документа, действующая на дату, важную для вопроса: сегодня — для нового клиента, дата договора — для старого, если так сказано в условиях. Клиент с договором 2024 года мог подписать другой тариф.
- Язык и продукт. Отсечь чужие продукты, которые похожи по словам.
Почему не в prompt (текст инструкций для модели)? Инструкция «используй самую свежую версию» — совет: модель может его пропустить, неверно понять, а внедрённый в данные текст может её переубедить. Да и сам совет неверен для старого договора. Фильтр в коде срабатывает одинаково каждый раз и проверяется детерминированным тестом.
Pre-filter (фильтр внутри запроса поиска) лучше post-filter (фильтра после выдачи) дважды: запрещённый документ вообще не проходит через систему, и top-k (k лучших результатов) не пустеет после отсева.
Retrieve и rerank: быстро и грубо, потом медленно и точно
Retrieve (выборка) — hybrid search (гибридный поиск): векторный поиск по смыслу и BM25 (стандартная формула поиска по словам) параллельно, списки сливаются по рангам. Он быстрый, потому что документы закодированы заранее: bi-encoder (модель, кодирующая запрос и документ по отдельности) превращает запрос в один вектор, а индекс находит соседей среди миллионов. Цена скорости — грубость: в top-50 полно похожего, но не того.
Rerank (переранжирование) делает reranker, обычно cross-encoder (модель, которая читает вопрос и chunk вместе и выдаёт оценку релевантности). Он видит связь между словами вопроса и куска, поэтому точнее. Но ему нужен полный прогон модели на каждую пару «вопрос — chunk», а заранее посчитать ничего нельзя: пара зависит от вопроса.
Арифметика объясняет, почему reranker не гоняют по всему корпусу. Корпус в 1 000 000 chunks — миллион прогонов на один вопрос. Кандидатов 50 — пятьдесят прогонов, в 20 000 раз меньше. Документация Sentence-Transformers описывает ту же схему: retriever достаёт, например, 100 кандидатов, cross-encoder пересортировывает только их.
Сколько оставлять после rerank — тоже замер. Anthropic в статье о contextual retrieval (2024) сравнивала 5, 10 и 20 chunks в context (всём, что модель видит в запросе): лучше всего вышло 20, но там же оговорка, что лишние chunks отвлекают модель, и число надо проверять на своей задаче.
Generate: только из найденного, со ссылками
Generation (генерация) — модель получает вопрос и отобранные chunks с инструкцией: отвечать только по ним и для каждого утверждения указывать citation (ссылку на chunk). Если ответа в chunks нет, правильное поведение — сказать «не нашёл» или передать человеку, а не угадывать. Это поведение проверяют отдельно: в наборе тестовых вопросов должны быть вопросы без ответа в корпусе.
Check: проверка до клиента
Check (проверка) — последний шаг перед клиентом. Каждое утверждение, особенно каждое число, должно найтись в процитированном chunk. Это groundedness (опора ответа на источник), её проверяют кодом для чисел и отдельной моделью-судьёй для смысла.
Для самых рискованных фактов есть решение сильнее проверки: не давать модели производить число из текста вообще. Комиссию по договору клиента читает tool (вызываемая функция) из системы учёта, а модель только вставляет её в ответ. Источник числа становится системой учёта, а не текстом, и проверка сводится к простому сравнению: число в ответе обязано совпасть с тем, что вернул tool.
Пример целиком
Клиент с договором от мая 2024 года спрашивает про досрочку. Rewrite даёт «early settlement fee, auto loan». Фильтр оставляет chunks автокредита на русском, доступные клиентам, в версии, действовавшей в мае 2024 года: тариф 2026 года отсечён, хотя по словам он похож сильнее. Hybrid retrieve возвращает 50 кандидатов; нужный пункт тарифа на 7-м месте, выше стоят общие страницы о кредитах. Cross-encoder поднимает его на 1-е место, в context идут 5 chunks. Модель отвечает «1% от остатка, но не меньше 5 000 ₸» со ссылкой на chunk (цифры в примере выдуманы), а check находит обе цифры в нём — или комиссию сразу читает tool по номеру договора.
Что пишется в trace
На каждом шаге: исходный и переписанный запрос, применённые фильтры, chunk IDs и версии документов с рангами до и после rerank, что попало в context, ответ с citations, результат проверки. Без этого неверный ответ потом не разобрать.
Схема
Термины · Key terms
- query rewrite
- переформулировка вопроса перед поиском
- metadata filter
- отсев chunks по карточке кодом: доступ, дата, язык
- pre-filter / post-filter
- фильтр внутри поиска / после выдачи
- top-k
- k лучших результатов
- bi-encoder
- модель, кодирующая запрос и документ по отдельности; быстрый поиск
- cross-encoder
- модель, читающая вопрос и chunk вместе; точная оценка
- reranker
- второй, медленный и точный отбор кандидатов
- citation
- ссылка на chunk, из которого взято утверждение
- groundedness
- опора каждого утверждения ответа на найденный текст
- tool
- функция, которую агент вызывает, например чтение комиссии из системы учёта
Сделай сам
Объясни вслух термин cross-encoder за одну минуту, в четыре части: русский смысл, простое значение, образ для запоминания, пример из банка. Запиши голосовую заметку.
Обсуди с тьютором: перескажи текстом, что сказал, и попроси тьютора задать тебе уточняющий вопрос собеседующего: «why not run the cross-encoder over the whole corpus?»
Проверь себя · ответ под вопросом
1Почему reranker не прогоняют сразу по всему корпусу?
2На каком шаге online-половины применяется фильтр по дате, и почему не в prompt?
3Что должна сделать система, если в отобранных chunks ответа нет?
Interview questions · US / UK
1Walk me through what happens online, from the user's question to the answer.
2What's the difference between a bi-encoder and a cross-encoder, and where does each sit?
3Follow-up probe: a colleague wants to add "only use documents the user is allowed to see" to the system prompt. Is that enough?
Ключевые фразы · Say it aloud
- Fast and rough first, slow and precise secondсначала быстро и грубо, потом медленно и точно
- Filters run in retrieval code, before rankingфильтры в коде поиска, до ranking
- The user's groups come from the login token, never from model outputгруппы из token входа, не из текста модели
- If the chunks don't contain the answer, say so or escalateнет ответа в chunks — скажи или передай человеку
- High-stakes numbers come from a tool, not from proseрискованные числа даёт tool, а не текст
Материалы
- Sentence-Transformers — Retrieve & Re-Rank — bi-encoder против cross-encoder, схема из двух стадий (8 мин)
- Anthropic — Introducing Contextual Retrieval — разделы про reranking и число chunks в context (5 мин)
- pgvector — README — раздел про фильтрацию: почему post-filter опустошает выдачу (4 мин)