Гибридный поиск в Agentic RAG: совмещение векторов и ключевых слов 2026 | AiManual
AiManual Logo Ai / Manual.
13 Мар 2026 Гайд

Гибридный поиск в Agentic RAG: когда векторы и ключевые слова перестают спорить

Пошаговое руководство по внедрению гибридного поиска в Agentic RAG-системы. Комбинируем семантический поиск и BM25 для точного извлечения контекста.

Агент против поиска: почему одна технология всегда проигрывает

Вы построили умного агента. Он умеет планировать, разбивать запрос на подзадачи, вызывать инструменты. Но когда дело доходит до поиска в вашей базе документов - он слепнет. Классический RAG с векторами хорош для общих вопросов, но спотыкается о конкретику. Пользователь спрашивает "как настроить параметр max_connections в PostgreSQL 16.3", а система приносит общую статью про оптимизацию баз данных. Знакомо?

Проблема в фундаментальном несоответствии. Векторные эмбеддинги отлично улавливают семантику, но теряют точные термины, версии софта, коды ошибок. Ключевой поиск (BM25) ловит конкретные слова, но не понимает смысл. Agentic RAG требует от поиска максимальной точности - каждый неверный документ ломает цепочку рассуждений агента.

Agentic RAG - это RAG-система, где языковая модель действует как автономный агент. Она не просто ждет промпта, а сама планирует выполнение задачи: разбивает запрос, решает, когда и какой поиск выполнить, как объединять результаты. Точность поиска становится критичной - одна ошибка на первом шаге рушит всю цепочку.

1Что ломает обычный поиск в Agentic RAG

Возьмем реальный сценарий. Агент получает запрос: "Сравни производительность Redis 7.2 и KeyDB 6.3 на AWS c6g.8xlarge". В идеальном мире агент должен:

  1. Найти спецификации инстанса c6g.8xlarge
  2. Найти бенчмарки Redis 7.2
  3. Найти бенчмарки KeyDB 6.3
  4. Сравнить результаты

Векторный поиск с моделью text-embedding-3-large (последняя доступная версия на март 2026) отлично найдет документы про производительность баз данных. Но может пропустить критически важный документ, где нет слова "производительность", зато есть конкретные цифры QPS и задержек.

Ключевой поиск по BM25 поймает "Redis 7.2" и "KeyDB 6.3", но может проигнорировать контекст сравнения или обсуждение именно ARM-инстансов.

💡
В Agentic RAG агент часто выполняет несколько последовательных поисковых запросов. Погрешность накапливается. Если первый поиск вернет нерелевантные документы, вся цепочка действий агента пойдет по неправильному пути.

Гибридный поиск: не сложение, а умножение

Гибридный поиск - это не просто "взять результаты BM25 и векторного поиска, склеить список". Это стратегия, где каждая технология компенсирует слабости другой.

На 2026 год появились новые подходы к гибридизации:

  • Reciprocal Rank Fusion (RRF) - все еще золотой стандарт, но с модификациями
  • Весовые схемы на основе доверия - система оценивает, какой тип поиска лучше для конкретного запроса
  • Нейросетевые ранжировщики - отдельная модель принимает финальное решение

Но в Agentic RAG есть своя специфика. Агент может метаданные о типе запроса. Если он ищет "код ошибки 0x80070005", это явный сигнал для ключевого поиска. Если запрос "оптимизация сложных JOIN-запросов" - для векторного.

Тип запросаСила векторовСила ключевых словРекомендуемый вес
Конкретные термины, кодыСлабаяСильнаяBM25: 0.8, Векторы: 0.2
Концептуальные вопросыСильнаяСлабаяBM25: 0.3, Векторы: 0.7
Смешанные запросыСредняяСредняяBM25: 0.5, Векторы: 0.5

2Архитектура гибридного поиска для Agentic RAG

Собираем систему, которая не просто ищет, а понимает контекст агента. Нам нужны три компонента:

# Псевдокод архитектуры
class HybridSearchForAgenticRAG:
    def __init__(self):
        self.vector_store = Qdrant(host="localhost", port=6333)  # или Weaviate 1.25+
        self.bm25_index = BM25Okapi(corpus)
        self.query_classifier = QueryClassifier()  # определяет тип запроса
        self.reranker = CrossEncoder('cross-encoder/ms-marco-MiniLM-L-6-v2')
    
    def search(self, query: str, agent_context: dict):
        # Анализируем, что ищет агент
        search_type = self.classify_query(query, agent_context)
        
        # Получаем результаты из обоих индексов
        vector_results = self.vector_search(query, limit=20)
        keyword_results = self.bm25_search(query, limit=20)
        
        # Применяем гибридное ранжирование с учетом контекста агента
        fused = self.context_aware_fusion(
            vector_results, 
            keyword_results, 
            search_type,
            agent_context
        )
        
        # Нейросетевой реранкинг
        reranked = self.reranker.rerank(query, fused[:10])
        
        return reranked[:5]  # топ-5 для агента

Ключевое отличие от обычного RAG - agent_context. Агент передает метаинформацию: "я ищу конкретные цифры для сравнения", "мне нужны точные команды для выполнения", "это шаг 3 из 5 в плане".

Самая частая ошибка - игнорировать контекст агента. Если агент ищет документы для генерации SQL-запроса, а система приносит общие статьи про базы данных - цепочка ломается. Контекст должен влиять на веса гибридного поиска.

Пошаговая реализация: от теории к работающему коду

1Шаг 1: Подготовка индексов

Нельзя просто взять готовый векторный индекс и добавить BM25. Данные должны быть синхронизированы.

# Правильная подготовка данных
from qdrant_client import QdrantClient
from qdrant_client.models import VectorParams, Distance
from rank_bm25 import BM25Okapi
import numpy as np

