Как построить RAG-систему для поиска по документам: пошаговое руководство с LangChain и Qdrant

Как построить RAG-систему для поиска по документам: пошаговое руководство с LangChain и Qdrant

Введение

Представьте: у вас сотни тысяч страниц корпоративных документов — от технической документации до юридических контрактов. Найти нужную информацию — всё равно что иголку в стоге сена. Традиционный поиск по ключевым словам часто выдаёт сотни нерелевантных результатов, заставляя перелопачивать горы текста. Но что, если бы можно было задать вопрос на естественном языке и получить точный ответ, подкреплённый ссылками на источники? Именно это и делает Retrieval-Augmented Generation (RAG) — одна из самых горячих технологий в AI-инженерии 2026 года.

RAG-системы сочетают в себе мощь больших языковых моделей (LLM) и точность семантического поиска. Вместо того чтобы заставлять модель «запоминать» все данные, вы даёте ей доступ к актуальной информации через векторную базу данных. Это не только повышает качество ответов, но и решает проблему галлюцинаций. В этой статье мы разберём, как построить production-ready RAG-пайплайн с использованием LangChain и Qdrant — двух ключевых инструментов в стеке современного AI-инженера.

Если вы хотите глубже разобраться в архитектуре AI-продуктов, включая RAG и другие пайплайны, на платформе asibiont.com есть полноценный курс по этой теме. Но сейчас — к делу.

Что такое RAG-система и почему она нужна?

Retrieval-Augmented Generation — это архитектура, которая добавляет этап поиска релевантной информации перед генерацией ответа. Вместо того чтобы LLM отвечала «из головы», вы сначала находите подходящие фрагменты документов в векторной базе, а затем передаёте их модели как контекст. Это даёт три ключевых преимущества:

  • Актуальность: модель всегда использует последние данные, не нужно часто переобучать.
  • Точность: ответы меньше содержат галлюцинации, так как опираются на источники.
  • Прозрачность: можно показать пользователю, откуда взят ответ, повышая доверие.

Без RAG поиск по документам превращается в классический full-text search — хорош для точных совпадений, но бесполезен при синонимах или перефразировании. RAG же работает на уровне смыслов, используя векторные эмбеддинги.

Шаг 1: Архитектура RAG-пайплайна

Прежде чем писать код, важно понять, из каких блоков состоит система. Вот упрощённая архитектура:

Компонент Задача Пример инструмента
Индекс документов Разбить тексты на чанки, создать эмбеддинги, загрузить в векторную БД LangChain, Qdrant, Sentence-Transformers
Поисковый модуль По запросу найти топ-K релевантных чанков Qdrant (semantic search)
Контекстный промпт Собрать найденные чанки и передать их LLM LangChain Prompt Template
Генератор ответа Сформулировать ответ на основе контекста OpenAI GPT-4o, Llama 3, Claude
Мониторинг Отслеживать latency, cost, качество LangSmith, Grafana

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

Шаг 2: Выбор стека — почему LangChain и Qdrant?

На 2026 год LangChain остаётся стандартом де-факто для построения LLM-пайплайнов. Он предоставляет абстракции для работы с моделями, промптами и векторными хранилищами. Qdrant — это векторная база данных, написанная на Rust, которая предлагает высокую производительность, гибкость фильтрации и простую настройку под production.

Сравним Qdrant с популярными альтернативами:

Особенность Qdrant Chroma Pinecone
Тип хостинга Self-hosted / Cloud Self-hosted Cloud-only
Язык Rust (быстрый) Python Closed-source
Фильтрация Расширенная (payload filtering) Базовая Расширенная
Цена Бесплатно (self-hosted) Бесплатно Платно (по кол-ву векторов)
Поддержка мультивекторности Да Нет Да

Для стартапов и средних проектов Qdrant — идеальный баланс производительности и стоимости. LangChain легко интегрируется с ним через встроенный класс QdrantVectorStore.

