Проблема: почему выбирать между SQL и векторами - тупик
Вот типичная ситуация: у вас есть MSSQL с таблицами клиентов, заказов, инвойсов. И куча PDF-документов - договоры, переписка, техзадания. Пользователь спрашивает: "Покажи все контракты с компанией X за 2025 год, где сумма превышает 100к, и найди похожие случаи в переписке".
Что вы делаете? Запускаете Text-to-SQL для структурированных данных? Или эмбеддинг-поиск по документам? Оба варианта проигрывают. SQL не поймет "похожие случаи в переписке". Векторный поиск не достанет точные суммы из таблиц. В итоге вы или теряете контекст, или получаете неточные данные.
Классическая ошибка: пытаться запихнуть все в одну базу. Векторные БД плохо работают с точными запросами типа "WHERE amount > 100000", а реляционные СУБД не умеют в семантический поиск. Результат - либо медленно, либо неточно.
Решение: гибридный пайплайн, а не компромисс
Мы не выбираем между SQL и векторами. Мы используем оба подхода одновременно, а результаты интегрируем через LLM-арбитра. Вот как это работает:
- Пользовательский запрос анализируется классификатором (работает ли он с таблицами, документами или обоими типами данных)
- Для SQL-части - Text-to-SQL агент преобразует запрос, выполняет, получает точные данные
- Для семантической части - эмбеддинг-поиск находит релевантные документы
- Арбитр (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% точности.
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 решает три задачи:
- Intent classification (что хочет пользователь)
- Data source routing (куда отправлять запрос)
- 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
Не всегда нужна такая сложная система. Рассмотрите альтернативы:
- Только SQL - если у вас 95% запросов к структурированным данным. Используйте Text-to-SQL агента с guardrails.
- Только векторы - если работаете с документами, кодом. Посмотрите Ragex для семантического поиска по коду.
- Упрощенный гибрид - если ресурсы ограничены. HashIndex предлагает компромисс между точностью и скоростью.
Для большинства корпоративных сценариев гибридный подход окупается через 2-3 месяца. Особенно когда нужно оживлять архивы документов вместе с операционными данными.
Что дальше: куда движется гибридный RAG в 2026
Тренды, которые я наблюдаю в production-системах:
- Автоматическая настройка весов - система сама учится, когда доверять SQL, когда - векторам
- Мультимодальность - добавление поиска по изображениям, аудио. Мультимодальный RAG становится стандартом
- Edge-развертывание - весь пайплайн работает на ноутбуке, как в браузерном RAG для юристов
- Авто-ML для эмбеддингов - автоматический подбор моделей под тип данных
Мой прогноз: к концу 2026 гибридный RAG станет такой же базовой инфраструктурой, как база данных сегодня. Не потому что это модно, а потому что альтернатив нет - данные по своей природе гибридные.
Совет напоследок: начните с простого. Сначала настройте Text-to-SQL. Затем добавьте векторный поиск по документам. И только потом соедините их через классификатор. Так вы найдете специфичные для ваших данных проблемы на раннем этапе. И да, обязательно посмотрите полный каталог инструментов для локального ИИ - там есть все для сборки такой системы.
Архитектура готова. Компоненты выбраны. Осталось собрать и настроить под свои данные. Удачи - и помните, что идеальный гибридный поиск это не тот, который всегда находит ответ, а тот, который понимает, когда ответа нет.