Гибридный RAG: SQL + векторный поиск - архитектура на 2026 год | AiManual
AiManual Logo Ai / Manual.
30 Янв 2026 Гайд

Когда SQL и векторный поиск дерутся за ваши данные: архитектура локального RAG-пайплайна

Пошаговый гайд по созданию локального RAG-пайплайна, объединяющего текстовый SQL и векторный поиск. Архитектура, инструменты, код на 2026 год.

Проблема: почему выбирать между SQL и векторами - тупик

Вот типичная ситуация: у вас есть MSSQL с таблицами клиентов, заказов, инвойсов. И куча PDF-документов - договоры, переписка, техзадания. Пользователь спрашивает: "Покажи все контракты с компанией X за 2025 год, где сумма превышает 100к, и найди похожие случаи в переписке".

Что вы делаете? Запускаете Text-to-SQL для структурированных данных? Или эмбеддинг-поиск по документам? Оба варианта проигрывают. SQL не поймет "похожие случаи в переписке". Векторный поиск не достанет точные суммы из таблиц. В итоге вы или теряете контекст, или получаете неточные данные.

Классическая ошибка: пытаться запихнуть все в одну базу. Векторные БД плохо работают с точными запросами типа "WHERE amount > 100000", а реляционные СУБД не умеют в семантический поиск. Результат - либо медленно, либо неточно.

Решение: гибридный пайплайн, а не компромисс

Мы не выбираем между SQL и векторами. Мы используем оба подхода одновременно, а результаты интегрируем через LLM-арбитра. Вот как это работает:

  1. Пользовательский запрос анализируется классификатором (работает ли он с таблицами, документами или обоими типами данных)
  2. Для SQL-части - Text-to-SQL агент преобразует запрос, выполняет, получает точные данные
  3. Для семантической части - эмбеддинг-поиск находит релевантные документы
  4. Арбитр (LLM) объединяет результаты, убирает противоречия, формирует финальный ответ

Звучит сложно? На практике это 4 микросервиса и один координатор. И все работает локально - никаких облачных API, никаких утечек данных.

Архитектура: что куда и зачем

Представьте себе конвейер. На входе - естественный язык. На выходе - структурированный ответ с ссылками на источники. Вот компоненты:

Компонент Технологии (2026) Зачем нужен
SQL-агент Llama 3.2 8B, SQLCoder-34B, Guardrails Преобразует текст в SQL, проверяет запросы, предотвращает инъекции
Векторный поиск Qdrant 1.9.x, BGE-M3 embeddings, Voyage AI Находит семантически похожие документы, поддерживает мультиязычность
Классификатор Fine-tuned Mistral 7B, fastText Определяет тип запроса (SQL/документы/оба), маршрутизирует
Арбитр GPT-4o-mini локально, Llama 3.1 70B Объединяет результаты, разрешает конфликты, генерирует ответ

Ключевой момент: все компоненты общаются через единый протокол (я использую gRPC с Protobuf). Никаких JSON-over-HTTP для внутренней коммуникации - только бинарные протоколы. Это дает 3-5x прирост скорости на больших объемах данных.

1 Собираем SQL-агента: не просто Text-to-SQL

Большинство статей про Text-to-SQL показывают простые примеры. В реальности все сложнее. Ваш SQL-агент должен:

  • Понимать схему БД (не просто таблицы, но и связи, индексы, типы данных)
  • Генерировать оптимизированные запросы (никаких SELECT * FROM huge_table)
  • Иметь guardrails - защиту от SQL-инъекций, ограничения на сложные JOIN
  • Уметь объяснять, почему выбрал именно такой запрос

Вот минимальная конфигурация для локального SQL-агента:

# docker-compose.sql-agent.yml
version: '3.8'

services:
  sql-agent:
    image: ghcr.io/text-to-sql/llama-sql:latest
    ports:
      - "50051:50051"  # gRPC порт
    volumes:
      - ./schemas:/app/schemas  # Описания схем БД
      - ./guardrails:/app/guardrails  # Правила безопасности
    environment:
      - MODEL_PATH=/models/llama-3.2-8b-instruct-q4
      - DB_TYPE=mssql
      - MAX_JOINS=3  # Ограничение сложности
      - ENABLE_EXPLANATIONS=true
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]

