Реалізація Multi-Index RAG: об'єднання кількох джерел

Уявіть: корпоративна пошукова система має працювати по п'яти різних сховищах — Confluence (5200 сторінок), SharePoint (3800 документів), JIRA, GitHub та внутрішня CRM-документація. Монолітний векторний індекс з єдиним розміром чанків неминуче втрачає точність: для коротких FAQ потрібні маленькі чанк

Напрямки AI-розробки

Часті запитання

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1440
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1301
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    997
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1264
  • image_logo-advance_0.webp
    Розробка логотипу компанії B2B Advance
    712
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    1002

Уявіть: корпоративна пошукова система має працювати по п'яти різних сховищах — Confluence (5200 сторінок), SharePoint (3800 документів), JIRA, GitHub та внутрішня CRM-документація. Монолітний векторний індекс з єдиним розміром чанків неминуче втрачає точність: для коротких FAQ потрібні маленькі чанки, для довгих регламентів — великі. У нашій практиці ми часто стикаємося з такими запитами, і рішення — Multi-Index RAG (визначення див. у Wikipedia). Ця архітектура дозволяє об'єднати кілька окремих індексів (векторних сховищ) в єдиний потік пошуку, зберігаючи індивідуальні налаштування для кожного типу даних та домену. Multi-Index RAG — це реалізація корпоративного пошуку AI, яка поєднує RAG з кількох джерел та забезпечує ізоляцію даних.

Коли потрібен Multi-Index RAG?

Різні типи даних: структуровані FAQ (короткі відповіді) та довгі регламенти потребують різних розмірів чанків та стратегій retrieval. Різні домени: юридична документація, технічна документація, продуктові описи — семантичні простори слабо перетинаються, окремі індекси дають більш точний retrieval. Різні джерела: Confluence, SharePoint, Notion, GitHub — кожне потребує свого парсера та має специфічні метадані. Ізоляція даних за безпекою: дані різних відділів зберігаються в різних індексах з контролем доступу.

Чому Multi-Index RAG ефективніший за монолітний індекс?

Основна ідея — створити окремі індекси для кожного типу/джерела, налаштувати їх параметри (розмір чанка, модель ембендінгів) індивідуально, а потім використовувати LLM-роутер для вибору релевантних індексів під конкретний запит. Паралельний асинхронний пошук по вибраних індексах та подальший reranking об'єднаних результатів дають високу точність. В одному з проектів впровадження Multi-Index RAG підвищило Context Recall з 0.71 до 0.88 — приріст на 24%. Multi-Index RAG на 24% точніший за монолітний індекс. Економія часу співробітників на пошук інформації склала до 2 годин на тиждень, що еквівалентно €500 на місяць для невеликої команди. Вартість впровадження від €15,000.

Тип даних Оптимальний розмір чанка Стратегія чанкінгу
FAQ 200-300 токенів За питаннями
Регламенти 1000-1500 токенів За розділами
Код 500-800 токенів За функціями

Архітектура Multi-Index RAG

Код архітектури Multi-Index RAG
from typing import Optional from langchain_community.vectorstores import Qdrant from langchain_openai import OpenAIEmbeddings, ChatOpenAI from langchain.schema import Document import asyncio class MultiIndexRAG: def __init__(self, embeddings, llm): self.embeddings = embeddings self.llm = llm self.indexes: dict[str, Qdrant] = {} self.router_llm = ChatOpenAI(model="gpt-4o-mini", temperature=0) def add_index(self, name: str, collection: str, description: str): """Реєструємо індекс з описом для роутера""" self.indexes[name] = { "retriever": Qdrant.from_existing_collection( embeddings=self.embeddings, collection_name=collection, url="http://localhost:6333", ).as_retriever(search_kwargs={"k": 5}), "description": description, } def route_query(self, query: str) -> list[str]: """LLM-роутер визначає релевантні індекси""" index_descriptions = "\n".join([ f"- {name}: {info['description']}" for name, info in self.indexes.items() ]) response = self.router_llm.invoke(f""" Визнач, в яких із наступних індексів потрібно шукати для відповіді на запит. Поверни JSON-список імен індексів. Доступні індекси: {index_descriptions} Запит: {query} Відповідь (JSON список):""") import json try: return json.loads(response.content) except: return list(self.indexes.keys()) # Fallback: всі індекси async def _search_index(self, index_name: str, query: str) -> tuple[str, list]: """Асинхронний пошук в одному індексі""" retriever = self.indexes[index_name]["retriever"] docs = await asyncio.to_thread(retriever.invoke, query) return index_name, docs async def retrieve(self, query: str) -> dict[str, list]: """Паралельний пошук по релевантних індексах""" relevant_indexes = self.route_query(query) tasks = [ self._search_index(idx, query) for idx in relevant_indexes if idx in self.indexes ] results = await asyncio.gather(*tasks) return dict(results) def build_context(self, search_results: dict[str, list]) -> str: """Збираємо контекст з кількох індексів""" context_parts = [] for index_name, docs in search_results.items(): if docs: context_parts.append(f"## Джерело: {index_name}\n") for doc in docs: context_parts.append(f"- {doc.page_content}\n") return "\n".join(context_parts) 

