LlamaIndexllama-index-core 0.14 · Python 3.10+
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
20 small wins to finish your pathNext lesson

Metadata filters: who may see which documents

Staff documents must not reach customers through the assistant. A metadata filter limits retrieval to chunks the asker may see.

A staff-only document joins the help centre, in its own folder:

Examplestaff/refund-approvals.md
# Refund approvals (staff only)

Refunds over 200 need a team lead's approval before they are paid. Refunds for customers flagged for abuse must be sent to the fraud team.
Example
from llama_index.core import Settings
from llama_index.embeddings.huggingface import HuggingFaceEmbedding

Settings.embed_model = HuggingFaceEmbedding(model_name="sentence-transformers/all-MiniLM-L6-v2")
import os

from llama_index.core import SimpleDirectoryReader, VectorStoreIndex


def with_audience(path):
    audience = "staff" if "/staff/" in path else "customer"
    return {"file_name": os.path.basename(path), "audience": audience}


documents = SimpleDirectoryReader(".", recursive=True, required_exts=[".md"], file_metadata=with_audience).load_data()
for document in documents:
    document.excluded_embed_metadata_keys = ["audience"]
    document.excluded_llm_metadata_keys = ["audience"]
index = VectorStoreIndex.from_documents(documents)

with_audience marks each file staff or customer by its folder. recursive=True reads both folders. The audience key is excluded from embedding and from the prompt: it controls access, it is not content.

Example
retriever = index.as_retriever(similarity_top_k=2)
print("no filter: ", [n.metadata["file_name"] for n in retriever.retrieve("Who approves a large refund?")])

from llama_index.core.vector_stores import ExactMatchFilter, MetadataFilters

customers_only = MetadataFilters(filters=[ExactMatchFilter(key="audience", value="customer")])
retriever = index.as_retriever(similarity_top_k=2, filters=customers_only)
print("customer:  ", [n.metadata["file_name"] for n in retriever.retrieve("Who approves a large refund?")])

Unfiltered, a customer asking about large refunds gets the staff approval rules first, internal fraud process included. With filters, the staff document is not a candidate at all, so it cannot be retrieved however similar it is.

Filter by who is asking, on the server. The filter must come from the logged-in user's role, set by your application, never from something the user can type. The SSO, OAuth and RBAC topic covers where that role comes from.

Filters depend on the vector store
The in-memory store used here supports exact-match filters. Production vector stores such as Qdrant, pgvector or Chroma each support their own set of filter operators through the same MetadataFilters interface; check yours before relying on one.
Try it yourself
  • Make a filter for staff and ask the same question.
  • Add a third audience, partner, and a document for it.
  • Use the customer filter in a query engine with ExtractiveLLM.

You understood something today that you didn't yesterday.