Реалізація Self-Query RAG з метаданими

Проектуємо та впроваджуємо системи штучного інтелекту: від прототипу до production-ready рішення. Наша команда поєднує експертизу в машинному навчанні, дата-інжинірингу та MLOps, щоб AI працював не в лабораторії, а в реальному бізнесі.
Показано 1 з 1Усі 1566 послуг
Реалізація Self-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

Реалізація Self-Query RAG з метаданими

Self-Query RAG — техніка, при якій LLM аналізує запит користувача та автоматично конструює структурований фільтр за метаданими в доповнення до векторного пошуку. Замість пошуку тільки за семантикою, система точково фільтрує документи за датою, типом, автором, відділом та іншими атрибутами.

Проблема без Self-Query

Без Self-Query запит "політики безпеки, видані в 2024 році" ищет все документи за семантикою "безопасность", не фільтруючи за роком. Користувач отримує змішані результати різних періодів. З Self-Query: LLM видобуває фільтр date >= 2024-01-01 AND doc_type = "security_policy" та застосовує його разом з векторним пошуком.

Реалізація через LangChain SelfQueryRetriever

from langchain.retrievers.self_query.base import SelfQueryRetriever
from langchain.chains.query_constructor.base import AttributeInfo
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.vectorstores import Qdrant

# Опис метаданих для LLM
metadata_field_info = [
    AttributeInfo(
        name="doc_type",
        description="Тип документу: contract, regulation, policy, faq, procedure",
        type="string",
    ),
    AttributeInfo(
        name="department",
        description="Відділ або підрозділ: hr, legal, finance, it, security",
        type="string",
    ),
    AttributeInfo(
        name="year",
        description="Рік публікації документу",
        type="integer",
    ),
    AttributeInfo(
        name="status",
        description="Статус документу: active, archived, draft",
        type="string",
    ),
    AttributeInfo(
        name="author",
        description="Автор або відповідальна особа за документ",
        type="string",
    ),
]

document_content_description = "Корпоративна документація компанії: регламенти, політики, договори, процедури"

llm = ChatOpenAI(model="gpt-4o", temperature=0)
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")

retriever = SelfQueryRetriever.from_llm(
    llm=llm,
    vectorstore=vectorstore,
    document_contents=document_content_description,
    metadata_field_info=metadata_field_info,
    enable_limit=True,  # Дозволяє LLM вказувати limit у запиті
    verbose=True,
)

Приклади Self-Query

# Приклад 1: Фільтр за роком та типом
result = retriever.invoke(
    "Які політики безпеки діяли в 2023 році?"
)
# LLM генерує фільтр: {"doc_type": "policy", "department": "security", "year": 2023, "status": "active"}
# Потім виконує векторний пошук з цим фільтром

# Приклад 2: Фільтр за відділом
result = retriever.invoke(
    "Покажи регламенти HR відділу"
)
# Фільтр: {"doc_type": "regulation", "department": "hr"}

# Приклад 3: Без фільтра (чистий векторний пошук)
result = retriever.invoke(
    "Як підготуватися до аудиту?"
)
# LLM не видобуває структурованих фільтрів — чистий semantic search

Кастомна реалізація Self-Query без LangChain

from pydantic import BaseModel, Field
from typing import Optional
from openai import OpenAI
import json

class SearchFilter(BaseModel):
    semantic_query: str = Field(description="Чисто семантична частина для векторного пошуку")
    doc_type: Optional[str] = Field(default=None, description="Тип документу")
    department: Optional[str] = Field(default=None, description="Відділ")
    year_from: Optional[int] = Field(default=None, description="Рік від (включно)")
    year_to: Optional[int] = Field(default=None, description="Рік до (включно)")
    status: Optional[str] = Field(default=None, description="Статус: active/archived")

def parse_query_to_filter(user_query: str, client: OpenAI) -> SearchFilter:
    response = client.beta.chat.completions.parse(
        model="gpt-4o-mini",
        messages=[{
            "role": "system",
            "content": "Видобути структуровані фільтри з запиту користувача для пошуку документів."
        }, {
            "role": "user",
            "content": user_query
        }],
        response_format=SearchFilter,
        temperature=0,
    )
    return response.choices[0].message.parsed

def self_query_search(user_query: str, vectorstore, top_k: int = 5) -> list:
    filter_obj = parse_query_to_filter(user_query, openai_client)

    # Будуємо фільтр для Qdrant
    qdrant_filter = build_qdrant_filter(filter_obj)

    return vectorstore.similarity_search(
        filter_obj.semantic_query,
        k=top_k,
        filter=qdrant_filter,
    )

Практичний кейс: корпоративна база знаній з метаданими

Задача: пошуковий асистент для 15 000 внутрішніх документів з метаданими (тип, відділ, рік, статус, автор).

До Self-Query: 42% запитів повертали архівні документи замість актуальних.

Після Self-Query:

  • Архівні документи в результатах для "актуальних" запитів: 42% → 3%
  • Precision@5: 0.68 → 0.89
  • Задоволеність користувачів: +31%

Failure cases: LLM іноді неправильно інтерпретує параметри фільтра при неоднозначних запитах. Рішення — додати confidence threshold та fallback на pure semantic search при низькій впевненості.

Графіки

  • Розмітка метаданих документів: 1–3 тижні (залежить від наявності даних)
  • Реалізація Self-Query Retriever: 3–5 днів
  • Тестування та підбір промпта: 3–5 днів
  • Всього: 2–5 тижнів