Важный нюанс: не используйте общие эмбеддинг-модели для понимания схемы БД. Специализированные модели вроде SQLCoder-34B показывают на 40% лучшую точность на сложных запросах. Если нужно быстро и локально - Llama 3 с правильными промптами дает 96% точности.

💡
Не храните описания схем в промптах. Используйте векторный поиск по метаданным таблиц. Когда агент видит запрос про "клиентов", он сначала ищет похожие таблицы в векторном индексе метаданных, затем генерирует SQL. Это снижает ошибки на 30%.

2 Векторный поиск: не только эмбеддинги

Типичная ошибка: закинули PDF в Chroma, получили эмбеддинги, думаете что все работает. На деле через месяц поиск деградирует - классическая проблема scale.

Для гибридного пайплайна нужна многоуровневая стратегия:

# Многоуровневый поиск документов
class HybridDocumentSearch:
    def __init__(self):
        # Уровень 1: Ключевые слова (быстро)
        self.keyword_index = WhooshIndex()
        
        # Уровень 2: Эмбеддинги (точно)
        self.vector_db = Qdrant(
            "localhost",
            vectors_config=VectorParams(size=1024, distance=Distance.COSINE)
        )
        
        # Уровень 3: Переранжирование
        self.reranker = BGE-Reranker-v2()
    
    async def search(self, query: str, filters: dict = None):
        # Параллельный поиск
        keyword_results = await self.keyword_search(query)
        vector_results = await self.vector_search(query)
        
        # Fusion ранжирование
        fused = self.fuse_results(keyword_results, vector_results)
        
        # Переранжирование топ-20
        reranked = await self.reranker.rerank(query, fused[:20])
        
        return reranked[:5]  # Топ-5 результатов

Почему именно Qdrant в 2026? Потому что только он нормально поддерживает sparse-dense hybrid search из коробки. И у него есть встроенная поддержка метрик качества поиска, что критично для отладки.

3 Классификатор запросов: мозг системы

Самый недооцененный компонент. Пользователь спрашивает: "Сколько у нас контрактов?". Это SQL-запрос к таблице contracts? Или поиск документов с типом "контракт"? Или и то, и другое?

Классификатор на базе тонко настроенной Mistral 7B решает три задачи:

  1. Intent classification (что хочет пользователь)
  2. Data source routing (куда отправлять запрос)
  3. Query rewriting (как переформулировать для каждого источника)

Пример обучения классификатора:

# Подготовка данных для классификатора
# Каждый пример - запрос + метки

training_data = [
    {
        "query": "Покажи продажи за январь 2025",
        "intent": "structured_query",
        "sources": ["sql"],
        "rewrites": {
            "sql": "SELECT * FROM sales WHERE month = '2025-01'"
        }
    },
    {
        "query": "Найди похожие договоры с клиентом X",
        "intent": "semantic_search",
        "sources": ["vector"],
        "rewrites": {
            "vector": "договор контракт клиент X аналогичный"
        }
    },
    {
        "query": "Какие у нас контракты с суммой больше 100к и есть ли похожие случаи",
        "intent": "hybrid_query",
        "sources": ["sql", "vector"],
        "rewrites": {
            "sql": "SELECT * FROM contracts WHERE amount > 100000",
            "vector": "контракт договор сумма большая аналогичный прецедент"
        }
    }
]

После 500-1000 размеченных примеров точность классификации достигает 92-95%. Без этого компонента система будет постоянно ошибаться с маршрутизацией.

4 Арбитр: когда два источника говорят разное

Сценарий: SQL-агент нашел 3 контракта с клиентом X. Векторный поиск нашел 5 документов с упоминанием клиента X. Но в документах фигурируют суммы, не совпадающие с табличными данными. Кто прав?

Арбитр (Llama 3.1 70B в 4-битном квантовании) делает следующее:

  • Сравнивает данные из разных источников
  • Определяет конфликты (разные суммы, даты, условия)
  • Приоритизирует источники (табличные данные обычно точнее документов)
  • Формирует ответ с указанием расхождений

Промпт арбитра выглядит так:

Ты - арбитр гибридной поисковой системы. Твоя задача:

1. Объединить результаты из SQL (структурированные данные) и векторного поиска (документы)
2. Найти противоречия между источниками
3. Создать связный ответ с указанием:
   - Что известно точно (из таблиц)
   - Что найдено в документах (возможны неточности)
   - Где есть расхождения данных