# 1. Загружаем и чанкуем документы
documents = load_and_chunk_documents(
    chunk_size=512, 
    chunk_overlap=50,
    smart_chunking=True  # режет по смысловым границам
)

# 2. Создаем эмбеддинги с современной моделью
# На март 2026 актуальны text-embedding-3-large или альтернативы типа BGE-M3
embeddings = create_embeddings(
    texts=[doc.text for doc in documents],
    model="text-embedding-3-large",
    dimensions=3072  # используем максимальную размерность
)

# 3. Индексируем в Qdrant 1.8+ (поддержка sparse векторов)
client = QdrantClient("localhost", port=6333)
client.create_collection(
    collection_name="docs",
    vectors_config=VectorParams(
        size=3072,
        distance=Distance.COSINE
    )
)

# 4. Параллельно строим BM25 индекс
# Важно: токенизируем так же, как при поиске
bm25_corpus = [tokenize(doc.text) for doc in documents]
bm25_index = BM25Okapi(bm25_corpus)

2Шаг 2: Классификатор запросов

Агент помогает классифицировать, но нужен fallback на случай, если агент не передал контекст.

class QueryClassifier:
    def classify(self, query: str, agent_context: dict = None):
        """Определяем тип запроса"""
        
        # Если агент передал явный тип - используем его
        if agent_context and "search_type" in agent_context:
            return agent_context["search_type"]
        
        # Эвристики для определения типа запроса
        query_lower = query.lower()
        
        # Признаки запроса на ключевые слова
        keyword_patterns = [
            r'\d+\.\d+\.\d+',  # версии типа 1.2.3
            r'0x[0-9a-fA-F]+',     # hex коды
            r'error\s+\d+',       # ошибка 404
            r'[A-Z]{2,}',          # аббревиатуры SQL, API, JSON
        ]
        
        keyword_score = 0
        for pattern in keyword_patterns:
            if re.search(pattern, query):
                keyword_score += 1
        
        # Длина запроса - короткие часто требуют точного match
        if len(query.split()) < 4:
            keyword_score += 1
        
        # Определяем тип
        if keyword_score >= 2:
            return "keyword"
        elif "как" in query_lower or "почему" in query_lower:
            return "conceptual"
        else:
            return "mixed"

3Шаг 3: Гибридное ранжирование с RRF

Reciprocal Rank Fusion работает, но его нужно модифицировать под Agentic RAG.

def context_aware_rrf(vector_results, keyword_results, search_type, k=60):
    """
    Модифицированный RRF с учетом типа поиска
    k = 60 (стандартное значение)
    """
    
    # Веса в зависимости от типа запроса
    weights = {
        "keyword": {"bm25": 0.8, "vector": 0.2},
        "conceptual": {"bm25": 0.2, "vector": 0.8},
        "mixed": {"bm25": 0.5, "vector": 0.5}
    }
    
    w = weights[search_type]
    
    # Словари для быстрого поиска ранга по ID документа
    vector_ranks = {doc.id: rank for rank, doc in enumerate(vector_results)}
    keyword_ranks = {doc.id: rank for rank, doc in enumerate(keyword_results)}
    
    # Все уникальные документы
    all_docs = set(vector_ranks.keys()) | set(keyword_ranks.keys())
    
    scores = {}
    for doc_id in all_docs:
        score = 0.0
        
        # Взвешенный RRF
        if doc_id in vector_ranks:
            score += w["vector"] / (k + vector_ranks[doc_id] + 1)
        if doc_id in keyword_ranks:
            score += w["bm25"] / (k + keyword_ranks[doc_id] + 1)
        
        scores[doc_id] = score
    
    # Сортируем по убыванию score
    sorted_docs = sorted(scores.items(), key=lambda x: x[1], reverse=True)
    
    # Возвращаем документы в нужном порядке
    # Нужно мапить ID обратно к объектам документов
    return [get_doc_by_id(doc_id) for doc_id, _ in sorted_docs]

Почему это работает лучше обычного RAG

Гибридный поиск в Agentic RAG дает прирост точности 30-50% на сложных запросах. Но цифры - не главное. Главное - стабильность.

Агент выполняет цепочку из 5 поисковых запросов. Если точность каждого 90%, итоговая вероятность успеха цепочки: 0.9^5 = 0.59. 59% шанс, что агент выполнит задачу правильно.

С гибридным поиском точность поднимается до 95%. Вероятность успеха цепочки: 0.95^5 = 0.77. Это уже рабочий инструмент, а не игрушка.

💡
В production системах используйте инкрементальное обучение классификатора запросов. Собирайте feedback от агента: какие результаты привели к успешному выполнению задачи, какие - к провалу. Через месяц система сама научится определять оптимальные веса для вашей предметной области.

Что будет дальше: гибридный поиск через год

На 2026 год мы видим две тенденции:

  1. Нейросетевые ранжировщики на чипе - выделенный ускоритель для re-ranking рядом с векторной БД
  2. Агенты, обучающие поиск под себя - агент не просто использует поиск, а оптимизирует его параметры под свои задачи

Самый перспективный подход - когда агент и поисковая система становятся единым организмом. Агент сообщает поиску: "следующий запрос будет о сравнении двух технологий, готовь соответствующие веса". Поиск отвечает: "для таких запросов у меня точность 92%, предлагаю расширить запрос синонимами".

Это уже не RAG. Это диалог равных партнеров. Один понимает смысл, другой - факты. Вместе они решают задачи, которые не под силу ни одному LLM.

Начните с гибридного поиска сегодня. Через год ваша система научится разговаривать сама с собой. И это будет самый продуктивный диалог в вашей инфраструктуре.

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