Материал подготовлен в рамках курса «ИИ для Python‑разработчиков».
Привет, Хабр! Меня зовут Андрей Бирюков. Я — независимый эксперт в области ИТ и ИБ, преподаю в учебных центрах и пишу статьи и книги.
Вы когда‑нибудь пытались спросить у ChatGPT о том, как работает ваш собственный проект? Модель вежливо отвечает, но её ответы — это общие шаблоны, основанные на обучении на миллионах чужих репозиториев.
Она не знает, что в вашей системе используется кастомная авторизация через Redis, а соединение с базой данных настраивается через переменные окружения с префиксом MY_APP_.
И здесь проблема совсем не в модели. Проблема в том, что знания модели закончились в момент её обучения, а ваша документация вообще живёт отдельно.
Retrieval‑Augmented Generation (RAG) решает эту проблему. Вместо того чтобы заставлять модель «помнить» всё, мы даём ей доступ к нужным документам в момент ответа. Модель конечно не становится умнее, но она получает шпаргалку.
Что такое RAG и почему это работает
RAG — это архитектурный паттерн, который добавляет к LLM этап поиска. Вместо того чтобы отвечать на основе заученных данных, модель сначала находит релевантные документы в вашей базе знаний, а затем использует их как контекст для генерации ответа.

Это как экзамен, на который вы приносите учебник. Вы не обязаны помнить каждую формулу — вы знаете, где её искать.
В типичном RAG‑пайплайне четыре шага:
Вы получаете вопрос от пользователя.
Превращаете вопрос в вектор — числовое представление смысла.
Ищете в векторной базе документы, чьи векторы ближе всего к вектору вопроса.
Подставляете найденные документы в промпт и отправляете в LLM.
Модель отвечает на основе ваших документов, а не на основе общих знаний. Ответ становится точным, актуальным и относящимся к вашему контексту.
Эмбеддинги — как компьютер понимает смысл
Ключевая технология, которая положена в основу RAG, — это эмбеддинги, векторы чисел, которые представляют смысл текста. Например, два предложения со схожим смыслом будут иметь похожие векторы, даже если в них нет общих слов.
Возьмём классический пример:
“A beginner's guide to engine troubleshooting”
“Motor diagnostics for intermittent power loss”
Эти предложения говорят об одном и том же, но не имеют общих слов. Ключевой поиск не свяжет их — он ищет точное совпадение.
Эмбеддинги свяжут, потому что оба текста описывают диагностику двигателей, и их векторы окажутся близкими в многомерном пространстве.
На практике вы загружаете модель эмбеддингов, например all-MiniLM-L6-v2 от Hugging Face:
from sentence_transformers import SentenceTransformer model = SentenceTransformer("all-MiniLM-L6-v2") documents = [ "A beginner's guide to engine troubleshooting", "Motor diagnostics for intermittent power loss" ] # Получаем векторы embeddings = model.encode(documents) # embeddings[0] и embeddings[1] будут близки по косинусному расстоянию
В результате, семантический поиск ищет смысл, а не слова. Когда пользователь спрашивает «как настроить подключение к БД», система найдёт документы про «конфигурацию пула соединений», даже если точных совпадений нет.
Полный RAG‑пайплайн на Python
Теперь давайте перейдем к практике и соберём рабочий пример. Мы будем использовать Sentence Transformers для генерации эмбеддингов и FAISS или ChromaDB для векторного поиска и OpenAI/Groq API для генерации ответа.
Шаг 1. Подготовка документов
Документы редко помещаются в контекст целиком — они слишком большие. Их нужно разбить на чанки — смысловые куски по 500–1000 слов с небольшим перекрытием, чтобы не терять контекст на границах.
def chunk_text(text, chunk_size=500, overlap=50): words = text.split() chunks = [] for i in range(0, len(words), chunk_size - overlap): chunk = " ".join(words[i:i + chunk_size]) chunks.append(chunk) return chunks
Шаг 2. Создание векторного индекса
Далее для каждого чанка генерируем эмбеддинг и сохраняем его в базу. Для этого мы воспользуемся ChromaDB — простой векторной базой для локальной разработки:
import chromadb from sentence_transformers import SentenceTransformer client = chromadb.Client() collection = client.create_collection("docs") model = SentenceTransformer("all-MiniLM-L6-v2") for i, chunk in enumerate(chunks): embedding = model.encode(chunk).tolist() collection.add( ids=[str(i)], embeddings=[embedding], documents=[chunk] )
Шаг 3. Поиск по вопросу
Когда приходит запрос, мы генерируем эмбеддинг вопроса и ищем в базе самые близкие чанки:
def retrieve(query, top_k=3): query_embedding = model.encode(query).tolist() results = collection.query( query_embeddings=[query_embedding], n_results=top_k ) return results["documents"][0]
Шаг 4. Генерация ответа с контекстом
После этого, найденные документы подставляются в промпт, и LLM генерирует ответ на их основе:
from openai import OpenAI client = OpenAI() def ask(query): docs = retrieve(query) context = "\n\n".join(docs) prompt = f""" Ответь на вопрос, используя только информацию из предоставленных документов. Если в документах нет ответа, скажи об этом прямо. Документы: {context} Вопрос: {query} """ response = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": prompt}] ) return response.choices[0].message.content
Чат‑бот по документации проекта
Теперь представьте, что у вас есть репозиторий с 50 файлами документации. Вы хотите, чтобы разработчики могли задавать вопросы и получать ответы, основанные именно на вашей документации, а не на общих знаниях модели.
Давайте соберём это в класс, как в проектах SimpleRAG или RAG‑STARTER:
class ProjectRAG: def __init__(self, api_key): self.embedder = SentenceTransformer("all-MiniLM-L6-v2") self.client = chromadb.Client() self.collection = self.client.create_collection("project_docs") self.llm = OpenAI(api_key=api_key) def add_document(self, file_path): with open(file_path, 'r') as f: text = f.read() chunks = self._chunk_text(text) embeddings = self.embedder.encode(chunks) self.collection.add( ids=[f"{file_path}_{i}" for i in range(len(chunks))], embeddings=embeddings.tolist(), documents=chunks, metadatas=[{"source": file_path} for _ in chunks] ) def ask(self, query): q_emb = self.embedder.encode([query]).tolist() results = self.collection.query( query_embeddings=q_emb, n_results=3 ) context = "\n\n".join(results["documents"][0]) sources = set(r["source"] for r in results["metadatas"][0]) prompt = f""" Ты — ассистент по проекту. Отвечай, используя только документы ниже. Если ответа нет в документах, скажи "В документации нет информации". В конце ответа укажи источники. Документы: {context} Вопрос: {query} """ response = self.llm.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": prompt}] ) return { "answer": response.choices[0].message.content, "sources": sources }
В результате, при обращении к модели будет использоваться промпт, аналогичный приведенному на рисунке:

А ответ модели будет иметь следующий вид:

Что дальше: гибридный поиск и улучшения
Простой семантический поиск хорошо работает, но иногда точное совпадение ключевых слов даёт лучший результат.
Поэтому в продуктиве используют гибридный поиск — комбинацию ключевого (BM25) и семантического поиска с последующим переранжированием.
На практике это выглядит так:
Вы находите документы двумя способами — по ключевым словам и по смыслу.
Объединяете результаты.
Переранжируете их с помощью более мощной модели (например, кросс‑энкодер).
Это даёт наилучшую точность: вы не пропускаете ни точных совпадений, ни смысловых аналогов.
Заключение. RAG — это не про магию, это про архитектуру
RAG не делает LLM умнее, он просто даёт ей доступ к вашей информации. Модель — это интерпретатор, а RAG — это система доступа к данным, которая решает, что подать на вход интерпретатору.
Для того, чтобы RAG работал хорошо, нужно правильно разбивать документы на чанки с перекрытием, выбирать модель эмбеддингов под задачу, настраивать нужное количество чанков и проектировать промпт так, чтобы модель использовала только контекст.
RAG превращает LLM из генератора «общих ответов» в эксперта по вашей предметной области. И для этого не нужно дообучать модель — достаточно правильно организовать доступ к документам.
ЧТО ПОЧИТАТЬ ПО ТЕМЕ:
RAG для тех, кто разочаровался: почему retrieval ломается и как это починить.
Contextual Retrieval: техника, которая чинит главную проблему RAG за 50 центов на тысячу чанков.

Работа с LLM в разработке быстро выходит за рамки генерации кода. Возникают практические вопросы: можно ли доверять ответам модели, как найти причину ошибки и проверить предложенное исправление → научиться использовать искусственный интеллект как помощника в разработке без потери контроля над качеством результата.
На бесплатных демо-уроках разберём реальные сценарии применения искусственного интеллекта:
8 сентября в 20:00. «ИИ против бага: как разобрать инцидент в Python‑проекте от логов до исправления». Записаться
22 сентября в 20:00. «Можно ли доверять ИИ‑коду: как находить галлюцинации, уязвимости и устаревшие решения». Записаться
Больше бесплатных уроков сентября смотрите в дайджесте.