4. Предложить варианты уточнения

SQL результаты: {sql_results}
Документы: {documents}

Пользовательский запрос: {query}

Ответ должен быть структурированным, но естественным.

Сборка всего пайплайна: Docker Compose для бедных

Вместо Kubernetes (overkill для локальной системы) используем Docker Compose с health checks:

# docker-compose.hybrid-rag.yml
version: '3.8'

services:
  # Классификатор
  classifier:
    build: ./classifier
    ports: ["50052:50051"]
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 30s
    volumes:
      - ./models:/models

  # SQL-агент
  sql-agent:
    build: ./sql-agent
    ports: ["50053:50051"]
    depends_on:
      classifier:
        condition: service_healthy
    environment:
      - CLASSIFIER_URL=classifier:50051

  # Векторная БД
  qdrant:
    image: qdrant/qdrant:latest
    ports: ["6333:6333"]
    volumes:
      - ./qdrant_storage:/qdrant/storage

  # Поисковый сервис
  search-service:
    build: ./search-service
    ports: ["50054:50051"]
    depends_on:
      qdrant:
        condition: service_started

  # Арбитр
  arbiter:
    build: ./arbiter
    ports: ["50055:50051"]
    depends_on:
      sql-agent:
        condition: service_healthy
      search-service:
        condition: service_healthy

  # API Gateway
  gateway:
    build: ./gateway
    ports: ["8080:8080"]
    depends_on:
      arbiter:
        condition: service_healthy

Запускаем одной командой:

docker-compose -f docker-compose.hybrid-rag.yml up -d

Через 2-3 минуты система готова. API Gateway принимает запросы на порту 8080.

Где все ломается: нюансы, которые не пишут в туториалах

После десятка внедрений я собрал топ-5 проблем гибридных RAG-систем:

Проблема Симптом Решение
Конфликт версий данных В таблице сумма 100к, в PDF - 120к Арбитр указывает на расхождение, предлагает проверить актуальность документа
Разные форматы дат SQL: 2025-01-30, документ: 30 января 2025 Нормализация дат на уровне арбитра
Синонимы в запросах Пользователь: "договор", SQL: "contract", вектор: "контракт" Синонимизатор в классификаторе
Время ответа Гибридный запрос занимает 8+ секунд Параллельное выполнение SQL и векторного поиска
Мониторинг Неясно, какой компонент тормозит Distributed tracing (Jaeger) + метрики Prometheus

Особенно внимательно следите за конфликтами данных. В 70% случаев расхождение между таблицами и документами - это не ошибка, а разные версии информации. Ваша система должна это обнаруживать, а не скрывать.

Альтернативы: когда гибридный подход - overkill

Не всегда нужна такая сложная система. Рассмотрите альтернативы:

Для большинства корпоративных сценариев гибридный подход окупается через 2-3 месяца. Особенно когда нужно оживлять архивы документов вместе с операционными данными.

Что дальше: куда движется гибридный RAG в 2026

Тренды, которые я наблюдаю в production-системах:

  1. Автоматическая настройка весов - система сама учится, когда доверять SQL, когда - векторам
  2. Мультимодальность - добавление поиска по изображениям, аудио. Мультимодальный RAG становится стандартом
  3. Edge-развертывание - весь пайплайн работает на ноутбуке, как в браузерном RAG для юристов
  4. Авто-ML для эмбеддингов - автоматический подбор моделей под тип данных

Мой прогноз: к концу 2026 гибридный RAG станет такой же базовой инфраструктурой, как база данных сегодня. Не потому что это модно, а потому что альтернатив нет - данные по своей природе гибридные.

Совет напоследок: начните с простого. Сначала настройте Text-to-SQL. Затем добавьте векторный поиск по документам. И только потом соедините их через классификатор. Так вы найдете специфичные для ваших данных проблемы на раннем этапе. И да, обязательно посмотрите полный каталог инструментов для локального ИИ - там есть все для сборки такой системы.

Архитектура готова. Компоненты выбраны. Осталось собрать и настроить под свои данные. Удачи - и помните, что идеальный гибридный поиск это не тот, который всегда находит ответ, а тот, который понимает, когда ответа нет.