Как построить 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-пайплайн. Результат вас удивит.
Комментарии