AI-инжиниринг
RAG: Retrieval-Augmented Generation от теории до реализации
Цели урока
- Понять, зачем LLM нужна внешняя память и какие проблемы решает RAG
- Освоить полный RAG pipeline: indexing, retrieval, augmentation, generation
- Реализовать production-ready RAG с pgvector и NestJS
- Написать prompt template, минимизирующий галлюцинации
- Настроить метрики качества: faithfulness, relevance, correctness
Предварительные знания
- Работа с vector databases и embeddings
Fine-tuning GPT-4 стоит `100K USD`+ и занимает недели. RAG стоит `50 USD/месяц` на хостинг Qdrant и запускается за день. И RAG чаще даёт лучший результат - потому что не делает модель умнее. Он просто кладёт ответ прямо в контекст и просит пересказать. Это технически обман. Но он работает - и именно на этом принципе построены Perplexity, Notion AI и 80% корпоративных AI-проектов.
- Perplexity AI (2022) - первый массовый RAG-продукт, поиск по интернету в реальном времени, 500M+ valuation
- Notion AI - RAG по документам пользователя, +100M ARR за первый год без единой строки fine-tuning
- GitHub Copilot - RAG по кодовой базе репозитория для контекстных подсказок
- 80% AI-проектов в enterprise используют RAG - это стандартная архитектура корпоративных чатботов
Как RAG появился
**2020**: Patrick Lewis и команда Facebook AI Research публикуют "Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks" - оригинальный RAG paper. Идея: соединить retriever (DPR) и generator (BART) в единую дифференцируемую архитектуру. Модель обучалась искать релевантные документы и генерировать ответы на их основе. **2022**: Perplexity запускается как первый массовый продукт на RAG-принципе - поисковик, который не показывает ссылки, а синтезирует ответ из найденных источников. Пользователи видят не 10 синих ссылок, а связный текст с цитатами. **2023**: RAG становится стандартом - каждый корпоративный AI-чатбот строится по этому паттерну. LangChain, LlamaIndex, pgvector - вся экосистема выросла вокруг одной идеи из 2020 года.
Зачем LLM нужна внешняя память
GPT-4 обучался на данных до апреля 2024. Спросить про событие мая 2024 - и модель уверенно выдаёт галлюцинацию. Спросить про внутреннюю документацию компании - и модель придумывает правдоподобный, но полностью ложный ответ. Это не баг. Это архитектурное ограничение, вшитое в принцип работы авторегрессивных моделей.
У LLM три системные проблемы, которые нельзя решить дообучением:
| Проблема | Суть | Пример |
|---|---|---|
| Knowledge cutoff | Модель не знает ничего после даты обучения | "Какой курс доллара сегодня?" - ответ устарел на месяцы |
| Hallucinations | Модель генерирует правдоподобный, но ложный текст | "Процитируй статью из Nature" - модель выдумывает DOI и авторов |
| No private data | Модель не имеет доступа к корпоративным данным | "Какая политика отпусков в нашей компании?" - модель фантазирует |
**RAG (Retrieval-Augmented Generation)** решает все три одним архитектурным паттерном: перед генерацией ответа - найти релевантные документы и подставить их в prompt. Модель не "вспоминает" - она **читает** предоставленный контекст. Это принципиально разные вещи.
Альтернативы - fine-tuning и длинный контекст - проигрывают по всем фронтам:
| Подход | Стоимость | Актуальность | Контроль |
|---|---|---|---|
| Fine-tuning GPT-4 | 100K+ долларов за обучение | Устаревает при обновлении данных | Нельзя быстро обновить |
| Длинный контекст (1M tokens) | 5-20 долларов за запрос | Актуально, но дорого | Медленно, теряет точность |
| RAG | 0.001-0.01 доллара за запрос | Обновляется мгновенно | Точный контроль источников |
RAG - это open-book exam. Вместо того чтобы заставлять студента зубрить всё наизусть, ему дают справочник. LLM хорошо умеет понимать и синтезировать информацию - нужно лишь предоставить правильные источники.
Какую проблему RAG НЕ решает?
RAG Pipeline: от запроса до ответа
RAG pipeline состоит из двух фаз: **indexing** (однократная подготовка документов) и **retrieval + generation** (при каждом запросе). Ключевой инсайт - indexing запускается редко, а query - тысячи раз в день. Стоимость системы определяет именно query-фаза.
Фаза 1: Indexing (офлайн)
- **Load** - загрузить документы (PDF, Markdown, HTML, база данных)
- **Split** - разбить на chunks по 512 токенов с overlap 20% (102 токена) - золотой стандарт для text-embedding-3-small
- **Embed** - превратить каждый chunk в вектор 1536 измерений через `text-embedding-3-small` (`0.02` за 1M токенов)
- **Store** - сохранить vectors + metadata в vector database (pgvector, Qdrant, Pinecone)
Фаза 2: Query (при каждом запросе)
- **Embed query** - превратить вопрос пользователя в embedding той же моделью
- **Search** - найти top-K ближайших chunks через cosine similarity (порог 0.7)
- **Augment** - вставить найденные chunks в prompt как контекст
- **Generate** - LLM генерирует ответ строго на основе контекста
- **Return** - вернуть ответ + ссылки на источники
Оператор `<=>` в pgvector - это cosine distance. `1 - (embedding <=> `1`)` даёт cosine similarity (от 0 до 1). Чем ближе к 1 - тем релевантнее документ. Порог 0.7 подобран эмпирически для text-embedding-3-small - при значениях ниже начинается шум.
В каком порядке выполняются шаги RAG при обработке запроса пользователя?
Реализация RAG с pgvector и Node.js
pgvector - расширение PostgreSQL для работы с vector embeddings. Для backend-разработчика, уже использующего PostgreSQL, это самый быстрый путь к production RAG: ноль новой инфраструктуры, привычные транзакции, JOIN с остальными таблицами. Perplexity начинала именно на pgvector - до того как масштаб потребовал перехода на Qdrant.
Полноценный RAG-сервис в NestJS - от indexing до ответа с источниками:
Порог similarity (`> 0.7`) - критически важен. Без него RAG вернёт нерелевантные chunks, и LLM будет галлюцинировать на основе плохого контекста. Значение подбирается эмпирически: 0.7-0.8 для text-embedding-3-small. Для мультиязычных задач - 0.65.
Зачем в поисковом запросе pgvector используется порог similarity > 0.7?
Prompt template для RAG
Качество RAG на 50% зависит от retrieval и на 50% от prompt. Плохой prompt может испортить даже идеально найденные документы. Распространённая иллюзия - "подать хороший контекст и всё само заработает". Нет. Без жёстких инструкций модель начнёт подмешивать собственные знания - тихо и уверенно.
Шаблон пользовательского сообщения тоже важен. Контекст и вопрос должны быть чётко разделены:
Распространённая ошибка - добавлять chunks как system message. Контекст лучше передавать в user message, а system оставить для инструкций. Это снижает prompt injection риски и упрощает кеширование system prompt - Anthropic даёт скидку 90% на кешированные system prompt.
Для повышения quality полезно передавать metadata каждого chunk - источник и дата помогают модели приоритизировать более свежие и авторитетные документы:
Какое правило НАИБОЛЕЕ важно в RAG system prompt?
Метрики качества RAG
RAG работает? Отвечает на вопросы? Этого недостаточно. Без метрик нельзя понять, улучшают ли изменения качество или ухудшают. Перемещение порога с 0.7 до 0.75 - помогает или нет? Только цифры покажут. Три ключевые метрики RAG: **faithfulness**, **relevance**, **answer correctness**.
| Метрика | Что измеряет | Как считать |
|---|---|---|
| Faithfulness | Ответ основан на контексте, а не выдуман? | LLM-as-judge: "Все ли утверждения в ответе подтверждаются контекстом?" |
| Context Relevance | Найденные документы релевантны вопросу? | Доля retrieved chunks, которые реально нужны для ответа |
| Answer Relevance | Ответ отвечает на заданный вопрос? | LLM-as-judge: "Насколько ответ соответствует вопросу?" |
| Answer Correctness | Ответ фактически правильный? | Сравнение с ground truth (если есть golden dataset) |
Фреймворки для evaluation: **RAGAS** (Python, стандарт индустрии), **LangSmith** (tracing + eval), **DeepEval** (Python). Для Node.js чаще проще реализовать LLM-as-judge самостоятельно, как показано выше - это 30 строк кода против новой зависимости.
Метрика faithfulness в RAG оценивает...
Типичные ошибки и подводные камни RAG
Naive RAG implementation часто даёт 50-60% accuracy. Прототип за выходные - и production система с 90% faithfulness - это разные продукты. Разрыв почти целиком в деталях, которые не видны на первом запуске.
| Проблема | Симптом | Решение |
|---|---|---|
| Chunks слишком большие | Ответ расплывчатый, много шума | Уменьшить chunk size до 256-512 tokens, добавить overlap 20% |
| Chunks слишком маленькие | Контекст вырван, ответ неполный | Увеличить chunk size, использовать parent document retriever |
| Нет порога similarity | LLM галлюцинирует на нерелевантном контексте | Добавить WHERE similarity > 0.7, возвращать 'не найдено' |
| Плохой embedding model | Семантически похожие документы не находятся | Перейти на text-embedding-3-large или multilingual модель |
| Нет metadata filtering | Поиск по всей базе вместо нужной категории | Добавить pre-filtering по metadata (дата, категория, автор) |
| System prompt не ограничивает | Модель подмешивает собственные знания | Добавить строгие инструкции: ТОЛЬКО контекст, иначе 'не найдено' |
Самая опасная ошибка - отсутствие fallback при пустых результатах. Если поиск не нашёл ничего релевантного, RAG должен честно сказать "информация не найдена", а не передавать пустой контекст в LLM. Модель начнёт отвечать из собственных знаний - и это будет выглядеть как нормальный ответ. Пользователь поверит. Доверие сломается позже - когда окажется, что ответ был выдуман.
Чек-лист production RAG:
- Similarity threshold настроен и протестирован на реальных запросах
- Metadata pre-filtering сужает scope поиска
- Fallback при пустых результатах - "не найдено" вместо галлюцинаций
- Логирование: query, retrieved chunks, similarity scores, response time
- Evaluation pipeline: faithfulness + relevance на golden dataset
- Мониторинг: % запросов с нулевыми результатами, средний similarity score
RAG возвращает расплывчатые ответы с большим количеством нерелевантной информации. Наиболее вероятная причина?
RAG решает проблему галлюцинаций
RAG снижает галлюцинации только тогда, когда релевантный chunk найден. Если поиск возвращает нерелевантные документы или пустой результат - модель галлюцинирует с той же уверенностью
RAG - это не магический антигаллюцинаторный слой. Это система доставки контекста. Если контекст плохой (неправильный chunking, низкий similarity threshold, плохая embedding модель) - галлюцинации остаются. Хуже того: модель галлюцинирует на основе плохого контекста и делает это уверенно, ссылаясь на "источники". Именно поэтому нужен similarity threshold 0.7 и честный fallback "информация не найдена".
RAG и fine-tuning решают одну задачу
Это разные инструменты для разных задач. Fine-tuning меняет поведение модели (стиль, формат, специализация). RAG даёт модели доступ к актуальным данным
Fine-tuning на корпоративных документах не означает, что модель будет "знать" эти документы - она выучит паттерны, но не факты. Через месяц документы устареют, а fine-tuning придётся повторять. RAG не меняет модель - он добавляет данные при каждом запросе. Обновил документы - RAG автоматически использует новую версию.
Итоги
- RAG решает три проблемы LLM: knowledge cutoff, галлюцинации, отсутствие приватных данных - за 0.001-0.01 доллара за запрос против 100K+ на fine-tuning
- Pipeline: Load -> Split (512 tokens, 20% overlap) -> Embed (text-embedding-3-small) -> Store (indexing) и Embed query -> Search (cosine > 0.7) -> Augment -> Generate (query)
- pgvector + HNSW - самый быстрый путь к production RAG без новой инфраструктуры
- Prompt template должен строго ограничивать модель контекстом - иначе модель тихо подмешивает собственные знания
- Faithfulness (верность контексту), Context Relevance, Answer Correctness - три метрики, без которых нельзя улучшать систему
- Главные ловушки: нет similarity threshold, нет fallback при пустых результатах, chunks слишком большие - каждая из них открывает дверь для галлюцинаций
Вопросы для размышления
- В каком из текущих или прошлых проектов RAG мог бы заменить или дополнить поиск по базе данных?
- Почему RAG - это "обман" с технической точки зрения, и почему этот обман работает лучше, чем fine-tuning на тех же данных?
- Если similarity score у всех найденных chunks ниже 0.7 - что должна делать система? Вернуть лучшие результаты или честно сказать "не знаю"?
Что дальше
Naive RAG покрывает 60-70% случаев. Для оставшихся 30% нужны advanced техники: hybrid search, re-ranking, query transformation.
- Advanced RAG — Hybrid search, re-ranking, query expansion - от 60% accuracy к 90%
- Chunking стратегии — Как правильно разбивать документы - fixed, recursive, semantic chunking
- Conversation Memory — Как добавить RAG в чатбот с сохранением контекста разговора
Связанные уроки
- aie-10-vector-databases — Retrieval в RAG работает поверх векторного хранилища
- aie-09-embeddings — Качество поиска зависит от качества эмбеддингов
- aie-13-advanced-rag — Наивный RAG это базис для продвинутых техник
- aie-14-chunking-strategies — Чанкинг задаёт потолок точности retrieval
- ml-52-search-ranking — Ранжирование retrieval использует скоринг из информационного поиска
- db-28-search — Retrieval в RAG это семантический поиск по корпусу
- net-21-http-basics
- db-30-vector