LLM-Independent Adaptive RAG: retrieval без LLM | AiManual
AiManual Logo Ai / Manual.
29 Май 2026 Гайд

LLM-Independent Adaptive RAG: как запускать retrieval без участия языковой модели

Разбираем новый подход от Skoltech/AIRI: Adaptive RAG, который не требует LLM для принятия решений о поиске. Снижаем затраты и галлюцинации.

Почему RAG, который полагается на LLM для retrieval, — это дорогая ошибка

Вы собрали RAG-систему по статье RAG за 15 минут, подняли FAISS, прикрутили GPT — и оно работает. Но через месяц счета за API вызывают нервный тик. Всё потому, что в классическом адаптивном RAG (adaptive RAG) решение о том, нужно ли вообще лезть в базу знаний, принимает сама языковая модель. Каждый запрос — это как минимум один вызов дорогой модели. А если запросов тысячи?

Проблема: LLM тратит вычислительные ресурсы не только на генерацию ответа, но и на оценку релевантности — нужно ли искать документы. Это как вызывать такси, чтобы доехать до мусоропровода.

Исследователи из Skoltech, AIRI и MWS AI (2025-2026) предложили радикальное решение: выкинуть LLM из контура принятия решений о retrieval. Встречайте LLM-Independent Adaptive RAG — подход, где всю интеллектуальную работу по определению необходимости поиска выполняют лёгкие эмбеддинг-модели и простые классификаторы. Результат: до 80% экономии по latency и стоимости без потери качества ответов.

Как работает адаптивный retrieval без участия LLM

Идея до смешного проста, но почему-то до 2025 года все упорно заставляли LLM играть роль диспетчера. В LLM-Independent Adaptive RAG весь пайплайн выглядит так:

  1. Вопрос приходит — пользователь спрашивает что-то в чат.
  2. Эмбеддинг запроса — специальная лёгкая модель (например, intfloat/multilingual-e5-small или BAAI/bge-small-en-v1.5) превращает текст в вектор размерностью 384.
  3. Классификатор решает: брать контекст или нет — простой MLP с одним скрытым слоём (параметров меньше, чем у одного слоя BERT) смотрит на эмбеддинг и выдаёт бинарный ответ: search или no-search.
  4. Если search — отправляем запрос в FAISS, получаем top-k документов, добавляем их в промпт.
  5. Если no-search — отправляем вопрос напрямую LLM без контекста.

Ключевое отличие от обычного adaptive RAG: решение принимается за микросекунды на CPU, без вызова LLM. Экономятся не только деньги, но и время — latency падает с ~1-2 секунд до 10-50 миллисекунд на этапе принятия решения.

Зачем это нужно, если есть правильный RAG?

В статье «Почему RAG-система извлекает правильные данные, но даёт неверный ответ» мы разбирали случай, когда retrieval работает идеально, а ответ всё равно плохой. Одна из причин — LLM слишком «умничает», пытаясь оценить, нужен ли ей контекст. Часто модель вместо обращения к документам просто генерирует ответ по своим внутренним знаниям, даже если они устарели. LLM-Independent подход избавляет от этой проблемы: классификатор обучен на реальных примерах, когда поиск необходим, и не поддаётся «галлюцинациям уверенности».

Пошаговый план: реализация с нуля

Давайте соберём свой LLM-Independent Adaptive RAG. Нам понадобятся:

  • Python 3.10+
  • sentence-transformers — для эмбеддингов
  • scikit-learn — для классификатора (MLP)
  • faiss-cpu — для векторного поиска
  • datasets — для генерации обучающих данных
  • torch — опционально, если захотим свой MLP

1Готовим обучающие данные: вопросы, которым нужен поиск

Главная сложность — собрать датасет, где для каждого вопроса размечено, нужно ли извлекать документы. В оригинальной работе Skoltech/AIRI использовали синтетическую генерацию: брали датасеты типа HotpotQA (требуют поиска) и смешивали с тривиальными вопросами (например, «Кто написал „Войну и мир“?» — такие вопросы LLM может ответить без контекста).

from datasets import load_dataset
from sentence_transformers import SentenceTransformer
from sklearn.linear_model import LogisticRegression

# Загружаем датасет с вопросами
# Для примера используем часть HotpotQA (требует retrieval)
# и простые вопросы из Natural Questions (может ответить без поиска)
dataset = load_dataset("hotpot_qa", "distractor", split="train[:5000]")

# Эмбеддинг-модель (лёгкая)
model = SentenceTransformer("intfloat/multilingual-e5-small")

# Генерируем эмбеддинги
embeddings = model.encode(dataset["question"], show_progress_bar=True)
labels = [1] * len(dataset)  # всё требует поиска (упрощённо)

# Добавляем негативные примеры (без поиска)
# ... (опущено для краткости)

На практике можно взять готовый датасет из репозитория MWS AI (поищите на Hugging Face — к маю 2026 года там есть размеченные данные).

2Тренируем классификатор

Мы используем LogisticRegression (можно MLP, но для демо хватит и его).

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(
    embeddings, labels, test_size=0.2, random_state=42
)

clf = LogisticRegression(max_iter=1000)
clf.fit(X_train, y_train)
print(f"Accuracy: {clf.score(X_test, y_test):.3f}")

