Elasticsearch для RAG: гибридный поиск вместо векторных хранилищ | Гайд 2026 | AiManual
AiManual Logo Ai / Manual.
23 Мар 2026 Гайд

Оптимизация RAG для LLM: практическое руководство по использованию Elasticsearch/OpenSearch вместо векторных хранилищ

Практическое руководство по замене векторных баз на Elasticsearch/OpenSearch в RAG-пайплайнах. Используем TF-IDF, BM25 и легкие BERT-эмбеддинги для быстрого и т

Зачем вам это? Потому что векторы — это дорого и медленно

Вы построили RAG-систему. Она работает. Но каждый запрос к векторной базе — это 50-200 мс ожидания, пока GPU посчитает косинусное сходство. Ваш сервер нагружен, как грузовик в гору. А точность все равно скачет. Знакомо?

В 2026 году специализированные векторные хранилища (Pinecone, Weaviate, Qdrant) стали стандартом де-факто. Но они создали новую проблему — избыточную сложность. Зачем держать отдельный кластер для векторов, если у вас уже есть Elasticsearch или OpenSearch для логов и текстового поиска? Они умеют в семантику. Про это почему-то молчат.

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

Серьезно? Elasticsearch для семантического поиска?

Да. И вот что изменилось к 2026 году:

  • Elasticsearch 9.x и OpenSearch 3.x имеют встроенную поддержку dense vector fields и приближенного поиска k-NN. Больше не нужны отдельные плагины.
  • Появились легкие модели для эмбеддингов, которые работают на CPU быстрее, чем вы успеваете произнести "трансформер". Например, BGE-M3-Compact или GTE-Qwen2-1.5B — они дают качество, близкое к большим моделям, при размере в 5-10 раз меньше.
  • Гибридный поиск (BM25 + векторное сходство) стал штатной функцией. Вы можете взвешивать результаты, комбинировать запросы и не выбирать что-то одно.

Суть подхода проста: используйте то, что уже есть в вашем стеке. Elasticsearch/OpenSearch — это не просто поиск по логам. Это полноценный движок для information retrieval, который пережил хайп вокруг векторов и вышел обновленным. Если вы читали наш материал про безвекторный RAG, то это его практическая реализация с использованием промышленных инструментов.

1 Готовим Elasticsearch: индекс для гибридного поиска

Забудьте про отдельный сервис для векторов. Создаем один индекс, который будет хранить и текст, и его векторное представление. Вот mapping для Elasticsearch 9.4 (актуален на март 2026).

{
  "mappings": {
    "properties": {
      "text": {
        "type": "text",
        "analyzer": "russian"
      },
      "vector": {
        "type": "dense_vector",
        "dims": 384,
        "index": true,
        "similarity": "cosine"
      },
      "metadata": {
        "type": "object"
      }
    }
  },
  "settings": {
    "index": {
      "number_of_shards": 1,
      "number_of_replicas": 0
    }
  }
}

Обратите внимание на "dims": 384. Это размерность эмбеддингов для легкой модели all-MiniLM-L12-v3 (да, она все еще в строю в 2026, но есть и новее). Если используете BGE-M3-Compact, размерность будет 1024. Главное — соответствие модели и mapping.

💡
Настройте анализатор под ваш язык. Для русского используйте встроенный или установите плагин анализаторов. Это критично для качества BM25. Если не настроить — поиск по словам "создать" и "создал" будет считать разными, и вы получите мусор вместо релевантных документов. Об этом мы подробно писали в статье про деградацию поиска.

2 Индексация: генерируем эмбеддинги на лету или заранее?

Тут два пути. Первый — классический: предрассчитать эмбеддинги для всех документов и загрузить в индекс. Второй — использовать ingest pipeline с Painless скриптом для генерации векторов на стороне Elasticsearch (если у вас стоит ML-нода). Но я рекомендую первый вариант для контроля.

Вот как это выглядит на Python с использованием SentenceTransformers. Возьмем актуальную на 2026 год легкую модель из Hugging Face Hub (партнерская ссылка).

from sentence_transformers import SentenceTransformer
from elasticsearch import Elasticsearch
import numpy as np

# Используем компактную модель, оптимизированную для CPU
# На март 2026 актуальна версия 2.3.0 фреймворка
model = SentenceTransformer('BAAI/bge-m3-compact', device='cpu')

# Ваши документы
documents = [
    {"text": "Как настроить Kubernetes кластер для production", "category": "devops"},
    {"text": "Оптимизация запросов PostgreSQL с использованием индексов", "category": "database"},
]

# Генерация эмбеддингов
texts = [doc["text"] for doc in documents]
embeddings = model.encode(texts, normalize_embeddings=True)

# Подключение к Elasticsearch
# Для OpenSearch используйте from opensearchpy import OpenSearch
es = Elasticsearch("http://localhost:9200")

# Индексация
for i, doc in enumerate(documents):
    es.index(
        index="rag_documents",
        document={
            "text": doc["text"],
            "vector": embeddings[i].tolist(),
            "metadata": {"category": doc["category"]}
        }
    )

Почему BGE-M3-Compact? Потому что она дает качество на уровне больших моделей, но в 5 раз быстрее на CPU. А если у вас совсем мало ресурсов, присмотритесь к all-MiniLM-L6-v4 (размерность 384) — она стала стабильнее в последних релизах.

3 Поисковый запрос: гибрид BM25 и векторов

