Реализация Multi-Query RAG для повышения качества извлечения

Проектируем и внедряем системы искусственного интеллекта: от прототипа до production-ready решения. Наша команда объединяет экспертизу в машинном обучении, дата-инжиниринге и MLOps, чтобы AI работал не в лаборатории, а в реальном бизнесе.
Показано 1 из 1Все 1566 услуг
Реализация Multi-Query RAG для повышения качества извлечения
Средний
от 1 дня до 3 дней
Часто задаваемые вопросы

Направления AI-разработки

Этапы разработки AI-решения

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1284
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1196
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    901
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1119
  • image_logo-advance_0.webp
    Разработка логотипа компании B2B Advance
    586
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    853

Реализация Multi-Query RAG для повышения качества извлечения

Multi-Query RAG — техника улучшения retrieval, при которой исходный запрос автоматически перефразируется несколькими способами, каждый вариант запускается в поиске, а результаты объединяются. Это снижает зависимость качества ответа от конкретной формулировки запроса и повышает полноту извлечения.

Проблема, которую решает Multi-Query

Векторные embedding-модели чувствительны к формулировке запроса. Один и тот же вопрос, заданный по-разному, может давать разные top-K результаты:

  • «Как оформить отпуск?» → находит статьи про заявления
  • «Процедура получения ежегодного отпуска» → находит раздел регламента
  • «Правила предоставления отпускных дней» → находит политику HR

Multi-Query объединяет все три и получает более полный контекст.

Реализация с LangChain

from langchain.retrievers.multi_query import MultiQueryRetriever
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.vectorstores import Qdrant

llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.3)
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Qdrant.from_existing_collection(
    embeddings=embeddings,
    collection_name="knowledge_base",
    url="http://localhost:6333",
)

retriever = MultiQueryRetriever.from_llm(
    retriever=vectorstore.as_retriever(search_kwargs={"k": 5}),
    llm=llm,
    include_original=True,  # Включает оригинальный запрос
)

# Использование
docs = retriever.invoke("каков порядок согласования крупной сделки")
# Внутри LangChain генерирует 3 перефразирования + оригинал,
# ищет по каждому и дедуплицирует результаты

Кастомный Multi-Query с контролем промпта

Стандартный промпт LangChain можно заменить специализированным:

from langchain.prompts import PromptTemplate
from langchain_core.output_parsers import BaseOutputParser

class LineListOutputParser(BaseOutputParser):
    """Парсит список вопросов из ответа LLM"""
    def parse(self, text: str) -> list[str]:
        lines = text.strip().split("\n")
        return [line.strip().lstrip("123456789.-) ") for line in lines if line.strip()]

MULTI_QUERY_PROMPT = PromptTemplate(
    input_variables=["question"],
    template="""Ты — AI-ассистент по поиску документов. Твоя задача — сгенерировать
5 различных вариантов следующего вопроса для улучшения поиска в векторной базе.

Правила:
- Используй синонимы и альтернативные формулировки
- Один вариант — более конкретный, один — более общий
- Сохраняй смысл оригинального вопроса
- Каждый вопрос с новой строки, без нумерации

Оригинальный вопрос: {question}

Варианты:"""
)

custom_retriever = MultiQueryRetriever(
    retriever=vectorstore.as_retriever(search_kwargs={"k": 4}),
    llm_chain=MULTI_QUERY_PROMPT | llm | LineListOutputParser(),
    include_original=True,
)

Parallel Multi-Query с дедупликацией

Для уменьшения latency запускаем поиск по всем вариантам параллельно:

import asyncio
from openai import AsyncOpenAI

async def multi_query_search(
    original_query: str,
    vectorstore,
    n_variants: int = 4,
    top_k_per_query: int = 5,
) -> list[str]:
    """Параллельный multi-query retrieval"""

    async_client = AsyncOpenAI()

    # Генерируем варианты запроса
    response = await async_client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{
            "role": "user",
            "content": f"Сгенерируй {n_variants} перефразирования вопроса:\n{original_query}\nОдин вопрос на строку."
        }],
        temperature=0.5,
    )
    variants = response.choices[0].message.content.strip().split("\n")
    all_queries = [original_query] + variants[:n_variants]

    # Параллельный поиск
    search_tasks = [
        asyncio.to_thread(vectorstore.similarity_search, q, k=top_k_per_query)
        for q in all_queries
    ]
    results_per_query = await asyncio.gather(*search_tasks)

    # Дедупликация по content
    seen_texts = set()
    unique_docs = []
    for docs in results_per_query:
        for doc in docs:
            text_hash = hash(doc.page_content[:100])
            if text_hash not in seen_texts:
                seen_texts.add(text_hash)
                unique_docs.append(doc)

    return unique_docs

Практический кейс: влияние на recall

Датасет: корпоративная база знаний юридической компании (3200 документов).

Тестовый набор: 200 запросов, для каждого размечены все релевантные документы.

Конфигурация Recall@10 Precision@5 Latency (avg)
Single query, k=5 0.61 0.71 280мс
Single query, k=15 0.72 0.58 310мс
Multi-query (4 варианта), k=5 0.84 0.69 680мс
Multi-query + Reranker 0.84 0.81 920мс

Multi-query поднимает recall с 0.61 до 0.84 (+38%) при умеренном росте latency (×2.4). После reranker precision также восстанавливается до 0.81.

Когда Multi-Query не нужен

  • Очень высокий latency requirement (<200мс)
  • Запросы уже хорошо структурированы пользователями
  • Датасет небольшой (<5000 документов) — single query уже даёт высокий recall

Сроки

  • Реализация Multi-Query Retriever: 2–3 дня
  • Подбор промпта и числа вариантов: 2–3 дня
  • Тестирование на датасете: 2–3 дня
  • Итого: 1 неделя