Налаштування індексів для корпоративної бази знань

rag = MultiIndexRAG( embeddings=OpenAIEmbeddings(model="text-embedding-3-small"), llm=ChatOpenAI(model="gpt-4o", temperature=0), ) rag.add_index( name="legal", collection="legal_contracts", description="Договори, угоди, юридичні висновки", ) rag.add_index( name="hr", collection="hr_policies", description="Політики HR: відпустки, відрядження, найм, звільнення", ) rag.add_index( name="it", collection="it_procedures", description="IT-процедури: доступи, обладнання, інформаційна безпека", ) rag.add_index( name="finance", collection="finance_regulations", description="Фінансові регламенти: бюджет, закупівлі, авансові звіти", ) rag.add_index( name="faq", collection="general_faq", description="Загальні часто задавані питання співробітників", ) 

Reranking об'єднаних результатів

Після збору результатів з кількох індексів важливо виконати ранжування результатів та переранжувати:

from flashrank import Ranker, RerankRequest ranker = Ranker(model_name="ms-marco-MiniLM-L-12-v2") def rerank_multi_index_results( query: str, search_results: dict[str, list[Document]], top_n: int = 6, ) -> list[Document]: """Об'єднує та переранжує результати з різних індексів""" # Збираємо всі документи all_docs = [] for docs in search_results.values(): all_docs.extend(docs) if not all_docs: return [] # Reranking passages = [{"id": i, "text": doc.page_content} for i, doc in enumerate(all_docs)] rerank_req = RerankRequest(query=query, passages=passages) ranked = ranker.rerank(rerank_req) return [all_docs[r["id"]] for r in ranked[:top_n]] 

Практичний кейс: корпоративний асистент з 5 джерел

В одному з проектів ми впровадили Multi-Index RAG для великої компанії з п'ятьма джерелами: Confluence (5200 сторінок), SharePoint (3800 документів), JIRA (експорт задач), GitHub (wiki, README), внутрішня CRM-документація. Інтеграція Confluence, SharePoint, JIRA, GitHub та CRM — стандартний сценарій.

Проблема монолітного індексу: різні типи контенту мають різні оптимальні розміри чанків. README з GitHub оптимально індексувати по-функціонально (блоки коду + опис), Confluence-сторінки — за розділами, CRM-документацію — за відповідями.

Конфігурація Multi-Index:

  • 5 окремих колекцій в Qdrant
  • LLM-роутер на GPT-4o-mini (~15 мс overhead)
  • Паралельний пошук (async) скорочує latency з 5×T до 1.2×T
Метрика Монолітний індекс Multi-Index
Context Recall 0.71 0.88
Precision@5 0.74 0.86
Latency P95 1.2 с 1.5 с
Routing accuracy 91%

Failure cases: 9% запитів потрапляють у неправильний набір індексів — переважно крос-доменні питання. Рішення: при низькому router confidence запускати пошук по всіх індексах з threshold відсікання за score.

Федеративний пошук з access control

def retrieve_with_permissions( query: str, user_id: str, permission_service, ) -> dict[str, list]: """Пошук тільки по дозволених для користувача індексах""" allowed_indexes = permission_service.get_allowed_indexes(user_id) relevant_indexes = [ idx for idx in route_query(query) if idx in allowed_indexes ] return {idx: search(idx, query) for idx in relevant_indexes} 

Що входить в роботу

  • 5+ років досвіду в NLP та AI
  • Аудит джерел даних та контенту
  • Проектування Multi-Index архітектури під ваші сценарії
  • Розробка ingestion пайплайнів для кожного джерела
  • Налаштування LLM-роутера та reranking
  • Інтеграція з існуючою інфраструктурою (Qdrant, Pinecone, pgvector)
  • Документація архітектури та конфігурацій
  • Навчання команди замовника
  • Пост-релізна підтримка протягом 3 місяців

Процес роботи

Ми виконуємо проекти під ключ: від аудиту до деплою.

  1. Аналітика: вивчаємо типи даних, їх обсяги, вимоги до безпеки.
  2. Проектування: вибираємо стэк (векторні БД, моделі ембендінгів, LLM-роутер).
  3. Розробка: пишемо парсери, налаштовуємо чанкінг, індексацію, роутинг.
  4. Тестування: оцінюємо метрики Recall, Precision, latency.
  5. Деплой: розгортаємо на інфраструктурі замовника (on-prem або хмара).

Строки орієнтовно

  • Проектування Multi-Index архітектури: 1 тиждень
  • Розробка ingestion пайплайнів (5 джерел): 3–4 тижні
  • LLM-роутер та інтеграція: 1 тиждень
  • Reranking та оцінка: 1 тиждень
  • Разом: 6–8 тижнів

Напишіть нам для консультації та оцінки вашого проекту. Оцінимо проект безкоштовно. Замовте впровадження Multi-Index RAG — отримайте точний пошук по всіх ваших джерелах. Наш досвід в RAG-архітектурах та 20+ реалізованих проектів гарантують високу якість.