Материал подготовлен в рамках курса «ИИ для 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 = "nn".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 = "nn".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. «Можно ли доверять ИИ‑коду: как находить галлюцинации, уязвимости и устаревшие решения». Записаться
Больше бесплатных уроков сентября смотрите в дайджесте.
Автор: Andrey_Biryukov


