Что такое RAG?
RAG (Retrieval-Augmented Generation) — техника, при которой LLM перед генерацией ответа сначала ищет релевантные фрагменты в базе знаний. Это позволяет модели отвечать точно на вопросы о ваших документах, не требуя дообучения.
Компоненты RAG-системы
- Загрузчики документов — PDF, DOCX, TXT, веб-страницы
- Text Splitter — разбивка на чанки (500-1000 токенов)
- Embedding модель — преобразование текста в векторы
- Векторная БД — Chroma, Qdrant, FAISS
- LLM — генерация ответа на основе найденного контекста
Установка зависимостей
pip install langchain langchain-ollama langchain-community
pip install chromadb pypdf sentence-transformers Полный пример RAG на Python
from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import Chroma
from langchain_community.embeddings import OllamaEmbeddings
from langchain_ollama import OllamaLLM
from langchain.chains import RetrievalQA
# Загрузка и разбивка документа
loader = PyPDFLoader("document.pdf")
docs = loader.load()
splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
chunks = splitter.split_documents(docs)
# Создание векторной базы
embeddings = OllamaEmbeddings(model="nomic-embed-text")
db = Chroma.from_documents(chunks, embeddings, persist_directory="./chroma_db")
# RAG-цепочка
llm = OllamaLLM(model="llama3.2")
qa = RetrievalQA.from_chain_type(llm=llm, retriever=db.as_retriever())
result = qa.invoke("Какова главная тема документа?")
print(result["result"]) Embedding модели для Ollama
# Скачать специализированную модель для эмбеддингов
ollama pull nomic-embed-text
# Или английский вариант
ollama pull mxbai-embed-large Частые ошибки RAG-систем
Большая часть проблем с RAG не в коде, а в качестве извлечения — модель отвечает не из контекста, потому что контекст изначально найден неверно.
Модель отвечает мимо контекста, хотя нужный фрагмент есть в базе
Частая причина — промпт не заставляет модель опираться именно на найденные чанки: она отвечает из общих знаний, если явно не указать "отвечай только на основе приведённого контекста, и если ответа там нет — скажи, что не знаешь". Вторая причина — retriever возвращает слишком мало чанков (k=1-2), и нужный фрагмент просто не попал в выборку.
Dimension mismatch при добавлении документов в Chroma
chromadb.errors.InvalidDimensionException: Embedding dimension 768 does not match collection dimensionality 1024 Эта ошибка возникает, если в одну и ту же коллекцию Chroma добавляли эмбеддинги, сгенерированные разными моделями — у них разная размерность вектора. Решение — использовать одну embedding-модель на весь срок жизни коллекции или создавать отдельную коллекцию при смене модели.
Chroma, Qdrant и FAISS: сравнение
| Критерий | Chroma | Qdrant | FAISS |
|---|---|---|---|
| Запуск | Встроенный, без сервера | Отдельный сервер или Docker | Библиотека, без сервера |
| Персистентность из коробки | Да | Да | Нужно сохранять индекс вручную |
| Фильтрация по метаданным | Базовая | Продвинутая | Отсутствует |
| Подходит для | Прототипов и небольших баз | Продакшена с большим объёмом | Максимальной скорости поиска в памяти |
Когда RAG не подходит
- Документов мало, и они целиком помещаются в контекстное окно модели — проще передать весь текст в промпт, без векторной БД.
- Данные обновляются почти в реальном времени — придётся постоянно переиндексировать чанки, и свежесть ответа всё равно будет отставать.
- Нужны точные вычисления или агрегация по всем документам сразу — RAG находит похожие фрагменты, а не считает суммы и не сравнивает все записи.
- Вопросы пользователей требуют рассуждений по всей базе, а не по отдельным фрагментам — здесь ближе полнотекстовый анализ или SQL-запрос, а не поиск по векторам.
Проверка качества поиска
results = db.similarity_search_with_score("тестовый вопрос из вашей базы", k=3)
for doc, score in results:
print(score, doc.page_content[:100]) Запустите поиск с заведомо известным вопросом, ответ на который точно есть в документах, и проверьте, что нужный чанк оказался среди первых по релевантности. Если он есть в базе, но не попадает в top-k, стоит уменьшить размер чанка или увеличить k, прежде чем менять модель эмбеддингов.
Ещё один нюанс — обновление документов в базе. Если исходный файл изменился, простое повторное добавление чанков в ту же коллекцию Chroma создаст дубликаты, а не заменит старые версии. Перед повторной индексацией стоит либо удалять чанки с прежним идентификатором источника, либо создавать отдельную коллекцию на каждую версию документа и явно переключать retriever на актуальную.