- BrainTools - https://www.braintools.ru -
Материал подготовлен в рамках курса «ИИ для Python‑разработчиков» [1].
Меня зовут Андрей Бирюков. Я — независимый эксперт в области ИТ и ИБ, преподаю в учебных центрах и пишу статьи и книги.
Вы когда‑нибудь пытались спросить у ChatGPT о том, как работает ваш собственный проект? Модель вежливо отвечает, но её ответы — это общие шаблоны, основанные на обучении [2] на миллионах чужих репозиториев.
Она не знает, что в вашей системе используется кастомная авторизация через Redis, а соединение с базой данных настраивается через переменные окружения с префиксом MY_APP_.
И здесь проблема совсем не в модели. Проблема в том, что знания модели закончились в момент её обучения, а ваша документация вообще живёт отдельно.
Retrieval‑Augmented Generation (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] будут близки по косинусному расстоянию
В результате, семантический поиск ищет смысл, а не слова. Когда пользователь спрашивает «как настроить подключение к БД», система найдёт документы про «конфигурацию пула соединений», даже если точных совпадений нет.
Теперь давайте перейдем к практике и соберём рабочий пример. Мы будем использовать 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 не делает LLM умнее, он просто даёт ей доступ к вашей информации. Модель — это интерпретатор, а RAG — это система доступа к данным, которая решает, что подать на вход интерпретатору.
Для того, чтобы RAG работал хорошо, нужно правильно разбивать документы на чанки с перекрытием, выбирать модель эмбеддингов под задачу, настраивать нужное количество чанков и проектировать промпт так, чтобы модель использовала только контекст.
RAG превращает LLM из генератора «общих ответов» в эксперта по вашей предметной области. И для этого не нужно дообучать модель — достаточно правильно организовать доступ к документам.
ЧТО ПОЧИТАТЬ ПО ТЕМЕ:
RAG для тех, кто разочаровался: почему retrieval ломается и как это починить [4].
Contextual Retrieval: техника, которая чинит главную проблему RAG за 50 центов на тысячу чанков [5].
Промпт, RAG или дообучение: что реально выучит вашу LLM [6].

Работа с LLM в разработке быстро выходит за рамки генерации кода. Возникают практические вопросы: можно ли доверять ответам модели, как найти причину ошибки [7] и проверить предложенное исправление → научиться использовать искусственный интеллект [8] как помощника в разработке без потери контроля над качеством результата.
На бесплатных демо-уроках разберём реальные сценарии применения искусственного интеллекта:
8 сентября в 20:00. «ИИ против бага: как разобрать инцидент в Python‑проекте от логов до исправления». Записаться [9]
22 сентября в 20:00. «Можно ли доверять ИИ‑коду: как находить галлюцинации, уязвимости и устаревшие решения». Записаться [10]
Больше бесплатных уроков сентября смотрите в дайджесте. [11]
Автор: Andrey_Biryukov
Источник [12]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/34977
URLs in this post:
[1] курса «ИИ для Python‑разработчиков»: https://otus.pw/pjXt/
[2] обучении: http://www.braintools.ru/article/5125
[3] Возвращение RAG в 2026 году: https://habr.com/ru/companies/otus/articles/1001970/
[4] RAG для тех, кто разочаровался: почему retrieval ломается и как это починить: https://habr.com/ru/companies/otus/articles/1034386/
[5] Contextual Retrieval: техника, которая чинит главную проблему RAG за 50 центов на тысячу чанков: https://habr.com/ru/companies/otus/articles/1054594/
[6] Промпт, RAG или дообучение: что реально выучит вашу LLM: https://habr.com/ru/companies/otus/articles/1064244/
[7] ошибки: http://www.braintools.ru/article/4192
[8] интеллект: http://www.braintools.ru/article/7605
[9] Записаться: https://otus.pw/rhtn/
[10] Записаться: https://otus.pw/0pvv/
[11] в дайджесте.: https://habr.com/ru/companies/otus/articles/1076824/
[12] Источник: https://habr.com/ru/companies/otus/articles/1075188/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1075188
Нажмите здесь для печати.