Шаг 3: Настройка окружения

Для работы нам понадобятся:

  • Python 3.11+
  • Qdrant (локально через Docker или облачный экземпляр)
  • LangChain (последняя версия)
  • Модель эмбеддингов (например, text-embedding-3-small от OpenAI или intfloat/multilingual-e5-large)
  • LLM для генерации (GPT-4o, Claude 3.5 или Llama 3.1)

Установим зависимости:

pip install langchain langchain-community langchain-openai qdrant-client pypdf sentence-transformers

Шаг 4: Индексация документов

Первый этап — подготовка данных. У нас есть PDF-документы, которые нужно разбить на чанки, превратить в векторы и сохранить в Qdrant.

Разбиение текста на чанки

Выбор стратегии чанкинга критичен. Вот основные подходы:

Стратегия Описание Когда использовать
RecursiveCharacterTextSplitter Рекурсивно делит по разделителям (параграфы, предложения) Универсально
SemanticChunker Делит по смыслу, используя эмбеддинги Когда важна смысловая целостность
TokenTextSplitter Делит по токенам (для моделей с лимитом) Для точного контроля длины

Пример кода с RecursiveCharacterTextSplitter:

from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import PyPDFLoader

loader = PyPDFLoader("document.pdf")
documents = loader.load()

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=1000,
    chunk_overlap=200,
    separators=["\n\n", "\n", " ", ""]
)
chunks = text_splitter.split_documents(documents)
print(f"Создано {len(chunks)} чанков")

Создание эмбеддингов и загрузка в Qdrant

Теперь нужно создать векторное представление каждого чанка и сохранить.

from langchain_openai import OpenAIEmbeddings
from langchain_qdrant import QdrantVectorStore
from qdrant_client import QdrantClient
from qdrant_client.http.models import Distance, VectorParams

# Подключаемся к Qdrant
client = QdrantClient(url="http://localhost:6333")  # или используем облачный URL

# Создаём коллекцию
collection_name = "my_docs"
client.recreate_collection(
    collection_name=collection_name,
    vectors_config=VectorParams(size=1536, distance=Distance.COSINE),
)

# Инициализируем хранилище с LangChain
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vector_store = QdrantVectorStore(
    client=client,
    collection_name=collection_name,
    embedding=embeddings,
)

# Добавляем документы
vector_store.add_documents(documents=chunks)
print("Индексация завершена")

Обратите внимание: размер вектора (1536) должен совпадать с размером эмбеддингов модели. text-embedding-3-small даёт 1536 измерений. Если используете другие модели, проверьте размерность.

Шаг 5: Поиск и генерация

Вторая фаза — обработка запроса пользователя. Мы ищем похожие чанки, формируем контекст и передаём его LLM.

Семантический поиск

# Поиск топ-3 релевантных чанков
query = "Каковы условия возврата товара?"
results = vector_store.similarity_search(query, k=3)

for i, doc in enumerate(results):
    print(f"Результат {i+1}: {doc.page_content[:200]}...")

Qdrant поддерживает также фильтрацию по метаданным (payload filtering). Например, можно искать только по документам за 2026 год:

from qdrant_client.http.models import Filter, FieldCondition, Range

my_filter = Filter(
    must=[
        FieldCondition(key="year", range=Range(gte=2026))
    ]
)
results = vector_store.similarity_search(query, k=3, filter=my_filter)

Формирование ответа с LangChain

Теперь создадим цепочку RAG с использованием LangChain Expression Language (LCEL):

from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser

# Инициализируем LLM
llm = ChatOpenAI(model="gpt-4o", temperature=0)

# Шаблон промпта
prompt = ChatPromptTemplate.from_template(
    """Ты — ассистент, отвечающий на вопросы на основе документов.
Используй ТОЛЬКО следующий контекст для ответа.
Если ответа нет в контексте, скажи, что не знаешь.

Контекст:
{context}

Вопрос: {question}

Ответ:"""
)

