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

Серия «Базовый минимум» (4 части): 

Базовый минимум. Часть 1:  большие языковые модели;

Базовый минимум. Часть 2:  промпт-инжиниринг;

Базовый минимум. Часть 3:  RAG-системы (вы здесь); 

Базовый минимум. Часть 4:  ИИ-агенты. 

«Знание бывает двух видов. Мы либо знаем предмет сами, либо знаем, где можно найти о нем сведения» — Самюэль Джонсон

Содержание

Проблемы обычных языковых моделей

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

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

7.drawio (1).png

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

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

Что такое RAG и зачем это нужно

В документации OpenAI Retrieval-Augmented Generation (RAG) определяется как техника, которая улучшает ответы модели за счёт добавления внешнего контекста в запрос во время генерации.

8.drawio.png

Вместо того чтобы полагаться только на знания, полученные на этапе обучения модели, система извлекает релевантную информацию из подключённых источников данных и использует её для генерации.

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

Что такое чанкование (chanking)

Чанкование (chunking) — это разбиение документов на небольшие фрагменты (чанки, chunks), которые затем становятся базовыми единицами индексации и поиска в RAG-системе. Дело в том, что LLM получает документы не целиком, а лишь в виде ограниченного набора извлеченных фрагментов. Поэтому качество чанкования во многом определяет, какие факты окажутся доступными на этапе генерации ответа.

10.drawio.drawio.png

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

На практике используют разбиение по фиксированной длине или по смысловым границам (заголовки, абзацы), иногда добавляя перекрытие (overlap), чтобы не терять смысл на стыках. Оптимальные параметры подбираются эмпирически под конкретный корпус и тип запросов.

Как работает RAG


Источник: Gao Y., Xiong Y., Gao X., Jia K., Pan J., Bi Y., Dai Y., Sun J., Wang M., Wang H. Retrieval-Augmented Generation for Large Language Models: A Survey // arXiv. 2023. URL: https://arxiv.org/abs/2312.10997 (дата обращения: 05.11.2025).
Источник: Gao Y., Xiong Y., Gao X., Jia K., Pan J., Bi Y., Dai Y., Sun J., Wang M., Wang H. Retrieval-Augmented Generation for Large Language Models: A Survey // arXiv. 2023. URL: https://arxiv.org/abs/2312.10997 (дата обращения: 05.11.2025).

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

В подходе RAG (синие стрелки) документы заранее индексируются: разбиваются на чанки, каждый из которых переводится в векторное представление и сохраняется во векторной базе. 

Когда поступает запрос, система сначала выполняет retrieval — преобразует запрос в представление, удобное для поиска, и извлекает наиболее релевантные фрагменты. 

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

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

Варианты реализации

Источник: Gao Y., Xiong Y., Gao X., Jia K., Pan J., Bi Y., Dai Y., Sun J., Wang M., Wang H. Retrieval-Augmented Generation for Large Language Models: A Survey // arXiv. 2023. URL: https://arxiv.org/abs/2312.10997 (дата обращения: 05.11.2025).
Источник: Gao Y., Xiong Y., Gao X., Jia K., Pan J., Bi Y., Dai Y., Sun J., Wang M., Wang H. Retrieval-Augmented Generation for Large Language Models: A Survey // arXiv. 2023. URL: https://arxiv.org/abs/2312.10997 (дата обращения: 05.11.2025).

На схеме показаны основные варианты реализации RAG: от базовых к более продвинутым. Слева — Naive RAG: есть индекс документов, строятся эмбеддинги, по запросу извлекаются ближайшие фрагменты и без дополнительной обработки передаются модели. 

В центре — Advanced RAG: добавляется последовательная обработка вокруг поиска. Запрос можно предварительно переформулировать (rewrite), переранжировать (rerank) результаты, очистить от дубликатов и «шума» и т. д. Это заметно повышает качество и во многих практических системах становится вариантом по умолчанию.

Справа — Modular RAG: вместо линейного конвейера используется набор независимых модулей: маршрутизация запросов, память, различные методы поиска, методы объединения результатов (fusion). 

Сравнение RAG и дообучения

Screenshot 2025-11-04 at 22.18.12.png
Источник: Gao Y., Xiong Y., Gao X., Jia K., Pan J., Bi Y., Dai Y., Sun J., Wang M., Wang H. Retrieval-Augmented Generation for Large Language Models: A Survey // arXiv. 2023. URL: https://arxiv.org/abs/2312.10997 (дата обращения: 05.11.2025).

