Enhancing RAG Accuracy with Automated Metadata Filtering

We design and deploy artificial intelligence systems: from prototype to production-ready solutions. Our team combines expertise in machine learning, data engineering and MLOps to make AI work not in the lab, but in real business.
Showing 1 of 1All 1566 services
Enhancing RAG Accuracy with Automated Metadata Filtering
Medium
from 1 day to 3 days
Frequently Asked Questions

AI Development Areas

AI Solution Development Stages

Latest works

  • image_website-b2b-advance_0.webp
    B2B ADVANCE company website development
    1317
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1226
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    925
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1156
  • image_logo-advance_0.webp
    B2B Advance company logo design
    620
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    894

Imagine you're searching for "last year's security policies" in a corporate database of 15,000 documents. A regular RAG returns all documents semantically related to "security" — including archive regulations from five years ago. The user drowns in irrelevant results. Self-Query RAG solves this: the LLM analyzes the query and automatically builds a filter: doc_type=policy AND year>=current_year-1 AND status=active, applying it together with vector search. Precision@5 increases from 0.68 to 0.89, the share of archived documents drops from 42% to 3%. This approach is known as metadata search using LLMs, a key aspect of AI document search.

We implement Self-Query RAG turnkey — from metadata labeling to deploying the assistant. Our engineers adapt the solution to any stack: LangChain, Qdrant, Pinecone, Weaviate. Get a consultation — we'll discuss details for your scenario. Total project cost: $10,000–$25,000; estimated annual savings: $50,000–$100,000.

How Self-Query Solves the Filtering Problem

Without Self-Query, a query like "HR department regulations" searches all documents for the words "regulation" and "HR", without filtering by department. You get regulations from IT, Legal, and even marketing instructions. Self-Query forces the LLM to extract filter department=hr AND doc_type=regulation and discard everything unnecessary at the storage level. This saves search time and reduces query processing costs due to accuracy. Companies save up to 40% time on document search and reduce knowledge base maintenance costs by 25%. RAG with filtering is essential for corporate databases; LLM filter extraction is the key innovation.

Comparison with Regular RAG

Metric Regular RAG Self-Query RAG
Precision@5 0.68 0.89
Share of archived documents 42% 3%
Average search time 2.1s 2.3s (due to LLM step)
User satisfaction 72% 94%

Self-Query RAG is 1.3x better than regular RAG in Precision@5 and reduces irrelevant results by 92% compared to standard RAG.

Why Self-Query is a Must-Have for Databases with Metadata

Corporate knowledge bases contain documents of different types, departments, and statuses. Without filtering, users get a jumble. Self-Query automatically classifies the query and applies relevant metadata. This is especially important for legal, HR, and financial documents where accuracy is critical.

Metadata Examples for Self-Query

Field Type Description
doc_type string policy, contract, faq
department string hr, legal, it
year integer Year of publication
status string active, archived
author string Author name

Implementation via 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

# Metadata description for LLM
metadata_field_info = [
    AttributeInfo(
        name="doc_type",
        description="Document type: contract, regulation, policy, faq, procedure",
        type="string",
    ),
    AttributeInfo(
        name="department",
        description="Department or division: hr, legal, finance, it, security",
        type="string",
    ),
    AttributeInfo(
        name="year",
        description="Year of publication",
        type="integer",
    ),
    AttributeInfo(
        name="status",
        description="Document status: active, archived, draft",
        type="string",
    ),
    AttributeInfo(
        name="author",
        description="Author or responsible person",
        type="string",
    ),
]

document_content_description = "Corporate documentation: regulations, policies, contracts, procedures"

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,
    verbose=True,
)

LangChain's SelfQueryRetriever simplifies the implementation of self-query rag.

Self-Query in Action

# Example 1: Filter by year and type
result = retriever.invoke(
    "What security policies were active last year?"
)
# LLM generates filter: {"doc_type": "policy", "department": "security", "year": last_year, "status": "active"}

# Example 2: Filter by department
result = retriever.invoke(
    "Show HR department regulations"
)
# Filter: {"doc_type": "regulation", "department": "hr"}

# Example 3: No filter (pure vector search)
result = retriever.invoke(
    "How to prepare for an audit?"
)
# LLM doesn't extract structured filters — pure semantic search

Custom Self-Query Implementation Without LangChain

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

class SearchFilter(BaseModel):
    semantic_query: str = Field(description="Pure semantic part of the query for vector search")
    doc_type: Optional[str] = Field(default=None, description="Document type")
    department: Optional[str] = Field(default=None, description="Department")
    year_from: Optional[int] = Field(default=None, description="Year from (inclusive)")
    year_to: Optional[int] = Field(default=None, description="Year to (inclusive)")
    status: Optional[str] = Field(default=None, description="Status: 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": "Extract structured filters for document search from the user query."
        }, {
            "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_filter = build_qdrant_filter(filter_obj)
    return vectorstore.similarity_search(
        filter_obj.semantic_query,
        k=top_k,
        filter=qdrant_filter,
    )

Qdrant filters enable efficient metadata filtering, especially when combined with prompt fine-tuning for LLM filter extraction.

Case Study: Corporate Knowledge Base

Challenge: a search assistant for 15,000 internal documents with metadata (type, department, year, status, author).

Before Self-Query: 42% of queries returned archived documents instead of current ones.

After Self-Query (our client — a company of 500+ employees):

  • Archived documents in results for "current" queries: 42% → 3%
  • Precision@5: 0.68 → 0.89
  • User satisfaction: +31%

Failure cases: LLM may misinterpret filter parameters on ambiguous queries. Solution: add a confidence threshold and fallback to pure semantic search when confidence is low. We fine-tune the prompt if needed to improve filter extraction quality. Prompt fine-tuning is crucial for accurate metadata search.

When is Self-Query not beneficial?If metadata is sparse or indistinguishable (e.g., all documents of one type), Self-Query provides no gain. In such cases, regular semantic search suffices. We always perform a preliminary data audit.

How to Implement Self-Query: Step by Step

  1. Audit documents and metadata — define fields for filtering.
  2. Label or automatically extract metadata (NLP classification) — this is a key part of automatic document classification.
  3. Choose a vector database and configure indexing.
  4. Develop the LLM prompt and integrate the Self-Query Retriever.
  5. A/B testing and threshold tuning.

What's Included in the Work

  • Documentation: metadata schema, prompt description, extension guide.
  • Access control: user permission differentiation via document statuses.
  • Training: 2 hours for system administrators.
  • Support: 1 month post-launch.

Timeline and Cost

  • Metadata labeling: 1–3 weeks (depends on data availability).
  • Self-Query Retriever implementation: 3–5 days.
  • Testing and prompt tuning: 3–5 days.
  • Total: 2–5 weeks. For a typical project, the cost ranges from $10,000 to $25,000, with annual savings of $50,000–$100,000. Implementation costs start at $10,000, with annual savings up to $100,000.

We have been working with RAG for over 5 years and completed 30+ projects. We guarantee transparent architecture and documentation. Get a consultation — we'll discuss the details of your task. Request a demo — we'll show it on your data.

Source: Retrieval-augmented generation (Wikipedia)