Вот где начинается магия. Мы не просто ищем по векторам. Мы комбинируем лексический поиск (который отлично ловит конкретные термины) и семантический (который понимает смысл). В Elasticsearch 9.4 для этого есть запрос hybrid или простая комбинация через bool.should.

def hybrid_search(query, es_index="rag_documents", top_k=5):
    # 1. Генерируем эмбеддинг для запроса
    query_embedding = model.encode(query, normalize_embeddings=True).tolist()
    
    # 2. Формируем тело запроса
    search_body = {
        "size": top_k,
        "query": {
            "hybrid": {
                "queries": [
                    {
                        "match": {
                            "text": {
                                "query": query,
                                "boost": 0.7  # Вес для BM25
                            }
                        }
                    },
                    {
                        "knn": {
                            "field": "vector",
                            "query_vector": query_embedding,
                            "k": top_k,
                            "num_candidates": 100,
                            "boost": 0.3  # Вес для векторного поиска
                        }
                    }
                ]
            }
        }
    }
    
    # 3. Выполняем запрос
    response = es.search(index=es_index, body=search_body)
    return [hit["_source"] for hit in response["hits"]["hits"]]

Коэффициенты boost (0.7 для текста, 0.3 для вектора) — это отправная точка. Настройте их под свои данные. Если у вас много синонимов или жаргона — увеличьте вес векторов. Если важны точные формулировки — поднимите BM25.

Не повторяйте ошибку, которую я видел в трех проектах подряд: не используйте векторный поиск для запросов типа "ошибка 404". Это лексический запрос, и BM25 справится в 10 раз быстрее. Векторы хороши для смысла: "как исправить проблему с доступностью сервиса".

4 Интеграция с RAG пайплайном

Теперь нужно встроить этот поиск в вашу RAG-систему. Допустим, у вас есть цепочка на LangChain или LlamaIndex. Вот пример с минимальными изменениями.

from langchain.llms import Ollama  # Ollama 0.6.x в 2026
from langchain.prompts import ChatPromptTemplate

# Ваша функция гибридного поиска (см. выше)
retrieved_docs = hybrid_search("Как настроить репликацию в PostgreSQL?")

# Формируем контекст
context = "\n".join([doc["text"] for doc in retrieved_docs])

# Промпт с учетом контекста
prompt_template = ChatPromptTemplate.from_messages([
    ("system", "Ты — экспертный ассистент. Ответь на вопрос, используя только предоставленный контекст. Если в контексте нет ответа, скажи 'Не знаю'."),
    ("human", "Контекст: {context}\n\nВопрос: {question}")
])

prompt = prompt_template.format(context=context, question=question)

# Запрос к локальной LLM, например, через Ollama
llm = Ollama(model="qwen2.5:7b", temperature=0)  # Актуальная компактная модель на 2026
response = llm.invoke(prompt)

Если вы используете более сложную архитектуру, например, с re-ранкерами или декомпозицией запросов, то гибридный поиск станет только первым этапом. Но даже он один дает прирост точности на 15-25% по сравнению с чистым векторным поиском на разнородных данных.

Ошибки, которые убьют вашу производительность

  • Использование огромных эмбеддингов (1024+ размерности) без необходимости. Каждая лишняя размерность — это память и время. Для большинства задач хватит 384-512. Тестируйте.
  • Отсутствие фильтрации по метаданным. Elasticsearch позволяет фильтровать результаты до или после векторного поиска. Если вы ищете "ошибка аутентификации" только в документах по API, добавьте filter в запрос. Иначе получите мануалы по настройке сети где-то в результатах.
  • Индексация без чанкинга. Не загружайте целые PDF-файлы как один документ. Разбивайте на логические части (абзацы, разделы). Поиск по 10-20 предложениям работает точнее.
  • Игнорирование настройки анализаторов. Как уже говорил, без правильной обработки русского языка ваш BM25 будет бесполезен.

А что насчет OpenSearch?

OpenSearch — это форк Elasticsearch с открытой лицензией. На момент марта 2026 года, версия 3.2 практически догнала Elasticsearch по функциональности для RAG. Плагин k-NN встроен по умолчанию. Если вы предпочитаете полностью open-source стек без коммерческих лицензий — выбирайте OpenSearch. API почти идентичен.

Главное отличие в экосистеме: у Elasticsearch больше готовых интеграций с облачными провайдерами, у OpenSearch — активное сообщество и плагины для специфичных задач. Для внутренних проектов я чаще выбираю OpenSearch. Для корпоративных — Elasticsearch с официальной поддержкой.

Когда этот подход не сработает?

Есть сценарии, где специализированные векторные базы все еще лучше:

  • У вас миллиарды векторных эмбеддингов (да, именно миллиарды). Специализированные базы оптимизированы для ultra-large scale.
  • Вам нужен поиск по мультимодальным эмбеддингам (картинки, аудио, видео). Elasticsearch/OpenSearch работают с векторами, но их сила — в тексте.
  • Ваша команда уже глубоко вложилась в экосистему Pinecone или Weaviate, и переписывание кода обойдется дороже, чем экономия на инфраструктуре.

Но для 95% проектов RAG, где данные — это тексты (до 100 миллионов документов), Elasticsearch/OpenSearch — это разумный, производительный и простой в сопровождении выбор. Вы избавляетесь от лишнего сервиса, сокращаете latency и получаете контроль над всем пайплайном.

Попробуйте. Начните с одного индекса. Сравните latency и точность с вашим текущим решением. Скорее всего, вы удивитесь. А если упретесь в ограничения — всегда можно добавить дополнительные этапы фильтрации или ранжирования. Удачи.

Подписаться на канал