Сравнение способов адаптации языковых моделей [1]

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

Промпт-инжиниринг предполагает оптимизацию запроса с целью получить более точный и устойчивый ответ.

Дообучение, напротив, изменяет саму модель — генератор, ретривер (реранкер) или оба компонента. Такой подход обеспечивает глубокую кастомизацию, но требует данных, обучения и регулярных обновлений.

Практический пример: минимальный RAG-конвейер

Чтобы увидеть описанный выше механизм в действии, реализуем версию Naive RAG на Python для импровизированной задачи. В качестве базы знаний будем использовать фрагмент русской Википедии из 500 статей, а также небольшую языковую модель Qwen2.5-3B-Instruct. Целиком пример можно воспроизвести в Google Colab по ссылке, а ниже разберем ключевые моменты.

Шаг 1. Загрузка корпуса и чанкование

import torch
from datasets import load_dataset
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_core.documents import Document

device = "cuda" if torch.cuda.is_available() else "cpu"

dataset = load_dataset(
    "wikimedia/wikipedia",
    "20231101.ru",
    split="train[:500]"
)

documents = [
    Document(
        page_content=row["text"],
        metadata={"title": row["title"]}
    )
    for row in dataset
]

splitter = RecursiveCharacterTextSplitter(
    chunk_size=800,
    chunk_overlap=100
)

chunks = splitter.split_documents(documents)

print("Чанков:", len(chunks))

Результат выполнения:

Чанков: 17892

Каждая статья сохраняется как объект Document, текст помещается в page_content, а название в metadata. Далее при помощи готового метода документы разбиваются на чанки длиной 800 символов с перекрытием 100 символов. Напомню, что пока что мы имеет дело с символами, а не с токенами.

Шаг 2. Построение векторного индекса

from langchain_community.vectorstores import Chroma
from langchain_community.embeddings import HuggingFaceEmbeddings

embedding_model = HuggingFaceEmbeddings(
    model_name="sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2",
    model_kwargs={"device": device}
)

vectorstore = Chroma.from_documents(
    documents=chunks,
    embedding=embedding_model
)

В качестве модели эмбеддингов для примера используется мультиязычная sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2. Для хранения векторов применяется Chroma. Эта векторная база данных с высоким уровнем абстрации, которая позволяет одной строчкой создать индекс.

Шаг 3. Retrieval

import textwrap

def search(query, k=3, width=80):
    results = vectorstore.similarity_search(query, k=k)

    for i, doc in enumerate(results, 1):
        print(f"\n--- Результат {i} ---")
        print("Заголовок:", doc.metadata["title"])
        print(textwrap.fill(doc.page_content[:1000], width))

search("Кто такой Пушкин?")

И результат:

--- Результат 1 ---
Заголовок: Пушкин, Александр Сергеевич
Изучение Пушкина

--- Результат 2 ---
Заголовок: Пушкин, Александр Сергеевич
Изучение Пушкина

--- Результат 3 ---
Заголовок: Пушкин, Александр Сергеевич
Биография  Происхождение   Происхождение Александра Сергеевича Пушкина идёт от
разветвлённого нетитулованного дворянского рода Пушкиных, восходившего по
генеалогической легенде к «мужу честну» Ратше. Пушкин неоднократно писал о своей
родословной в стихах и прозе; он видел в своих предках образец истинной
«аристократии», древнего рода, честно служившего отечеству, но не снискавшего
благосклонности правителей и «гонимого». Не раз он обращался (в том числе в
художественной форме) и к образу своего прадеда по матери — африканца Абрама
Петровича Ганнибала, ставшего слугой и воспитанником Петра I, а потом военным
инженером и генералом.

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

Этот код не является обязательным для конечного результата, поскольку в полном RAG-конвейере retrieval вызывается внутри функции генерации (будет ниже). Однако отдельная демонстрация поиска наглядно показывает, какие фрагменты попадают в контекст модели.

В выводе можно увидеть дубликаты (первые два результата). Скорее всего, это связано с разбиением текста на чанки с перекрытием. В реальных системах такие особенности необходимо отслеживать и устранять, однако в демонстрационном примере это можно не исправлять.

