К основному содержимому

RAG на VDS: AI-ответы по вашим документам

AI-агенты на VDS · 19.04.2026 · 4 мин чтения
Иллюстрация к статье «RAG на VDS: AI-ответы по вашим документам»

Что такое RAG?

RAG (Retrieval-Augmented Generation) — техника, при которой LLM перед генерацией ответа сначала ищет релевантные фрагменты в базе знаний. Это позволяет модели отвечать точно на вопросы о ваших документах, не требуя дообучения.

Как работает RAG: 1) Документ разбивается на чанки → 2) Каждый чанк превращается в вектор → 3) Вектора хранятся в векторной БД → 4) При запросе находятся похожие чанки → 5) 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
Размер чанков: Слишком большие чанки (2000+ токенов) снижают точность поиска. Слишком маленькие (100 токенов) теряют контекст. Оптимально: 300-800 токенов с 10-15% перекрытием.

Частые ошибки 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 на актуальную.

Статья помогла?
← Назад в базу знаний Задать вопрос поддержке