RAG для локальных моделей: построение и внедрение

Курс о том, как спроектировать и реализовать Retrieval-Augmented Generation (RAG) для локально запускаемых LLM. Разберём подготовку данных, индексацию, retrieval, генерацию, оценку качества, а также практические вопросы развёртывания и безопасности.

1. Основы RAG и архитектуры локальных LLM

Основы RAG и архитектуры локальных LLM

Зачем нужен RAG в локальных сценариях

Большие языковые модели (LLM) хорошо генерируют текст и обобщают знания, но у них есть ограничения:

  • Ограниченное окно контекста: модель не может «удержать в голове» весь ваш архив документов.
  • Устаревание знаний: модель обучена на данных прошлого и не знает ваши свежие данные.
  • Галлюцинации: модель может уверенно «додумать» факты.
  • Требования к приватности: в локальных проектах данные часто нельзя отправлять во внешние API.
  • Retrieval-Augmented Generation (RAG) решает эти проблемы, подключая модель к вашим данным через поиск релевантных фрагментов и добавление их в промпт перед генерацией.

    > RAG объединяет поиск и генерацию, позволяя модели опираться на извлечённые документы, а не только на параметры самой модели. Источник: Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks (Lewis et al., 2020)

    Что такое RAG простыми словами

    RAG — это архитектурный шаблон, а не конкретная библиотека.

    Базовая идея

  • Вы храните свои документы (или их фрагменты) в системе, где можно быстро находить похожие по смыслу куски.
  • На вход вопроса пользователя вы находите несколько самых близких фрагментов.
  • Вы вставляете эти фрагменты в контекст LLM.
  • LLM отвечает, опираясь на предоставленные источники.
  • !Общая карта RAG: как вопрос превращается в поиск по данным и затем в ответ LLM

    Компоненты RAG

  • Ингест (ingestion): загрузка документов, очистка, нормализация.
  • Чанкинг (chunking): разбиение текста на фрагменты.
  • Эмбеддинги (embeddings): преобразование текста в векторы.
  • Векторное хранилище: быстрый поиск ближайших векторов.
  • Ретривер (retriever): логика поиска top-k фрагментов.
  • Реранкер (reranker): улучшение качества выдачи (не всегда нужен).
  • Сборка промпта: форматирование контекста, добавление инструкций.
  • Генерация: ответ локальной LLM.
  • Архитектура LLM на уровне, достаточном для RAG

    Чтобы правильно строить RAG, важно понимать, как LLM читает контекст и почему «просто положить больше текста» не всегда помогает.

    Токены и контекст

    LLM работает не со словами, а с токенами — кусочками текста (части слов, знаки, пробелы). Токенизация влияет на:

  • реальную «вместимость» окна контекста;
  • стоимость вычислений (чем больше токенов, тем медленнее);
  • качество: длинные и шумные контексты ухудшают точность.
  • Практический вывод для RAG: контекст должен быть коротким, релевантным и структурированным.

    Трансформер: что нужно знать

    Большинство современных LLM построены на архитектуре Transformer. Важные элементы:

  • Слои: модель последовательно обрабатывает вход через много блоков.
  • Self-attention: механизм, который позволяет каждому токену учитывать другие токены в контексте.
  • Feed-forward: нелинейная обработка признаков после внимания.
  • Ключевой прикладной факт: вычисления внимания растут быстро с длиной контекста, поэтому в RAG обычно выгоднее лучше отбирать куски, чем «заливать» всё подряд.

    KV-кэш и скорость инференса

    При автодополнении (генерации токен за токеном) многие локальные движки используют KV-cache (кэш ключей и значений внимания), чтобы не пересчитывать прошлый контекст заново на каждом шаге.

    Практические последствия:

  • длинный входной контекст замедляет старт ответа;
  • длинная генерация тоже занимает время, но часто масштабируется более предсказуемо;
  • стабильный формат промпта и разумные ограничения по контексту дают заметный выигрыш.
  • Что значит «локальная LLM» и из чего состоит стек

    Под локальной LLM обычно понимают:

  • веса модели хранятся у вас (на сервере или рабочей станции);
  • инференс выполняется вашим CPU/GPU;
  • доступ и данные контролируете вы.
  • Слои стека локального инференса

  • Файл весов модели
  • - Полноточные (FP16/BF16) или квантованные. - Часто используются форматы, оптимизированные под локальный запуск.
  • Движок инференса
  • - Выполняет матмуль, attention, управление памятью. - Примеры: llama.cpp (локальный CPU/GPU инференс), vLLM (высокопроизводительный серверный инференс).
  • Оркестрация (обёртка над пайплайном)
  • - Сборка промптов, RAG-логика, инструменты, логирование. - Примеры фреймворков: LangChain, LlamaIndex.
  • Хранилище знаний для RAG
  • - Векторные БД и библиотеки поиска: FAISS, Milvus, Chroma.

    !Как RAG-слой взаимодействует с векторным поиском и локальной LLM

    Квантование: почему локальные модели часто «4-bit»

    Квантование — это уменьшение точности хранения весов (например, до 8 или 4 бит), чтобы:

  • уменьшить потребление памяти;
  • ускорить инференс (в зависимости от железа и движка);
  • уместить модель на одну GPU или даже на CPU.
  • Компромисс: чем сильнее квантование, тем выше риск потери качества, особенно на сложных задачах.

    Практический вывод для RAG: часто выгоднее слегка более слабая локальная модель + хороший ретривал, чем попытка «вытянуть всё» генерацией без контекста.

    Как RAG «приземляется» на локальную LLM

    Жизненный цикл запроса

    Ниже — рабочая последовательность шагов, которую вы будете строить и улучшать на протяжении курса:

  • Пользователь задаёт вопрос.
  • Система нормализует вопрос (язык, лишние символы, возможно — переформулирование).
  • Строится эмбеддинг запроса.
  • Идёт поиск top-k чанков в векторном хранилище.
  • Опционально применяется реранкер, чтобы упорядочить найденные чанки по полезности.
  • Собирается промпт:
  • - системная инструкция (как отвечать); - контекст (найденные чанки); - вопрос пользователя; - требования к формату ответа (например, со ссылками на источники).
  • Локальная LLM генерирует ответ.
  • Система возвращает ответ и (желательно) показывает источники.
  • Почему важны чанкинг и формат контекста

    Ошибки RAG чаще всего не «в модели», а в данных и их подаче. Типовые проблемы:

  • слишком большие чанки: поиск попадает в нужный документ, но внутри чанка много мусора;
  • слишком маленькие чанки: теряется смысл и связи (определение без примера, таблица без заголовка);
  • нет метаданных: непонятно, откуда фрагмент и насколько он свежий;
  • контекст вставлен без структуры: LLM не отличает «правила» от «цитаты».
  • Хорошая практика — хранить рядом с чанком:

  • источник (файл/URL/раздел);
  • заголовки (иерархия документа);
  • дату/версию;
  • права доступа (если есть разграничение).
  • Минимальный набор решений и терминов, который пригодится дальше

    Top-k и компромисс точности/скорости

    В ретривале часто выбирают k — сколько чанков доставать. Больше k:

  • повышает шанс найти нужное;
  • увеличивает токены в контексте и может ухудшить ответ из-за шума;
  • замедляет систему.
  • В следующих статьях курса вы будете подбирать k и стратегию чанкинга под свой домен.

    Векторный поиск: что он реально делает

    Эмбеддинг превращает текст в числовой вектор. Дальше поиск делает примерно следующее: «найди в базе такие векторы, которые ближе всего к вектору запроса». Близость измеряется метрикой (часто косинусной), но на практике достаточно помнить:

  • качество эмбеддингов критично;
  • данные должны быть очищены и однородны;
  • метаданные и фильтры (например, по проекту/дате/языку) часто дают больше пользы, чем сложная математика.
  • Генерация: почему ответы могут быть «правдоподобными, но неверными»

    LLM оптимизирована на продолжение текста и согласованность, а не на истинность. Поэтому RAG-подход почти всегда усиливают правилами:

  • просить отвечать только на основе контекста;
  • просить цитировать источники;
  • просить говорить «не знаю», если в контексте нет ответа.
  • Практические ориентиры для локального RAG-проекта

    Качество

  • Начинайте с простого baseline: чанкинг + эмбеддинги + top-k.
  • Добавляйте реранкер только если видите, что top-k часто приносит «почти то».
  • Разделяйте проблемы:
  • - если не находится нужный фрагмент — это ретривал; - если фрагмент найден, но ответ плохой — это промпт/модель/формат контекста.

    Производительность

  • Сначала оптимизируйте токены (меньше мусора в контексте), а не железо.
  • Следите за длиной промпта и лимитом генерации.
  • Профилируйте отдельно:
  • - время эмбеддинга, - время поиска, - время генерации.

    Безопасность и приватность

    Локальный запуск не делает систему автоматически безопасной. В RAG важно продумать:

  • контроль доступа на уровне документов и метаданных;
  • фильтрацию выдачи по правам пользователя;
  • логирование запросов (и политику хранения логов).
  • Что дальше в курсе

    В следующих материалах вы перейдёте от общих принципов к реализации:

  • подготовка документов и стратегии чанкинга;
  • выбор и запуск эмбеддинг-модели локально;
  • устройство и настройка векторного хранилища;
  • сборка промптов и шаблоны «ответа с источниками»;
  • измерение качества RAG и отладка типовых провалов.
  • 2. Подготовка базы знаний: сбор, очистка, чанкинг и метаданные

    Подготовка базы знаний: сбор, очистка, чанкинг и метаданные

    Связь с предыдущей темой

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

    !Диаграмма показывает, как сырой контент превращается в индекс для RAG

    Что считается «базой знаний» в RAG

    В контексте RAG база знаний — это не просто папка с файлами. Это индексируемый набор фрагментов (чанков), где у каждого фрагмента есть:

  • текст, пригодный для поиска по смыслу;
  • метаданные, чтобы фильтровать, объяснять источник и контролировать доступ;
  • стабильные идентификаторы, чтобы обновлять и удалять данные без хаоса.
  • Практический ориентир: если вы не можете уверенно ответить на вопросы «откуда этот фрагмент?», «какой он версии?» и «кому его можно показывать?» — база знаний пока не готова для продакшена.

    Сбор данных: источники и границы

    Какие источники обычно подключают

  • Документация (Markdown/HTML, Confluence, GitBook).
  • PDF (регламенты, договоры, стандарты).
  • Office-документы (DOCX, PPTX).
  • Тикет-системы и базы знаний поддержки.
  • Репозитории кода и README (если RAG для разработки).
  • Внутренние чаты и письма (только если есть политика доступа и согласования).
  • Определите границы до начала работ

    Сбор «всего подряд» почти всегда ухудшает RAG: в индекс попадает мусор, дубли и противоречивые версии. Перед ingestion зафиксируйте:

  • цель: какие вопросы должен закрывать ассистент;
  • аудиторию: кто будет пользоваться;
  • типы документов: что включаем, что исключаем;
  • правила свежести: что считать актуальным;
  • правила доступа: какие документы нельзя смешивать в одном индексе без ACL.
  • Если у вас несколько отделов с разным доступом, обычно лучше делать либо:

  • один индекс + жёсткая фильтрация по метаданным доступа;
  • либо отдельные индексы по доменам/ролям.
  • Извлечение текста: парсинг и OCR

    Парсинг «цифровых» документов

    Для HTML/Markdown чаще всего достаточно корректно извлечь основной текст и заголовки. Для DOCX/PPTX важно не потерять структуру (заголовки, списки, подписи).

    PDF: главный источник проблем

    PDF бывает двух типов:

  • текстовый PDF, где текст реально существует как символы;
  • скан, где текст — это картинка, и нужен OCR.
  • Риски PDF для RAG:

  • «ломаная» последовательность строк, переносы, колонки;
  • потеря заголовков и иерархии разделов;
  • мусор вроде номеров страниц, колонтитулов и повторяющихся шапок.
  • Практика: если качество ответов «странное», часто причина в том, что в индекс попали колонтитулы и обрывки строк.

    Очистка и нормализация: что делать до чанкинга

    Очистка — это приведение всех документов к предсказуемому, однородному тексту. Цель не «сделать красиво», а улучшить:

  • качество эмбеддингов;
  • точность поиска;
  • полезность контекста для LLM.
  • Базовые шаги очистки

  • Удаление повторяющихся колонтитулов, номеров страниц, меню навигации.
  • Нормализация пробелов, переносов строк, странных символов.
  • Удаление «мусорных» блоков: оглавлений, если они дублируют заголовки; списков литературы, если они не нужны.
  • Явная обработка списков и таблиц, чтобы смысл не терялся.
  • Удаление дублей документов и дублей фрагментов.
  • Дедупликация: почему она важна

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

    Минимальная стратегия:

  • на уровне документов: не индексировать одинаковые файлы;
  • на уровне чанков: не хранить идентичные фрагменты.
  • Важно: дедупликация должна сохранять информацию об источниках. Иногда один и тот же фрагмент легально присутствует в двух местах, и это полезно для объяснимости.

    Нормализация языка и кодировок

  • Приведите всё к UTF-8.
  • Если у вас мультиязычная база, храните language в метаданных и используйте фильтрацию при поиске.
  • Если в одном документе встречаются русский и английский, часто помогает индексировать как есть, но обязательно сохранять язык чанка (или хотя бы документа).
  • Чанкинг: как разрезать текст так, чтобы он искался и читался

    Чанкинг — это разбиение документов на фрагменты, которые будут:

  • индексироваться эмбеддинг-моделью;
  • доставаться ретривером;
  • вставляться в промпт LLM.
  • Главный компромисс:

  • большие чанки дают контекст, но добавляют шум;
  • маленькие чанки точнее матчятся, но теряют смысл.
  • Базовые термины

  • Чанк — фрагмент текста, который вы храните и ищете.
  • Перекрытие (overlap) — часть текста, повторяющаяся между соседними чанками.
  • Перекрытие обычно нужно, чтобы важное предложение не оказалось «разрезанным пополам» и не потеряло контекст.

    Популярные стратегии чанкинга

    #### Чанкинг по структуре документа

    Подходит для документации и регламентов.

  • режем по заголовкам;
  • сохраняем путь заголовков как метаданные (section_path);
  • внутри раздела при необходимости дорезаем до нужного размера.
  • Плюс: фрагменты хорошо объяснимы, проще собирать «правильный контекст».

    #### Чанкинг фиксированного размера

    Подходит как быстрый baseline.

  • режем по длине (в символах или токенах);
  • добавляем overlap.
  • Минус: можно «порезать» смысловые блоки.

    #### Рекурсивный чанкинг

    Практический вариант «поумнее», который сначала пытается разделить по большим разделителям (заголовки, абзацы), а если не получается — по меньшим.

    Такой подход реализован во многих инструментах. Например, в LangChain есть семейство text splitters, включая рекурсивные (см. LangChain: Text splitters).

    #### Семантический чанкинг

    Идея: резать по смысловым границам (темам), а не по размеру. Реализации бывают разными и часто сложнее в отладке.

    Используйте, если:

  • документы длинные;
  • темы внутри документа меняются резко;
  • вы уже упёрлись в качество при обычном чанкинге.
  • Чанкинг для «сложных» типов контента

    #### Таблицы

    Если таблица важна для ответов, опасно превращать её в «кашу текста». Рабочие варианты:

  • хранить таблицу как текст с разделителями колонок;
  • добавлять строку-заголовок таблицы в каждый чанк-строку;
  • не смешивать в одном чанке несколько таблиц и окружающий текст, если это ломает смысл.
  • #### Код и конфигурации

  • не объединяйте несвязанные файлы в один чанк;
  • сохраняйте путь файла и язык (path, language);
  • часто полезно хранить рядом «комментарии + код» одним чанком.
  • #### FAQ и базы поддержки

    Для пар вопрос–ответ часто лучше делать один чанк на одну пару, добавляя метки:

  • тип записи (doc_type=faq);
  • продукт/модуль;
  • дата актуальности.
  • Метаданные: как сделать RAG управляемым

    Метаданные — это поля, которые вы храните рядом с чанком. Они решают три задачи:

  • фильтрация: сузить поиск по домену, продукту, языку, правам;
  • объяснимость: показать пользователю источники;
  • обновляемость: переиндексировать нужные куски, удалять устаревшее.
  • Минимальный набор метаданных для чанка

  • source: откуда взят текст (путь к файлу, URL, название системы).
  • document_id: стабильный ID документа.
  • chunk_id: ID чанка внутри документа.
  • title: заголовок документа.
  • section_path: путь заголовков (например, "Политика/Доступ/Пароли").
  • created_at или published_at: дата публикации.
  • updated_at: дата обновления.
  • language: язык.
  • Метаданные доступа (ACL)

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

    Типовые поля:

  • visibility: например, public/internal/confidential.
  • allowed_groups: список групп, которым можно показывать.
  • allowed_users: если нужны исключения.
  • Дальше ретривер делает поиск только среди разрешённых чанков.

    Метаданные качества и обслуживания

    Полезные поля для эксплуатации:

  • ingested_at: когда чанк попал в индекс.
  • parser_version: версия парсера/пайплайна.
  • checksum: хэш содержимого, чтобы понимать, изменился ли чанк.
  • Эти поля помогают отвечать на вопрос «почему в индексе старый текст» и безопасно переиндексировать.

    Как организовать ingestion-пайплайн

    Рекомендуемая последовательность

  • Выгрузка документов из источников.
  • Извлечение текста (парсинг/OCR).
  • Очистка и нормализация.
  • Разбиение на чанки.
  • Обогащение метаданными.
  • Эмбеддинги.
  • Запись в векторное хранилище.
  • Важно: шаги 2–5 должны быть детерминированными и повторяемыми, иначе вы не сможете нормально отлаживать качество.

    Пример структуры данных чанка

    Ключевая мысль: text должен быть максимально «чистым», а всё, что нужно для контроля и объяснений, должно жить в metadata.

    Типовые ошибки и как их избежать

    Ошибка: индексировать «как есть», без очистки

    Последствия:

  • поиск цепляется за мусорные токены (колонтитулы, навигацию);
  • в top-k попадает нерелевантное;
  • LLM отвечает хуже даже при хорошем ретривале.
  • Ошибка: чанки слишком большие

    Последствия:

  • в контекст попадает много лишнего;
  • растёт стоимость по токенам и падает точность из-за шума;
  • сложнее «процитировать» конкретное место.
  • Ошибка: чанки слишком маленькие

    Последствия:

  • фрагменты теряют смысл;
  • ответы становятся обрывочными;
  • модель вынуждена додумывать связи.
  • Ошибка: нет метаданных

    Последствия:

  • нельзя показать источник;
  • нельзя фильтровать по версии/продукту/языку;
  • невозможно нормально обновлять индекс.
  • Ошибка: нет контроля версий

    Если документ обновился, а старые чанки остались, RAG начинает «спорить сам с собой»: в контексте появляются противоречивые инструкции.

    Минимальная защита:

  • хранить updated_at;
  • при переиндексации удалять чанки старой версии по document_id.
  • Практические рекомендации для локального RAG

    Начните с простого baseline

  • один домен документов;
  • структурный или рекурсивный чанкинг;
  • overlap включён;
  • метаданные: источник, заголовки, даты, язык, доступ.
  • Ведите журнал ingestion

    Даже простой лог в JSON/CSV сильно ускоряет отладку:

  • сколько документов обработано;
  • сколько чанков получилось;
  • какие документы упали на OCR/парсинге;
  • какие документы изменились и переиндексированы.
  • Сразу продумайте «переиндексацию»

    RAG-система живёт в режиме изменений. Пайплайн должен уметь:

  • добавлять новые документы;
  • обновлять изменённые;
  • удалять устаревшие;
  • пересобирать индекс при смене правил чанкинга.
  • Что дальше

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

    Полезные ссылки для углубления:

  • LangChain: Text splitters
  • LlamaIndex: Loading data (Data connectors)
  • Chroma: Metadata filtering
  • 3. Эмбеддинги и векторные индексы: выбор моделей и хранилищ

    Эмбеддинги и векторные индексы: выбор моделей и хранилищ

    Связь с предыдущими темами

    В прошлой статье вы подготовили базу знаний: научились извлекать текст, очищать документы, делать чанкинг и добавлять метаданные. Теперь у вас есть главное сырьё для RAG: качественные чанки. Следующий шаг — сделать так, чтобы система могла быстро находить чанки по смыслу.

    Для этого нужны две вещи:

  • Эмбеддинги: превращают текст (чанки и запросы) в векторы.
  • Векторный индекс: позволяет быстро искать ближайшие векторы среди миллионов.
  • !Общая картина: где именно эмбеддинги и индекс находятся в RAG

    Что такое эмбеддинги и зачем они нужны

    Эмбеддинг — это числовое представление текста в виде вектора фиксированной длины (например, 384, 768 или 1024 чисел). Идея простая: тексты с похожим смыслом должны получаться близкими векторами.

    В RAG эмбеддинги используются в двух местах:

  • Для каждого чанка из базы знаний вы заранее считаете вектор и сохраняете его.
  • Для каждого пользовательского вопроса вы считаете вектор запроса и находите ближайшие векторы чанков.
  • Результат: LLM видит в контексте не весь архив, а только наиболее релевантные фрагменты.

    Что значит близкие векторы

    Чаще всего близость измеряют косинусным сходством. Оно сравнивает направление векторов (а не их длину) и даёт значение от до .

    Формула косинусного сходства:

    Обозначения:

  • — вектор запроса (question/query).
  • — вектор документа или чанка (document).
  • — скалярное произведение (число, отражающее согласованность направлений).
  • и — длины (нормы) векторов.
  • Практический вывод: если вы нормализуете векторы до длины 1, то косинусное сходство становится эквивалентно сортировке по скалярному произведению. Многие векторные БД и библиотеки используют именно этот трюк для ускорения.

    Как выбрать эмбеддинг-модель для локального RAG

    Выбор эмбеддинг-модели почти всегда влияет на качество RAG сильнее, чем выбор LLM. Причина: если нужный чанк не попал в top-k, модель не сможет корректно ответить, даже будучи очень сильной.

    Критерии выбора

  • Язык: нужна ли поддержка русского, английского, смешанных текстов.
  • Домен: общие тексты, юридические, технические, медицинские.
  • Длина входа: сколько токенов модель реально переваривает без деградации.
  • Размер и скорость: сможете ли вы считать эмбеддинги быстро на CPU/GPU.
  • Совместимость с продакшеном: лицензия, стабильность, возможность офлайн-развертывания.
  • Тип поиска: нужен ли чисто семантический поиск или гибрид с ключевыми словами.
  • Би-энкодеры и кросс-энкодеры

    В RAG обычно используют два разных класса моделей:

  • Би-энкодер (embedding model): кодирует запрос и документ независимо, затем сравниваются векторы; это быстро и масштабируется.
  • Кросс-энкодер (reranker): читает пару запрос+кандидат вместе и оценивает релевантность точнее, но намного медленнее.
  • В рамках этой статьи мы фокусируемся на би-энкодерах, потому что без них не построить векторный индекс. Реранкеры удобно подключать позже, когда базовый ретривал уже работает.

    Практически полезные семейства моделей

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

  • BGE: сильные универсальные эмбеддинги, есть мультиязычные варианты.
  • - BAAI/bge-small-en-v1.5 - BAAI/bge-m3
  • E5: популярное семейство, часто требует префиксы формата "query:" и "passage:".
  • - intfloat/multilingual-e5-base
  • Sentence-Transformers: экосистема и модели, удобные для старта и локального инференса.
  • - sentence-transformers/all-MiniLM-L6-v2 - Sentence-Transformers (проект)

    Важно: лучшей модели “вообще” нет. Для вашего домена победитель определяется тестом на ваших вопросах.

    Важные детали, которые часто ломают ретривал

    Инструкции и префиксы у эмбеддингов

    Некоторые модели обучены так, что ожидают специальные префиксы:

  • Для запросов: query: ...
  • Для документов: passage: ...
  • Если вы не используете нужный формат, качество может заметно упасть. Это критично, потому что внешне система будет работать, но ретривал станет хуже.

    Рекомендация: зафиксируйте единый формат для эмбеддинга запросов и чанков и держите его неизменным, пока сравниваете эксперименты.

    Нормализация векторов

    Есть два типовых режима:

  • Хранить “сырые” векторы и использовать метрику косинуса.
  • Нормализовать векторы до единичной длины и использовать скалярное произведение.
  • Практический подход: делайте так, как рекомендует документация выбранной модели и вашего хранилища. Смешивание режимов может дать некорректный порядок выдачи.

    Размерность эмбеддинга и память

    Если у вас чанков, а размерность эмбеддинга , то вы храните чисел.

    Практический смысл без математики:

  • Чем больше чанков, тем важнее скорость поиска и объём памяти.
  • Чем больше размерность, тем больше памяти и тем тяжелее индекс.
  • Поэтому иногда выгоднее:

  • улучшить чанкинг и очистку;
  • использовать более компактную эмбеддинг-модель;
  • а уже потом “разгонять” инфраструктуру.
  • Мультиязычность и смешанные документы

    Если у вас русские и английские документы, есть три устойчивых стратегии:

  • Одна мультиязычная модель для всего.
  • Две модели и два индекса по языкам.
  • Один индекс, но жёсткая фильтрация по метаданным language.
  • Чаще всего быстрее стартовать с вариантом 1 или 3.

    Что такое векторный индекс и почему без него нельзя

    Если у вас мало чанков, можно искать ближайшие вектора “в лоб”: посчитать сходство запроса со всеми векторами и взять top-k. Но это быстро перестаёт быть возможным.

    Векторный индекс — это структура данных, которая ускоряет поиск ближайших соседей.

    Есть два принципиальных режима:

  • Точный поиск: максимально корректно, но может быть медленно на больших объёмах.
  • Приближённый поиск (ANN): намного быстрее, обычно с очень небольшой потерей качества.
  • В RAG чаще всего используют ANN.

    Распространённые типы индексов

  • Flat (brute force): без индекса, просто сравнение со всеми; полезно как baseline и для малых коллекций.
  • HNSW: графовый индекс, очень популярен в векторных БД, хорош по качеству и скорости.
  • IVF: кластеризация пространства на “корзины”, затем поиск идёт в нескольких корзинах.
  • PQ (product quantization): сжатие векторов для экономии памяти, может снижать точность.
  • !Интуиция HNSW: почему графовый индекс ускоряет поиск

    Выбор векторного хранилища для локального проекта

    В локальном стеке есть три частых подхода:

  • Библиотека индекса “внутри приложения”.
  • Отдельный векторный сервер (векторная БД).
  • Расширение к классической БД.
  • Популярные варианты и когда их выбирать

    | Решение | Формат | Сильные стороны | Ограничения | Где уместно | |---|---|---|---|---| | FAISS | библиотека | высокая производительность, много типов индексов | надо самому строить сервисный слой, метаданные и фильтры “вручную” | прототипы, встроенный поиск, контроль индекса на низком уровне | | Qdrant | сервер | сильные фильтры по payload, удобная эксплуатация, HNSW | отдельный сервис, нужно следить за ресурсами | локальный продакшен, RAG с активными фильтрами | | Milvus | сервер | масштабирование, разные индексы, экосистема | заметно тяжелее в эксплуатации | большие объёмы, кластерные сценарии | | Chroma | библиотека/локальный сервер | быстро стартовать, удобна для прототипов | меньше “боевых” возможностей, чем у специализированных БД | быстрые MVP, эксперименты | | pgvector | расширение PostgreSQL | единая БД для данных и векторов, транзакции, SQL | скорость и ANN зависят от конфигурации и объёма | когда всё уже в Postgres и важна простота интеграции |

    Практическая рекомендация для локального RAG:

  • Если вам важны фильтры по метаданным, ACL и воспроизводимая эксплуатация, часто удобнее начать с Qdrant.
  • Если вы хотите максимальную скорость и готовы строить сервис вокруг индекса, FAISS даёт большую гибкость.
  • Если у вас уже есть Postgres и хочется минимизировать зоопарк сервисов, pgvector может быть “достаточно хорош” для первого релиза.
  • Метаданные и фильтрация: как связать индекс с вашей базой знаний

    Вы уже добавляли метаданные на этапе ingestion. Теперь важно, чтобы хранилище умело их использовать.

    Типовые сценарии фильтрации:

  • language=ru для русскоязычных запросов.
  • product=payments для ассистента по конкретному модулю.
  • updated_at >= 2025-01-01 для исключения старых версий.
  • allowed_groups для контроля доступа.
  • Ключевая мысль: векторное сходство отвечает на вопрос “похоже ли по смыслу”, а фильтры отвечают на вопрос “имею ли я право это показывать и подходит ли это по контексту задачи”.

    Если фильтров нет, вы почти неизбежно получите проблемы:

  • утечки между отделами;
  • смешение устаревших и актуальных политик;
  • выдачу “похожего, но из другого продукта”.
  • Практический минимум: как собрать рабочий baseline

    Ниже — минимальная последовательность, которая даёт работающий “скелет” RAG и позволяет дальше улучшать качество.

  • Выберите эмбеддинг-модель, подходящую по языку и скорости.
  • Зафиксируйте формат эмбеддинга документов и запросов.
  • Посчитайте эмбеддинги для всех чанков.
  • Загрузите векторы в выбранное хранилище вместе с метаданными.
  • Реализуйте поиск top-k с фильтрами по language и (если нужно) allowed_groups.
  • Проверьте ретривал без LLM: по 20–50 вопросам посмотрите, попадают ли нужные фрагменты в выдачу.
  • Последний пункт принципиален: прежде чем обвинять LLM в “галлюцинациях”, убедитесь, что ретривер вообще приносит правильные чанки.

    Как измерять качество эмбеддингов и индекса без сложной теории

    Для локального проекта полезнее всего простая дисциплина измерений.

    Соберите тестовый набор:

  • 30–200 вопросов от реальных пользователей.
  • Для каждого вопроса отметьте 1–3 “правильных” источника (документ или раздел).
  • Дальше проверяйте метрику “попал ли нужный источник в top-k”:

  • Если не попал, проблема в эмбеддингах, чанкинге, фильтрах или индексе.
  • Если попал, но итоговый ответ плохой, проблема чаще в промпте, формате контекста или LLM.
  • Типовые ошибки при выборе эмбеддингов и индекса

  • Выбрали хорошую LLM, но слабую эмбеддинг-модель, и ретривал не приносит нужные куски.
  • Не учли префиксы query: и passage: и потеряли качество без явных ошибок.
  • Не нормализовали вектора так, как ожидает выбранная метрика, и получили странный порядок выдачи.
  • Не сделали дедупликацию, и top-k заполнен одинаковыми чанками.
  • Не реализовали фильтрацию по доступу, и система стала небезопасной внутри организации.
  • Что дальше

    Теперь у вас есть понимание, как выбрать эмбеддинг-модель и где хранить векторы локально. Следующий шаг курса — собрать полный ретривал-пайплайн: подключить выбранное хранилище, реализовать запросы top-k с фильтрами и научиться отлаживать случаи, когда нужная информация “не находится”.

    4. Retrieval-стратегии: фильтры, rerank, hybrid search и контекст

    Retrieval-стратегии: фильтры, rerank, hybrid search и контекст

    Связь с предыдущими темами курса

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

  • зачем RAG нужен в локальных сценариях и как LLM читает контекст;
  • как подготовить базу знаний (очистка, чанкинг, метаданные);
  • как выбрать эмбеддинги и векторное хранилище.
  • Теперь вы уже можете построить базовый пайплайн: вопрос → эмбеддинг → top-k из векторного индекса → вставка в промпт → ответ. На практике этот baseline почти всегда даёт два типовых провала:

  • нужный фрагмент есть в базе, но не попадает в top-k;
  • нужный фрагмент попадает, но в контексте слишком много шума, и LLM отвечает хуже.
  • Эта статья — про retrieval-стратегии, которые переводят RAG из «демо работает» в «система стабильно полезна»: фильтры, гибридный поиск, rerank и сборка контекста.

    !Общая карта retrieval-стратегий: от запроса до финального контекста для LLM

    Что такое retrieval-стратегия в RAG

    Retrieval-стратегия — это набор решений, которые определяют:

  • где и как искать кандидатов (векторно, по ключевым словам, или вместе);
  • какие ограничения применить (язык, продукт, актуальность, доступ);
  • как переупорядочить найденное (rerank);
  • как сформировать контекст, чтобы LLM получила «сигнал», а не «шум».
  • Важно разделять два этапа:

  • Recall-этап: «принести всё потенциально полезное». Тут важнее не потерять нужное.
  • Precision-этап: «оставить только то, что реально отвечает на вопрос». Тут важнее отсечь лишнее.
  • Векторный top-k часто хорош для recall, а rerank и грамотная сборка контекста — для precision.

    Фильтры по метаданным: быстрый рост качества без усложнения моделей

    Фильтры — это способ сказать ретриверу: ищи только в подмножестве данных, где вообще может быть правильный ответ.

    Какие фильтры обычно дают наибольший эффект

  • Язык: language=ru для русскоязычного запроса.
  • Домен/продукт/команда: product=payments, team=infra.
  • Актуальность:
  • - исключить устаревшее по updated_at; - предпочесть «последнюю версию» документа.
  • Тип источника: doc_type=policy, doc_type=faq, doc_type=runbook.
  • ACL (доступ): allowed_groups и/или visibility.
  • Практический эффект: фильтры часто уменьшают конкуренцию между «похожими» документами из разных областей. В локальных корпоративных базах это один из главных источников ошибок: правильный фрагмент вытесняется семантически похожим, но из другого продукта или отдела.

    Pre-filter и post-filter

    Есть два способа применять ограничения:

  • Pre-filter: ограничение применяется до поиска (поиск идёт только по разрешённым объектам).
  • - Плюсы: выше качество и безопасность (особенно для ACL), меньше шума. - Минусы: нужна поддержка фильтров в хранилище/индексе.
  • Post-filter: сначала ищем, потом отфильтровываем.
  • - Плюсы: проще реализовать. - Минусы: можно «потерять» релевантное, если top-k забит тем, что потом отфильтруется.

    Для ACL почти всегда нужен pre-filter, иначе вы рискуете утечкой через:

  • логирование результатов поиска;
  • отладочные эндпоинты;
  • «случайное» отображение источников.
  • Где посмотреть, как фильтры реализованы на практике

  • Qdrant: Filtering — фильтры по payload (метаданным) в векторном поиске.
  • pgvector: Documentation — пример, как векторы живут рядом с SQL-фильтрами.
  • Hybrid search: векторный + ключевой поиск

    Чистый векторный поиск хорошо ловит смысловые перефразирования, но часто проигрывает ключевому поиску в ситуациях:

  • редкие термины, артикулы, коды ошибок, имена классов;
  • точные формулировки из регламентов;
  • запросы с отрицаниями и условиями («не делать X», «кроме случая Y»).
  • Hybrid search комбинирует:

  • семантический поиск (эмбеддинги);
  • лексический поиск (обычно BM25).
  • Что такое BM25 в одном абзаце

    BM25 — семейство алгоритмов ранжирования для поиска по словам. Интуитивно:

  • документ получает больше очков, если содержит слова из запроса;
  • редкие слова важнее частых;
  • слишком длинные документы не должны получать преимущество просто из-за размера.
  • Если вам нужен промышленный ключевой поиск локально, самые популярные движки на базе Lucene:

  • Elasticsearch: Documentation
  • OpenSearch: Documentation
  • Типовая архитектура hybrid search

    Есть два устойчивых варианта:

  • Два индекса (векторный и текстовый) + слияние результатов
  • - Векторное хранилище (например, Qdrant/FAISS/pgvector). - Текстовый движок (например, OpenSearch/Elasticsearch). - На запросе вы получаете два списка кандидатов и объединяете.

  • Один движок, который умеет sparse + dense
  • - Это возможно, но конкретные реализации и удобство зависят от выбранного хранилища.

    Для начала проще и понятнее вариант с двумя индексами.

    Как «склеивать» результаты без сложной математики

    Проблема: у векторного поиска и BM25 разные шкалы скоринга. Поэтому часто используют простую ранговую логику:

  • берём топ из вектора (например, 30) и топ из BM25 (например, 30);
  • объединяем по document_id+chunk_id;
  • даём кандидатам очки за высокие места в каждом списке;
  • сортируем по сумме очков.
  • Это не «идеально оптимально», но работает удивительно хорошо как baseline, особенно если дальше вы всё равно делаете rerank.

    Когда hybrid search особенно полезен в локальном RAG

  • база содержит много кода, логов, конфигов, таблиц;
  • пользователи часто спрашивают «как называется…», «где параметр…», «что значит ошибка…»;
  • есть длинные регламенты, где важна точная формулировка.
  • Rerank: как резко повысить точность, не увеличивая базу знаний

    Даже хороший ретривер часто приносит «почти то»: фрагменты из правильного документа, но не из того раздела, или фрагменты с близкой темой, но не отвечающие на вопрос.

    Reranker решает это так:

  • вы сначала делаете быстрый поиск и получаете кандидатов (например, 50 чанков);
  • затем более точная (и более дорогая) модель переоценивает пары запрос–кандидат и сортирует заново;
  • в контекст уходит уже top-n (например, 5–10 чанков).
  • Почему rerank почти всегда точнее, чем «просто top-k по эмбеддингам»

    Эмбеддинги сравнивают запрос и документ по отдельности (би-энкодер). Реранкер (обычно кросс-энкодер) смотрит на запрос и кандидат вместе и может учитывать:

  • условия и ограничения в вопросе;
  • отрицания;
  • соответствие конкретному пункту инструкции;
  • тонкие различия «похоже, но не про это».
  • Практические модели для локального rerank

  • Sentence-Transformers: Cross-Encoders — как устроены кросс-энкодеры и как ими пользоваться.
  • cross-encoder/ms-marco-MiniLM-L-6-v2 — популярный компактный реранкер (в первую очередь для английского).
  • BAAI/bge-reranker-base — распространённый реранкер семейства BGE.
  • Выбор реранкера так же, как и эмбеддингов, лучше делать на своём тест-наборе вопросов.

    Бюджет на rerank: сколько кандидатов переоценивать

    Ниже — рабочие стартовые числа, которые легко объяснить и отладить:

  • candidate_pool = 50 (после вектора или гибрида)
  • final_context = 5..10
  • Если вы видите, что правильный чанк часто «на 60–80 месте», увеличьте candidate_pool. Если правильный чанк есть в пуле, но не попадает в топ после rerank — проблема в реранкере или в том, что кандидаты слишком похожи и нужны дополнительные сигналы (фильтры/гибрид/метаданные).

    Важный нюанс: rerank по чанкам или по документам

    Есть два паттерна:

  • Rerank по чанкам: ранжируем фрагменты текста напрямую.
  • - Плюсы: точнее для «найди конкретный пункт». - Минусы: может теряться цельная картина документа.
  • Rerank по документам: сначала выбираем документы, затем берём из них лучшие чанки.
  • - Плюсы: полезно, когда ответы требуют контекста всего документа. - Минусы: сложнее пайплайн.

    Для старта почти всегда проще и эффективнее rerank по чанкам.

    Контекст для LLM: как превратить найденные чанки в ответ, а не в шум

    Retrieval заканчивается не на «нашли top-k». Нужно собрать контекст так, чтобы:

  • LLM быстро поняла, что является источником;
  • фрагменты не противоречили друг другу;
  • не было дублей;
  • контекст уложился в токен-бюджет.
  • Шаблон контекста, который легко масштабируется

    Рабочий формат:

  • короткая инструкция модели;
  • блок «Источники» с пронумерованными чанками;
  • вопрос пользователя;
  • требование цитировать источники по номерам.
  • Например (упрощённо):

    Ключевое: источники явно отделены от инструкций. Это уменьшает риск, что модель «перепутает» политику ответа с текстом документа.

    Дедупликация и «разнообразие» контекста

    Если в топе много почти одинаковых чанков, контекст становится бедным: модель видит одно и то же разными словами и теряет шанс получить дополнительную деталь.

    Простые приёмы:

  • дедуп по хэшу текста (или по checksum из ingestion);
  • лимит на документ: например, максимум 2 чанка с одного document_id;
  • перемешивание доменов: если у вас несколько типов источников (FAQ + регламент), можно гарантировать присутствие обоих.
  • Расширение чанка «соседями» (chunk expansion)

    Частая проблема: retrieved-чанк содержит определение, но рядом в документе — важное исключение или пример. Решение:

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

    Учет токен-бюджета: что резать первым

    Если контекст не влезает, режьте в таком порядке:

  • убирайте дубли и близнецов;
  • уменьшайте число источников (например, с 10 до 5);
  • сокращайте длинные источники, оставляя предложения, где встречаются ключевые термины запроса;
  • только потом уменьшайте инструкцию и служебные поля.
  • Практический ориентир: лучше 4–6 очень релевантных чанков, чем 15 средних.

    «Контекст должен быть актуальным»: как бороться с противоречиями

    Даже при хорошем поиске модель может увидеть два источника с разными версиями правила. Рабочие меры:

  • фильтровать по updated_at или по document_version;
  • приоритетить источники с более свежей датой;
  • если вы храните несколько версий, явно добавлять метку версии в текст источника, чтобы модель могла сослаться на актуальную.
  • Рекомендуемый baseline retrieval-пайплайна для локального RAG

    Ниже — последовательность, которую удобно реализовывать и профилировать по шагам.

  • Определить язык запроса и контекст пользователя (роль/группа/продукт).
  • Применить pre-filter по language и ACL.
  • Получить кандидатов одним из способов:
  • - только векторный поиск; - hybrid: вектор + BM25.
  • Объединить кандидатов и сделать лёгкую дедупликацию.
  • Применить rerank по top candidate_pool.
  • Сформировать финальный набор источников:
  • - лимит на документ; - при необходимости добавить соседние чанки.
  • Собрать контекст в строгом формате «источники отдельно».
  • Попросить модель отвечать только на основе источников и возвращать ссылки.
  • Этот baseline хорошо масштабируется: вы можете улучшать один блок (например, rerank), не ломая остальные.

    Типовые симптомы и быстрые способы диагностики

    Чтобы не «лечить не то», полезно различать симптомы.

    Симптом: «модель врёт», но источники не содержат ответа

    Почти всегда это retrieval-провал. Проверьте:

  • правильный ли language фильтр;
  • не слишком ли жёсткие фильтры (например, продукт не тот);
  • хватает ли candidate_pool до rerank;
  • не убивает ли дедуп или лимит на документ нужный фрагмент.
  • Симптом: правильный фрагмент есть, но модель не использует его

    Чаще это контекст/промпт:

  • источники смешаны с инструкциями;
  • слишком много источников и они противоречат;
  • в источниках нет структуры (непонятно, что важнее);
  • в контексте много мусора (колонтитулы, навигация).
  • Симптом: поиск приносит «похожее из другой области»

    Это почти всегда отсутствие или слабость фильтров. Лечится метаданными и pre-filter.

    Что дальше по курсу

    После того как retrieval-стратегии выстроены, следующий логичный шаг — эксплуатация и качество:

  • как измерять качество retrieval (попадание нужного источника в top-k, влияние rerank);
  • как логировать запросы и результаты безопасно;
  • как отлаживать провалы и улучшать базу знаний итерациями.
  • 5. Генерация с контекстом: промптинг, цитирование, антигаллюцинации

    Генерация с контекстом: промптинг, цитирование, антигаллюцинации

    Связь с предыдущими темами

    Вы уже умеете:

  • готовить базу знаний (очистка, чанкинг, метаданные);
  • выбирать эмбеддинги и векторные индексы;
  • строить retrieval-стратегии (фильтры, hybrid search, rerank) и собирать релевантные чанки.
  • Остаётся ключевой этап, который превращает найденные фрагменты в надёжный ответ: генерация с контекстом. На этом этапе чаще всего появляются проблемы, которые пользователи называют «галлюцинациями», хотя причины обычно приземлённые:

  • контекст вставлен в промпт так, что модель путает источники с инструкциями;
  • источники не структурированы, дублируются или противоречат;
  • в запросе нет строгого требования отвечать только по источникам;
  • нет механизма цитирования, поэтому невозможно проверить ответ.
  • Цель статьи: научиться проектировать промпт и формат контекста так, чтобы локальная LLM:

  • опиралась на retrieved-чанки;
  • цитировала источники;
  • честно говорила «не знаю», когда данных в контексте нет.
  • !Схема показывает, где именно промптинг и цитирование стоят в общем RAG-процессе

    Что такое «генерация с контекстом» в RAG

    В RAG генерация почти никогда не должна быть «свободной». Она должна быть grounded: то есть привязанной к явно предоставленным источникам.

    Практически это означает, что ваш промпт обычно включает:

  • инструкцию как отвечать;
  • блок источников (чанки, которые вы retrieved);
  • вопрос пользователя;
  • требования к формату ответа (включая цитаты).
  • Если retrieval принёс правильные чанки, но генерация всё равно «уехала», значит проблема, скорее всего, в формате контекста и инструкциях, а не в модели.

    Иерархия инструкций: почему модель «не слушается»

    Даже локальные LLM, запущенные через разные движки и обёртки, обычно используют структуру сообщений, похожую на:

  • system: правила поведения ассистента;
  • user: запрос пользователя;
  • assistant: ответ.
  • Ключевая идея: системные инструкции должны быть сильнее пользовательских, а источники должны быть отделены от инструкций.

    Типовая ошибка: вы добавляете контекст как обычный текст без границ, и модель начинает воспринимать фразы из документов как новые инструкции.

    Базовый шаблон промпта для RAG с цитированием

    Ниже шаблон, который хорошо работает как baseline почти в любом домене. Он намеренно строгий и слегка «канцелярский»: это уменьшает творческое поведение модели.

    Почему в шаблоне именно так:

  • источники пронумерованы, чтобы модель могла ссылаться на них коротко;
  • есть явное правило «не знаю», чтобы модель не пыталась «дотянуть логикой»;
  • формат ответа задан заранее, чтобы снизить вариативность.
  • Как правильно оформлять источники, чтобы модель могла цитировать

    Нумерация и стабильные идентификаторы

    Для цитирования нужны два уровня:

  • внутренняя ссылка: [1], [2] — для текста ответа;
  • стабильная привязка: метаданные чанка, чтобы пользователь (и вы) могли открыть первоисточник.
  • Минимально полезные поля рядом с чанком в источнике:

  • source (URL, путь, название системы);
  • document_id и chunk_id;
  • title или section_path;
  • updated_at (для борьбы с устаревшими версиями).
  • Не перегружайте источники служебными полями

    Если вы вставите в каждый источник огромный JSON метаданных, вы:

  • потратите токены;
  • увеличите шум;
  • повысите риск, что модель начнёт повторять метаданные вместо ответа.
  • Практика: показывайте модели только те поля, которые помогают выбрать правильный источник и сослаться на него.

    Явные границы источников

    Каждый источник должен быть визуально отделён. Рабочие варианты:

  • отдельная строка с [n];
  • одинаковый префикс метаданных;
  • пустая строка между источниками.
  • Это снижает риск, что модель «склеит» два чанка в одну мысль и исказит смысл.

    Антигаллюцинации: что реально работает в локальном RAG

    Галлюцинации в RAG чаще всего появляются по одной из причин:

  • модель не нашла ответ в источниках, но «не умеет» признавать неопределённость;
  • ответ частично есть, но модель добавляет детали для связности;
  • в источниках противоречия, и модель выбирает «самое правдоподобное»;
  • контекст слишком длинный и шумный, и модель теряет важное.
  • Ниже меры, которые дают практический эффект.

    Принцип «ответ только по источникам»

    В системной инструкции должно быть явно сказано:

  • отвечай только по источникам;
  • если нет — скажи «не знаю»;
  • не придумывай.
  • Важно: это должно быть не просьбой, а правилом поведения.

    Обязательное цитирование

    Требование цитировать [n] делает два полезных эффекта:

  • модель чаще «держится» текста источников, потому что ей нужно к чему-то привязаться;
  • вы получаете быстрый сигнал качества: если цитаты странные, значит retrieval или контекст-сборка сломаны.
  • Хорошая формулировка требования:

  • «Каждый факт в ответе должен иметь ссылку на источник [n]».
  • Плохая формулировка:

  • «По возможности добавь источники».
  • Разрешите модели честно отступать

    Если вы хотите меньше галлюцинаций, вы должны разрешить модельный отказ.

    Практические варианты отказа:

  • «Не знаю по предоставленным источникам»;
  • «В источниках нет информации о …»;
  • «Источники противоречат друг другу, поэтому нельзя сделать вывод».
  • Для корпоративных сценариев это часто лучше, чем «уверенный, но неверный» ответ.

    Обработка противоречий и свежести

    Если retrieval приносит два источника с разными правилами, модель может выбрать неверное. Поэтому полезно:

  • в источниках показывать updated_at;
  • в инструкции добавить правило: «если источники противоречат, приоритет у более свежего updated_at, но обязательно укажи, что есть расхождение».
  • Это не решает проблему версионности в базе знаний полностью (её нужно лечить ingestion и фильтрами), но резко повышает корректность поведения на краю.

    Контроль длины ответа и температуры

    У локальных LLM параметры генерации заметно влияют на склонность «добавлять от себя».

    Практические рекомендации:

  • уменьшайте temperature, если нужен фактологичный стиль;
  • ограничивайте max_tokens (или аналог), чтобы модель не уходила в рассуждения;
  • используйте стоп-маркеры, если у вас есть строгий формат.
  • Важно: параметры не заменяют правильный контекст. Они только уменьшают ущерб.

    Как собрать контекст так, чтобы он помогал, а не мешал

    Дедупликация контекста перед генерацией

    Даже после rerank вы можете получить близнецов: одинаковые или почти одинаковые чанки.

    Перед вставкой в промпт примените простые правила:

  • убрать идентичные чанки по checksum или хэшу текста;
  • ограничить число чанков с одного document_id (например, максимум 2);
  • приоритетить разнообразие источников, если ваш ответ обычно требует «политика + пример + FAQ».
  • Принцип «лучше меньше, но точнее»

    Для большинства локальных моделей и задач:

  • 4–8 сильных источников обычно лучше, чем 15 средних;
  • длинный контекст ухудшает точность, даже если «в нём где-то есть ответ».
  • Если вам постоянно хочется добавлять ещё источников, это сигнал, что:

  • чанкинг слишком мелкий или слишком крупный;
  • retrieval не хватает точности;
  • нужен rerank или hybrid.
  • Добавление соседних фрагментов без развала цитирования

    Если retrieved-чанк часто «обрывает» мысль, можно добавить соседний чанк слева или справа. Но важно не потерять контролируемость:

  • соседний фрагмент должен иметь свой номер источника;
  • полезно пометить его как neighbor_of=[n] в метаданных строки источника.
  • Так вы сохраняете проверяемость: пользователь видит, что часть контекста добавлена как расширение.

    Два режима ответа: кратко и «с расшифровкой»

    Один и тот же RAG-ассистент обычно обслуживает разные потребности:

  • быстрое решение (краткий ответ);
  • проверяемое решение (с обоснованиями и цитатами).
  • Удобная стратегия промпта:

  • всегда начинать с краткого ответа в 1–3 предложения;
  • затем давать 3–7 пунктов обоснований;
  • в каждом пункте добавлять цитаты.
  • Это помогает и пользователям, и отладке.

    Практический «боевой» шаблон ответа с цитированием

    Ниже пример формата, который легко валидировать автоматически.

    Почему «Источники» в конце полезны:

  • пользователь видит, откуда взялось;
  • вы можете логировать только список источников (без полного текста) для безопасной диагностики.
  • Типовые ошибки промптинга в RAG

  • Инструкция и источники смешаны, и модель начинает «следовать документу как правилам».
  • Нет явного разрешения сказать «не знаю», и модель компенсирует неопределённость выдумкой.
  • Источники без нумерации, и модель ссылается расплывчато («в документах сказано…»).
  • Источники слишком длинные, без структуры, и модель выбирает случайные предложения.
  • Контекст содержит противоречивые версии, но в промпте нет правила приоритета свежести.
  • Минимальный чеклист перед продакшеном

  • Системная инструкция включает: «только по источникам», «не знаю», «цитируй».
  • Источники отделены от инструкций, пронумерованы, содержат минимальные метаданные.
  • Есть дедупликация и лимит по документам.
  • Есть стратегия на противоречия: свежесть, пометка неопределённости.
  • Параметры генерации настроены под фактологичность (низкая вариативность).
  • Логи позволяют увидеть: вопрос, список источников, и итоговый ответ.
  • Полезные ссылки

  • Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks (Lewis et al., 2020)
  • LangChain: Concepts — Text splitters
  • Sentence-Transformers: Cross-Encoders
  • 6. Оценка и улучшение RAG: метрики, тест-наборы, наблюдаемость

    Оценка и улучшение RAG: метрики, тест-наборы, наблюдаемость

    Связь с предыдущими темами курса

    В предыдущих статьях вы построили почти полный локальный RAG-стек:

  • подготовили базу знаний (очистка, чанкинг, метаданные);
  • выбрали эмбеддинги и векторное хранилище;
  • настроили retrieval-стратегии (фильтры, hybrid search, rerank, сборка контекста);
  • собрали промпт и формат ответа с цитированием, чтобы снизить галлюцинации.
  • Теперь возникает практическая проблема: RAG можно сделать работающим, но сложно сделать стабильно качественным. Качество будет плавать из-за обновления документов, смены модели, новых запросов пользователей, изменений в чанкинге и фильтрах.

    Эта статья про то, как превратить улучшение RAG из «похоже стало лучше» в инженерный процесс:

  • какие метрики реально помогают;
  • как собирать тест-наборы, которые ловят регрессии;
  • как настроить наблюдаемость, чтобы быстро понимать, где именно сломалось: ingestion, retrieval или генерация.
  • !Цикл «оценка → диагностика → улучшение» для RAG

    Что именно нужно измерять в RAG

    У RAG-пайплайна есть минимум три уровня, и у каждого свои метрики:

  • Качество retrieval: нашёл ли ретривер те фрагменты, на которых вообще можно ответить.
  • Качество генерации на найденном контексте: использует ли LLM источники, не придумывает ли лишнее, корректно ли цитирует.
  • Системные показатели: задержки, стабильность, доля отказов, нагрузка, безопасность.
  • Ключевой принцип отладки:

  • если правильного фрагмента нет среди кандидатов, это проблема retrieval;
  • если фрагмент найден, но ответ плохой, это проблема контекста/промпта/LLM;
  • если всё правильно, но медленно или нестабильно, это проблема инфраструктуры и наблюдаемости.
  • Тест-наборы для RAG: что собирать, чтобы это работало

    Почему «просто 20 вопросов» недостаточно

    Если тест-набор маленький и однотипный, вы улучшите качество на нём, но получите регрессии в реальности. Поэтому тест-набор для RAG должен:

  • представлять реальные классы запросов;
  • фиксировать права доступа и контекст пользователя (если есть ACL);
  • проверять не только «ответ красивый», но и «источники корректные».
  • Минимальная структура тест-кейса

    Один тест-кейс обычно содержит:

  • question: вопрос пользователя;
  • user_context: роль/группа/язык/продукт (всё, что влияет на фильтры);
  • expected_sources: 1–3 ожидаемых источника (например, document_id или source), чтобы оценивать retrieval;
  • expected_answer_points: 2–6 фактов или тезисов, которые должны быть в ответе (опционально, но полезно);
  • notes: комментарии, почему кейс важен.
  • Важно: для RAG часто проще и надёжнее разметить ожидаемые источники, чем «идеальный текст ответа». Текст ответа может отличаться по стилю, но правильные источники и факты должны совпадать.

    Какие типы вопросов стоит включать

    Чтобы тест-набор ловил реальные провалы, в нём должны быть разные классы:

  • вопросы на точные формулировки (политики, регламенты);
  • вопросы с редкими терминами (коды ошибок, параметры конфигов);
  • вопросы на исключения и ограничения (условия «кроме случая…»);
  • вопросы, где ответа нет в базе (проверка честного «не знаю»);
  • вопросы с конфликтом версий (проверка свежести и поведения при противоречиях).
  • Размер и версия тест-набора

    Практичный ориентир для старта:

  • smoke (быстро прогонять на каждом изменении): 20–50 кейсов;
  • regression (ежедневно/перед релизом): 200–1000 кейсов;
  • domain suite (по доменам: продукт, команда): отдельные подборки по 50–300.
  • Обязательно версионируйте тест-набор: если база знаний обновилась, ожидаемые источники тоже могут поменяться.

    Метрики retrieval: как понять, что поиск приносит нужное

    Hit@k и Recall@k: самый полезный старт

    Если для вопроса вы знаете «правильный источник» (документ или чанк), то базовая метрика звучит так:

  • Hit@k: попал ли хотя бы один правильный источник в топ-k.
  • Часто это называют Recall@k в практическом смысле «нашли ли нужное», особенно если у вопроса несколько допустимых источников.

    Если всё-таки хочется формально записать Recall@k, можно использовать простую формулу:

    Где:

  • — множество релевантных (правильных) источников для вопроса;
  • — множество источников, которые вернул ретривер в первых k;
  • — количество элементов в множестве.
  • Интерпретация:

  • значение 1 означает, что все ожидаемые источники нашлись в топ-k;
  • 0 означает, что ретривер «принёс не то», и генерация почти обречена.
  • Precision@k: когда важно «меньше шума»

    Precision@k отвечает на вопрос: какая доля топ-k реально релевантна.

    Эта метрика полезна, когда retrieval приносит «много похожего», и контекст становится шумным. Но для RAG чаще сначала чинят recall (чтобы не терять нужное), а затем precision (чтобы не портить контекст).

    MRR: когда важен порядок в выдаче

    MRR (Mean Reciprocal Rank) важна, если вы хотите, чтобы правильный чанк был как можно ближе к началу.

    Интуиция:

  • если правильный источник на 1 месте, это отлично;
  • если на 20 месте, он может не попасть в контекст после rerank и ограничений по токенам.
  • nDCG: когда релевантность бывает «разной силы»

    nDCG полезна, если вы размечаете источники градациями, например:

  • 3 балла: «прямо отвечает на вопрос»;
  • 2 балла: «содержит важную часть»;
  • 1 балл: «слабосвязанный контекст»;
  • 0 баллов: «мимо».
  • Тогда nDCG оценивает качество ранжирования с учётом того, насколько релевантен каждый элемент и на каком месте он стоит.

    Метрики по этапам: до и после rerank

    Если вы используете rerank, измеряйте retrieval минимум в двух точках:

  • качество кандидатов на recall-этапе (до rerank, например top-50);
  • качество финального набора (после rerank, например top-5 или top-10).
  • Типовой вывод диагностики:

  • если правильное часто есть в top-50, но пропадает в top-5, значит проблема в reranker или в правилах сборки контекста;
  • если правильного нет уже в top-50, значит проблема в эмбеддингах, чанкинге, фильтрах или hybrid-компоненте.
  • Метрики генерации: как оценить «привязку к источникам»

    Для локального RAG в корпоративных сценариях генерация обычно должна быть grounded: без фактов, которых нет в источниках.

    Корректность цитирования

    Если вы требуете ссылки вида [1], [2], то появляется измеримая вещь: корректно ли ассистент ссылается.

    Практические проверки:

  • Citation coverage: у каждого существенного факта есть ссылка;
  • Citation validity: номер источника существует в контексте;
  • Citation support: текст источника реально подтверждает утверждение.
  • Последний пункт часто проверяют либо вручную на выборке, либо дополнительной моделью-«судьёй». В локальных сценариях разумно начинать с ручной проверки на 30–100 примерах и постепенно автоматизировать.

    Faithfulness: нет ли «придуманных деталей»

    В прикладной форме faithfulness означает: ответ не добавляет фактов, которых нет в retrieved-чанках.

    Сигналы, что faithfulness падает:

  • модель уверенно добавляет числа, сроки, названия систем, которых нет в источниках;
  • модель «склеивает» два источника и делает вывод, который в них не написан;
  • модель не умеет говорить «не знаю по источникам».
  • Доля корректных отказов

    Отдельная метрика для антигаллюцинаций:

  • если ответа нет в базе, ассистент должен отказать;
  • если ответ есть, ассистент не должен «перестраховываться» отказом.
  • Это две разные ошибки:

  • ложный ответ (опаснее);
  • лишний отказ (хуже UX, но безопаснее).
  • В продакшене часто задают целевой компромисс и отслеживают обе доли.

    Автоматизированные фреймворки оценки

    Если вы хотите ускорить автоматизацию, посмотрите:

  • Ragas — популярный набор метрик для RAG (качество контекста, groundedness и другие), обычно использует LLM как оценщика.
  • LangChain: Evaluation — концепты и подходы к оценке цепочек.
  • LlamaIndex: Evaluation — подходы к оценке retrieval и ответов.
  • Важно: автоматическая оценка LLM-«судьёй» может ошибаться. В локальном RAG полезно иметь слой ручной валидации, особенно для критичных доменов.

    Онлайн-метрики: что измерять в продакшене

    Оффлайн тесты ловят регрессии, но реальное качество проявляется в живом трафике.

    Практичный набор онлайн-метрик:

  • Latency по этапам: эмбеддинг запроса, поиск, rerank, генерация.
  • Контекстный бюджет: сколько токенов ушло на источники и инструкции.
  • Доля запросов с пустым/слабым retrieval: например, когда top-k ниже порога сходства.
  • Доля ответов с отказом: и как пользователи реагируют.
  • Повторные вопросы: пользователь переспрашивает сразу после ответа (сигнал плохого качества).
  • CTR по источникам: кликают ли на источники, открывают ли документы (если UI это позволяет).
  • Наблюдаемость RAG: как логировать и трассировать без хаоса

    Что такое наблюдаемость в RAG

    Наблюдаемость — это способность по логам, метрикам и трассам восстановить:

  • что система сделала на конкретном запросе;
  • какие документы она использовала;
  • где потратила время;
  • почему ответ получился таким.
  • Для RAG это особенно важно, потому что пайплайн многошаговый, и «просто логировать промпт» часто нельзя из-за приватности.

    Что логировать на каждом запросе

    Если упростить, вам нужен минимальный трассируемый след.

    Обычно логируют:

  • request_id и время;
  • безопасный отпечаток вопроса (например, хэш) и язык;
  • применённые фильтры (продукт, ACL-группы, версии);
  • результаты retrieval: список document_id/chunk_id, скоринг, позиции в ранжировании;
  • результаты rerank: новый порядок, скоринг;
  • финальный список источников, который ушёл в LLM;
  • параметры генерации (temperature, лимит токенов);
  • тайминги каждого шага;
  • признак отказа («не знаю») и наличие цитат.
  • По возможности не храните полный текст вопроса и источников в логах без политики безопасности. Часто достаточно хранить идентификаторы чанков и ссылку на документ.

    Трассировка пайплайна

    Трассировка позволяет увидеть один запрос как «цепочку спанов» (этапов): embedding, vector search, bm25 search, merge, rerank, prompt assembly, generation.

    Базовый стандарт:

  • OpenTelemetry — спецификация и инструменты для метрик и трасс.
  • Инструменты, которые часто используют для LLM/RAG наблюдаемости:

  • Langfuse — трейсинг, промпты, датасеты, оценка.
  • Arize Phoenix — наблюдаемость и отладка LLM-приложений.
  • Выбор инструмента не так важен, как дисциплина: фиксировать одинаковые поля и уметь по request_id восстановить весь путь.

    Дашборды и алерты: что считать «поломкой»

    Полезные алерты для RAG в продакшене:

  • рост p95/p99 latency на retrieval или generation;
  • рост доли запросов, где нет релевантных источников (по порогу скоринга);
  • рост доли ответов без цитат (если цитаты обязательны);
  • рост ошибок парсинга/ингеста (если у вас регулярные обновления базы знаний).
  • Как улучшать RAG системно: диагностическая карта

    Если retrieval не находит нужное

    Типовые рычаги улучшения, в порядке «дешевле → дороже»:

  • проверить и усилить метаданные и pre-filter (язык, продукт, ACL, свежесть);
  • исправить очистку и дедупликацию (часто в индексе слишком много мусора);
  • поменять стратегию чанкинга и overlap;
  • перейти на hybrid search, если много кодов/артикулов/ключевых терминов;
  • заменить эмбеддинг-модель или правильно включить префиксы query:/passage: (если модель их ожидает);
  • увеличить candidate_pool перед rerank.
  • Если retrieval находит, но LLM отвечает плохо

    Чаще всего помогают:

  • строгий формат источников (нумерация, границы, минимум метаданных);
  • жёсткое требование «только по источникам» и «иначе не знаю»;
  • дедуп и лимит на документ в контексте;
  • добавление соседних чанков для важных фрагментов;
  • снижение вариативности генерации (например, ниже temperature).
  • Если качество нестабильно со временем

    Проверьте:

  • версионность документов и удаление старых чанков при переиндексации;
  • дрейф базы знаний: появились новые формулировки, которых не было в тест-наборе;
  • изменение поведения пользователей (новые типы вопросов).
  • Практика: часть тест-набора должна регулярно пополняться из реальных логов (после обезличивания и проверки прав).

    Практический baseline: как организовать оценку как пайплайн

    Ниже схема, которую удобно внедрять итеративно.

  • Соберите smoke-набор из 30–50 вопросов и разметьте ожидаемые источники.
  • Реализуйте прогон retrieval без LLM и считайте Hit@k/Recall@k.
  • Подключите генерацию с цитированием и начните измерять долю ответов с валидными ссылками.
  • Настройте логирование request_id → retrieved chunk_ids → final context chunk_ids → answer.
  • Перед каждым изменением (чанкинг, эмбеддинги, фильтры, rerank, промпт) прогоняйте regression-набор и сравнивайте метрики с базовой версией.
  • Небольшой псевдокод, чтобы было ясно, что именно сравнивать:

    Главная идея статьи

    RAG улучшается быстрее всего, когда вы:

  • измеряете retrieval отдельно от генерации;
  • храните тест-набор с ожидаемыми источниками и контекстом пользователя;
  • включаете наблюдаемость, чтобы любой плохой ответ был «разбираем по косточкам»;
  • делаете улучшения маленькими шагами и проверяете, что нет регрессий.
  • Это и есть переход от «локальный ассистент иногда помогает» к «локальный ассистент надёжен и поддерживаем».

    7. Локальный продакшн: оптимизация, приватность, безопасность и деплой

    Локальный продакшн: оптимизация, приватность, безопасность и деплой

    Связь с предыдущими темами курса

    В предыдущих статьях вы собрали полный RAG-пайплайн на уровне логики:

  • подготовили базу знаний (ингест, очистка, чанкинг, метаданные);
  • выбрали эмбеддинги и векторный индекс;
  • настроили retrieval-стратегии (фильтры, hybrid search, rerank, сборка контекста);
  • спроектировали генерацию с цитированием и антигаллюцинациями;
  • научились оценивать качество и добавили наблюдаемость.
  • Теперь остаётся шаг, который отличает лабораторный прототип от системы, которой доверяют в компании: локальный продакшн. Здесь “локальность” означает не только то, что модель запущена на ваших серверах, но и то, что вы контролируете:

  • производительность и стоимость;
  • приватность данных;
  • безопасность (включая ACL, защиту от prompt injection и цепочку поставки моделей);
  • воспроизводимый деплой и обновления.
  • !Карта компонентов локального RAG-продакшена и точек контроля безопасности и наблюдаемости

    Что значит “продакшн” для локального RAG

    Продакшн-готовность для RAG удобно проверять по четырём группам требований:

  • Скорость и стабильность: предсказуемые задержки, понятный бюджет по токенам, контроль нагрузки.
  • Качество: минимальные регрессии при обновлениях базы знаний, эмбеддингов и LLM.
  • Приватность и безопасность: корректный ACL, отсутствие утечек через логи и источники, защита от атак на промпт.
  • Операционная управляемость: деплой, конфигурации, обновления, откаты, мониторинг.
  • Дальше разберём эти блоки как набор практических решений.

    Оптимизация: где реально тратится время и как ускорять

    Локальный RAG обычно состоит из таких этапов по времени:

  • вычисление эмбеддинга запроса;
  • поиск кандидатов (vector и, если есть, BM25);
  • rerank (если используется);
  • сборка контекста;
  • генерация LLM.
  • Профилирование по шагам

    Правило эксплуатации: оптимизируйте только то, что измерили.

    Минимальный набор таймингов, который стоит логировать на каждый запрос:

  • t_embed_query — эмбеддинг запроса;
  • t_retrieve_vector — векторный поиск;
  • t_retrieve_bm25 — ключевой поиск (если есть);
  • t_rerank — реранк;
  • t_prompt_build — сборка промпта;
  • t_generate_first_token — время до первого токена;
  • t_generate_total — полное время генерации;
  • prompt_tokens и completion_tokens — токен-бюджет.
  • Для трассировки удобно использовать OpenTelemetry, чтобы видеть один запрос как цепочку спанов.

    Оптимизация retrieval: меньше шума, меньше токенов, быстрее ответы

    Самые “дешёвые” ускорения обычно дают не GPU, а дисциплина данных:

  • Агрессивная дедупликация чанков перед записью в индекс и перед сборкой контекста.
  • Фильтры pre-filter по language, домену, продукту, updated_at, allowed_groups.
  • Ограничение на документ: например, максимум 2 чанка на document_id в финальном контексте.
  • Практический эффект: меньше кандидатов, меньше контекста, меньше времени на генерацию и меньше шансов “утонуть” в нерелевантных источниках.

    Оптимизация rerank: точность без “убийства” задержек

    Rerank почти всегда повышает точность, но его цена быстро растёт с числом кандидатов.

    Рабочий компромисс для старта:

  • candidate_pool = 30..80;
  • final_context = 4..10.
  • Если rerank дорогой, типовые способы ускорить:

  • уменьшить candidate_pool, но усилить recall через hybrid и фильтры;
  • ранжировать сначала документы, затем чанки (если ваш домен часто требует цельного документа);
  • кэшировать rerank для повторяющихся запросов (если такие есть).
  • Оптимизация LLM-инференса: что даёт наибольший выигрыш

    У генерации обычно два разных “узких места”:

  • время до первого токена: сильно зависит от длины входного контекста;
  • скорость токенов в секунду: зависит от движка и железа.
  • Практические рычаги:

  • Контроль токен-бюджета: чаще всего ускорение достигается уменьшением prompt_tokens, а не снижением max_tokens ответа.
  • Квантование: позволяет уместить модель и ускорить инференс на ограниченном железе, но требует проверки качества на вашем тест-наборе.
  • Выбор движка:
  • - llama.cpp часто используют для CPU/смешанных сценариев и квантованных весов. - vLLM часто выбирают для серверного инференса с хорошей пропускной способностью.

    Кэширование: когда оно безопасно и полезно

    Кэширование в RAG бывает двух типов:

  • Кэш retrieval: “вопрос → список chunk_id”.
  • Кэш генерации: “вопрос + контекст → ответ”.
  • Кэш retrieval часто безопаснее и полезнее, но при условиях:

  • ключ кэша учитывает ACL-контекст (allowed_groups, роль, продукт, язык);
  • TTL небольшой, если база знаний часто обновляется;
  • при переиндексации вы умеете сбрасывать кэш (например, по версии индекса).
  • Кэш генерации даёт экономию, но чаще рискованнее из-за:

  • высокой зависимости от контекста;
  • возможной утечки, если не включить в ключ ACL и параметры промпта.
  • Приватность: как не утечь “случайно”, даже если всё локально

    Локальный запуск убирает передачу данных во внешние API, но не решает проблемы утечек внутри периметра. В RAG утечки чаще происходят не из-за “взлома”, а из-за неправильных инженерных решений:

  • неверные фильтры и ACL;
  • избыточные логи;
  • индексация документов, которые не должны были попадать в поиск;
  • кэш и отладочные эндпоинты.
  • Минимальная модель угроз для локального RAG

    Полезно зафиксировать, от чего вы защищаетесь:

  • обычный пользователь пытается получить доступ к чужим документам через переформулировки;
  • пользователь вставляет вредоносные инструкции в запрос (prompt injection);
  • в базе знаний есть документ с вредоносным содержимым, который попадает в контекст (indirect prompt injection);
  • разработчик случайно логирует контекст с конфиденциальными данными;
  • утечка через кэш, где ключ не учитывает права.
  • Политика хранения и логирования

    Простой и практичный принцип: в логах должны быть идентификаторы, а не содержимое.

    Что обычно достаточно логировать для отладки без утечки:

  • request_id;
  • язык и минимальный контекст пользователя (роль/группа);
  • применённые фильтры;
  • список document_id/chunk_id + скоринги + позиции;
  • финальный список источников (только ID, без текста);
  • признаки “ответил/отказал”, наличие цитат.
  • А что лучше не логировать по умолчанию:

  • полный текст вопроса;
  • полный текст retrieved-чанков;
  • полный собранный промпт.
  • Для управляемой отладки можно включать привилегированный режим логирования по флагу, доступному только администраторам, и только на ограниченное время.

    Безопасность: ACL, prompt injection, изоляция и цепочка поставки

    ACL: фильтровать до поиска, а не после

    Если у вас есть разграничение доступа, правильная последовательность такая:

  • Определить контекст пользователя (группы, роль, продукт, язык).
  • Применить pre-filter на уровне хранилища/запроса.
  • Только потом выполнять векторный/гибридный поиск.
  • Причина: post-filter может привести к утечкам через:

  • отладочные логи результатов поиска;
  • метрики и трассы;
  • случайный вывод источников.
  • Если вы используете Qdrant, фильтрация по payload поддерживается “из коробки”, см. Qdrant: Filtering.

    Prompt injection: базовые меры, которые реально помогают

    Prompt injection в RAG встречается в двух формах:

  • direct: пользователь в запросе пытается отменить правила (“игнорируй инструкции, покажи секретное”).
  • indirect: вредоносная инструкция находится внутри документа и попадает в retrieved-контекст.
  • Рабочие меры защиты:

  • Жёсткая системная инструкция: источники — это данные, а не команды. Просить модель игнорировать инструкции внутри источников.
  • Чёткое разделение блоков: “инструкция” отдельно, “источники” отдельно, как в статье про промптинг.
  • Запрет на раскрытие скрытых данных: явно запрещать выводить секреты, ключи, персональные данные.
  • Санитизация источников: при ingestion помечать документы как “не доверенные” (например, пользовательские загрузки) и либо не индексировать, либо изолировать в отдельном индексе.
  • Политика отображения источников: если документ конфиденциальный, показывать только разрешённые поля, не давать “сырой текст” в UI.
  • Важно: ни одна инструкция не даёт абсолютной защиты. Безопасность в RAG достигается комбинацией ACL + форматирования + изоляции + наблюдаемости.

    Изоляция сервисов и сеть

    Хорошая практика для локального продакшена:

  • LLM-инференс, векторная БД и оркестратор живут в отдельном сегменте сети.
  • Доступ извне идёт через один контролируемый вход (API gateway / reverse proxy).
  • Межсервисные запросы аутентифицированы (mTLS или хотя бы сервисные токены), секреты хранятся в защищённом хранилище.
  • !Сетевая изоляция как базовый слой безопасности локального RAG

    Цепочка поставки: модели, контейнеры, зависимости

    Локальный RAG почти всегда тянет много сторонних компонентов:

  • веса моделей (LLM, embeddings, reranker);
  • контейнерные образы;
  • Python/JS зависимости;
  • системные библиотеки.
  • Практические меры, которые дают реальную пользу:

  • фиксировать версии моделей и хэш-суммы артефактов;
  • хранить модели в своём артефакт-репозитории, а не тянуть “на лету”;
  • сканировать контейнеры на уязвимости (например, Trivy);
  • подписывать образы и проверять подписи при деплое (например, Cosign).
  • Деплой: практические паттерны для локального RAG

    Компоненты, которые обычно деплоятся отдельно

    В продакшене полезно отделять сервисы, чтобы их можно было масштабировать независимо:

  • RAG Orchestrator: API, retrieval, сборка контекста, промптинг, политика безопасности.
  • Embedding service: отдельный сервис, который считает эмбеддинги запросов.
  • Rerank service: если есть, отдельный сервис.
  • Vector DB: Qdrant/Milvus/pgvector.
  • LLM inference server: llama.cpp server, vLLM и аналоги.
  • Document store: хранилище исходников/текстов и метаданных (если нужно открывать “полный документ”).
  • Разделение особенно важно, если вы ожидаете разные профили нагрузки: retrieval может быть лёгким и быстрым, а LLM-инференс тяжёлым.

    Контейнеризация и оркестрация

    Два устойчивых пути:

  • Docker Compose: быстрый старт, понятная схема для одного сервера.
  • Kubernetes: когда нужно масштабирование, отказоустойчивость, политики сети и секретов.
  • Для продакшена важнее не конкретный инструмент, а повторяемость:

  • конфигурации в виде кода;
  • одинаковые окружения dev/stage/prod;
  • контролируемые миграции индексов;
  • быстрый откат.
  • Миграции индекса: как обновляться без хаоса

    Индекс и база знаний меняются по причинам:

  • добавились документы;
  • поменялась стратегия чанкинга;
  • сменились эмбеддинги;
  • поменялись фильтры и метаданные.
  • Практический паттерн для безопасных обновлений:

  • Собирать новый индекс параллельно (новая “версия коллекции”).
  • Прогонять smoke/regression тест-набор на новой версии.
  • Переключать трафик на новую коллекцию атомарно (конфигом/флагом).
  • Держать старую коллекцию ограниченное время для отката.
  • Так вы избегаете состояния, когда часть чанков уже “новая”, а часть “старая”, и retrieval становится непредсказуемым.

    Наблюдаемость в продакшене: метрики, алерты, расследование инцидентов

    Вы уже обсуждали наблюдаемость в статье про оценку. Для продакшена добавьте эксплуатационные “красные флаги”.

    Алерты, которые действительно полезны

  • рост p95/p99 задержек по этапам (особенно t_generate_first_token и t_rerank);
  • рост доли запросов с “пустым retrieval” (например, по порогу сходства или по отсутствию источников);
  • рост доли ответов без цитат (если цитирование обязательно);
  • рост ошибок в ingestion (если индекс обновляется регулярно);
  • рост количества отказов “не знаю” (может означать дрейф запросов или поломку retrieval).
  • Диагностика плохого ответа по следу запроса

    Если у вас есть request_id, вы должны уметь восстановить:

  • Какие фильтры применялись (язык, продукт, ACL).
  • Какие кандидаты принес retrieval и с какими скорингами.
  • Что изменил rerank.
  • Какие источники ушли в финальный контекст.
  • Какие параметры генерации были у LLM.
  • Это превращает “пользователь говорит, что ассистент врёт” в конкретную инженерную задачу.

    Инструменты, которые часто используют для LLM-наблюдаемости:

  • Langfuse — трейсинг, датасеты, промпты.
  • Arize Phoenix — отладка и наблюдаемость LLM-приложений.
  • Практический чеклист перед запуском

    Производительность

  • определён максимальный токен-бюджет на контекст и ответ;
  • включены дедуп и лимит на документ;
  • измерены тайминги по этапам;
  • определены профили нагрузки и лимиты (rate limit, очереди, таймауты).
  • Приватность и безопасность

  • ACL реализован как pre-filter, а не post-filter;
  • источники показываются пользователю только в рамках его прав;
  • логи не содержат сырой контент по умолчанию;
  • есть политика кэша с учётом ACL;
  • ingestion-пайплайн помечает чувствительные и недоверенные источники.
  • Деплой и обновления

  • версии моделей и индексов зафиксированы;
  • есть стратегия blue/green для индекса;
  • есть regression-набор и автоматический прогон перед переключением;
  • есть план отката.
  • Главная идея статьи

    Локальный RAG становится продакшн-системой не тогда, когда “модель отвечает красиво”, а когда вы:

  • контролируете задержки и токены;
  • гарантируете приватность через корректный ACL и безопасные логи;
  • защищаетесь от prompt injection как от класса угроз;
  • деплоите и обновляете индекс и модели воспроизводимо, с тестами и откатом;
  • умеете расследовать плохой ответ по трассе запроса.
  • Это завершает картину курса: от данных и retrieval до генерации, оценки и эксплуатации в реальном локальном контуре.