Шаг 4. Generation

from transformers import AutoTokenizer, AutoModelForCausalLM
import torch
import textwrap


model_name = "Qwen/Qwen2.5-3B-Instruct"

tokenizer = AutoTokenizer.from_pretrained(model_name)

llm = AutoModelForCausalLM.from_pretrained(
    model_name,
    torch_dtype=torch.float16,
    device_map="auto"
)

def generate_answer(query, k=3, width=50):
    # 1. Retrieval
    results = vectorstore.similarity_search(query, k=k)
    context = "\n\n".join([doc.page_content for doc in results])

    # 2. Prompt
    prompt = f"""Ответь на вопрос, используя контекст.
    
            Контекст: {context}

            Вопрос: {query}
            
            Ответ:
            """

    # 3. Generation
    inputs = tokenizer(prompt, return_tensors="pt").to(llm.device)

    outputs = llm.generate(
        **inputs,
        max_new_tokens=200,
        temperature=0.7,
        do_sample=True
    )

    text = tokenizer.decode(outputs[0], skip_special_tokens=True)
    answer = text[len(prompt):].strip()

    print(f"\nВопрос:\n{textwrap.fill(query, width)}\n")
    print("Ответ:\n")
    print(textwrap.fill(answer, width))

# Пример
generate_answer("Кто такой Пушкин?")

И результат:

Вопрос:
Кто такой Пушкин?

Ответ:

Александр Сергеевич Пушкин был русским поэтом,
драматургом и прозаиком, значительное место в
мировой литературе занимающий. Он родился в
дворянской семье и воспитывался в духе
аристократического восприятия жизни. Его биография
связана с неоднократными упоминаниями о своей
родословной, где он рассматривал предков как
образцы истинной «аристократии», честно служившей
отечеству. Кроме того, Пушкин часто обращался к
образу своего прадеда по матери — африканца Абрама
Петровича Ганнибала, который был слугой и
воспитанником Петра I и затем военным инженером и
генералом. Его творчество включ

Видно, что генерация опирается на найденный контекст (вывод с шага 3). Упоминаются происхождение Пушкина и образ его прадеда Абрама Ганнибала. При этом модель не воспроизводит текст дословно, а перерабатывает его в связный и адаптированный ответ.

Заключение

Итого, RAG решает проблему устаревших знаний и галлюцинаций, объединяя LLM с внешними источниками данных. Чем сложнее стратегия, тем больше автономности и точности система получает без изменения самой модели. На каком-то этапе система уже умеет самостоятельно уточнять запросы, повторно обращаться к хранилищам и проверять результаты.

Это логически подводит к следующему шагу — ИИ-агентам, которые не только генерируют ответы, но и целенаправленно действуют: планируют шаги, вызывают инструменты и работают с внешними данными самостоятельно.

Далее — Базовый минимум: ИИ-агенты (в разработке).

Список используемых источников 

1. Gao Y., Xiong Y., Gao X., Jia K., Pan J., Bi Y., Dai Y., Sun J., Wang M., Wang H. Retrieval-Augmented Generation for Large Language Models: A Survey // arXiv. 2023. URL: https://arxiv.org/abs/2312.10997 (дата обращения: 05.11.2025).

2. Lewis P., Perez E., Piktus A., Petroni F., Karpukhin V., Goyal N., Küttler H., Lewis M., Yih W.-t., Rocktäschel T., Riedel S., Kiela D. Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks // Advances in Neural Information Processing Systems. — 2020. — Vol. 33.

3. Martineau K. What is retrieval-augmented generation (RAG)? [Электронный ресурс] // IBM Research : Blog. — 22 Aug 2023. — URL: https://research.ibm.com/blog/retrieval-augmented-generation-RAG (дата обращения: 03.01.2026).

4. RAG: учим искусственный интеллект работать с новыми данными [Электронный ресурс] // Yandex Cloud : блог. — 20 мая 2025 г. — URL: https://yandex.cloud/ru/blog/posts/2025/05/retrieval-augmented-generation-basics (дата обращения: 03.01.2026). 

5. Что такое RAG: как работает генерация с дополненной выборкой в нейросетях [Электронный ресурс] // Developers.Sber.ru : база знаний. — 27 октября 2025. — URL: https://developers.sber.ru/help/business-development/what-is-rag (дата обращения: 03.01.2026).