# Функция для форматирования контекста
def format_docs(docs):
    return "\n\n".join([d.page_content for d in docs])

# Цепочка RAG
rag_chain = (
    {"context": vector_store.as_retriever(search_kwargs={"k": 3}) | format_docs,
     "question": RunnablePassthrough()}
    | prompt
    | llm
    | StrOutputParser()
)

# Выполняем запрос
answer = rag_chain.invoke("Каковы условия возврата товара?")
print(answer)

Этот код формирует production-ready пайплайн. as_retriever() возвращает объект, который ищет документы и передаёт их в промпт.

Шаг 6: Оптимизация для production

Разработка RAG-системы не заканчивается на базовой связке. Вот что нужно учесть для продакшена:

1. Качество чанкинга

  • Используйте семантический чанкинг (например, через SemanticChunker от LangChain), чтобы не разрывать предложения посередине.
  • Экспериментируйте с размером чанка: от 500 до 1500 токенов — типичный диапазон.
  • Добавляйте метаданные: название документа, дату, страницу — это поможет при фильтрации.

2. Скорость поиска

  • Qdrant поддерживает индексацию HNSW — по умолчанию включено, но можно настроить параметры m и ef_construct.
  • Используйте кэширование эмбеддингов для частых запросов.
  • Рассмотрите гибридный поиск: комбинация векторного и full-text (Qdrant поддерживает через bm25).

3. Мониторинг

  • Отслеживайте latency: цель — < 1 секунда на поиск, < 3 секунд на генерацию.
  • Считайте cost: сколько токенов уходит на эмбеддинги и генерацию.
  • A/B тестируйте разные модели эмбеддингов и LLM.

4. Безопасность

  • Не передавайте в контекст документы, к которым у пользователя нет доступа — используйте фильтрацию по payload в Qdrant.
  • Настройте rate limiting на API.

Шаг 7: Мониторинг и оптимизация затрат

В production RAG-система может быть дорогой, если не контролировать расходы. Вот таблица типичных затрат:

Компонент Стоимость (за 1000 запросов) Как оптимизировать
Эмбеддинги (OpenAI) $0.13 Использовать локальные модели (Sentence-Transformers)
LLM (GPT-4o) $2.50 (вход) + $10.00 (выход) Использовать меньшие модели, кэширование, сокращать контекст
Qdrant (self-hosted) $0 (только железо) Оптимизировать индексы, уменьшить количество чанков
Инфраструктура $20–100/мес Kubernetes для масштабирования

Для снижения затрат:
- Используйте модель эмбеддингов с открытым исходным кодом (например, BAAI/bge-m3).
- Применяйте реранжинг (Reranker) — сначала ищите 20 чанков, затем ранжируйте топ-3, экономя токены LLM.
- Настройте кэш для повторяющихся запросов.

Заключение

RAG-система с LangChain и Qdrant — это не просто модный тренд, а рабочий инструмент, который уже сегодня позволяет строить семантический поиск по документам с минимальными затратами. Мы разобрали полный пайплайн: от индексации PDF до генерации ответа с контекстом. Главные выводы:

  • RAG решает проблему галлюцинаций и актуальности данных.
  • Qdrant — мощная и бесплатная векторная база для self-hosted проектов.
  • LangChain упрощает интеграцию и позволяет быстро переключаться между моделями.
  • Production требует оптимизации чанкинга, мониторинга и затрат.

Хотите научиться строить не только RAG, но и AI-агентов, fine-tune модели и деплоить всё в Kubernetes? На asibiont.com есть интенсивный курс «Full-Stack AI Engineer», где мы разбираем эти темы с production-кодом и практическими проектами. Присоединяйтесь, чтобы стать экспертом в AI-инженерии.

Начните с малого: возьмите свой корпоративный документ, запустите Qdrant в Docker и соберите первый RAG-пайплайн. Результат вас удивит.

← Все статьи

Комментарии