Типичная ошибка: использовать слишком сложный классификатор (нейронку с 2+ слоями) — появляется оверфиттинг, и на новых вопросах модель начинает ошибаться. LogisticRegression или MLP с одним скрытым слоем (50-100 нейронов) — золотая середина.

3Собираем пайплайн

from sentence_transformers import SentenceTransformer
from sklearn.linear_model import LogisticRegression
import faiss
import numpy as np

class LLMIndependentAdaptiveRAG:
    def __init__(self, retriever, llm, classifier, embedder):
        self.retriever = retriever
        self.llm = llm
        self.clf = classifier
        self.embedder = embedder

    def answer(self, question, top_k=3):
        emb = self.embedder.encode([question])
        need_search = self.clf.predict(emb)[0]
        
        if need_search:
            docs = self.retriever.search(question, top_k)
            context = "\n".join(docs)
            prompt = f"Context: {context}\n\nQuestion: {question}\nAnswer:"
        else:
            prompt = f"Question: {question}\nAnswer:"
        
        return self.llm.generate(prompt)

В качестве retriever используем FAISS (как в гайде RAG за 15 минут), а в качестве llm — любой Open Source (например, Llama 3.2 3B или Qwen 2.5 7B).

Почему это работает: анализ ошибок LLM при принятии решений

Исследователи из Skoltech провели эксперимент: сравнили решения LLM (GPT-4o) и легковесного классификатора (LogisticRegression на эмбеддингах bge-small) о необходимости retrieval на 10 000 вопросов. Оказалось:

  • LLM ошибалась в 12% случаев — то есть либо запускала retrieval, когда не нужно (лишние затраты), либо пропускала, когда нужно (галлюцинации).
  • Классификатор ошибался в 13% — практически та же точность, но в 100 раз быстрее и почти бесплатно.

А если подобрать эмбеддинг-модель под задачу (например, дообучить её на датасете запросов), ошибки снижаются до 7%. Так что аргумент «LLM умнее» не работает: классификатор на порядок эффективнее при равном качестве.

Нюансы, которые вас убьют на продакшене

Не наступайте на грабли, которые я собрал за несколько месяцев внедрения этой схемы:

  1. Дисбаланс классов. В реальном чате 80% вопросов — это «Привет!», «Как дела?», «Что нового?» — им не нужен retrieval. Если обучить классификатор на сырых данных, он будет предсказывать только «no-search». Нужно сбалансировать выборку (oversampling или синтетика).
  2. Домен-специфичные вопросы. Классификатор, обученный на общих данных, может не понять, что вопрос про внутренний API компании требует поиска. Решение: собрать 100-200 реальных логов запросов и разметить вручную.
  3. Порог уверенности. Используйте predict_proba вместо predict и ставьте порог (например, 0.6) — это позволяет гибко настраивать чувствительность. Если порог снизить, будет больше false positives (лишний поиск), зато меньше пропусков.
  4. Кеширование эмбеддингов. Если у вас часто повторяющиеся вопросы (например, тикеты поддержки), кешируйте результат классификации — сэкономите ещё 30% времени.

Важный совет: не пытайтесь встроить этот классификатор внутрь LLM (через инструменты). Именно LLM-independent означает, что слой принятия решений живёт до вызова модели. Иначе вы снова привязываетесь к LLM и теряете все преимущества.

Когда LLM-Independent подход не сработает

Я бы солгал, если бы сказал, что это серебряная пуля. В трёх случаях лучше вернуться к классическому adaptive RAG:

  • Вопросы, требующие не только поиска, но и переформулировки. Например: «Найди последние новости про ИИ и сравни с прошлым годом» — тут LLM должна сама решить, сколько итераций retrieval делать. Без LLM-диспетчера не обойтись (но это уже ближе к агентному RAG, описанному в статье «Agentic RAG»).
  • Динамически меняющийся контекст. Если база знаний обновляется каждые 5 минут, классификатор может устареть — его придётся переобучать с той же периодичностью.
  • Запросы на редких языках. Эмбеддинг-модели для малоресурсных языков пока работают плохо, и классификатор будет ошибаться чаще. Тут LLM с мультиязычной поддержкой всё ещё выигрывает.

Что дальше: куда движется adaptive RAG в 2026

В мае 2026 года команда MWS AI уже анонсировала гибридный подход: LLM-Independent + агентный RAG. Идея: классификатор решает, нужен ли retrieval, но если нужен — запускается полноценный агент с несколькими шагами. Это позволяет снизить latency на 60%, сохранив гибкость. Следите за их репозиториями — к концу года обещают релиз.

Лично я считаю, что future RAG — за многоуровневыми классификаторами. Сначала проверка на тривиальные запросы (не нужен retrieval), затем на запросы, которым достаточно одного документа, и только потом — вызов агента. Каждый уровень можно сделать LLM-independent. Так мы получим систему, где 90% запросов обрабатываются за миллисекунды и копейки, а LLM вызывается только для сложных случаев.

Не дайте галлюцинациям и дороговизне убить ваш проект. Начните с LLM-Independent Adaptive RAG уже сегодня.

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