Advanced Filtering
Use filter expressions (EQ, AND, OR, NOT) for complex logical filtering of knowledge base searches.
When basic dictionary filters aren't enough, filter expressions give you logical control over knowledge searches. Use them to combine multiple conditions with AND/OR logic, exclude content with NOT, or perform comparisons like "greater than" and "less than".
For basic filtering with dictionary format, see Search & Retrieval.
Filter Expression Operators
Agno provides a rich set of filter expressions that can be combined to create sophisticated search criteria:
Comparison Operators
These operators let you match against specific values:
EQ (Equals)
Match content where a metadata field equals a specific value.
from agno.filters import EQ
# Find only HR policy documents
EQ("department", "hr")
# Find content from a specific year
EQ("year", 2024)IN (Contains Any)
Match content where a metadata field contains any of the specified values.
from agno.filters import IN
# Find content from multiple regions
IN("region", ["north_america", "europe", "asia"])
# Find multiple document types
IN("document_type", ["policy", "guideline", "procedure"])GT (Greater Than) & LT (Less Than)
Match content based on numeric comparisons.
from agno.filters import GT, LT
# Find recent documents
GT("year", 2020)
# Find documents with high priority scores
GT("priority_score", 8.0)
# Find documents before 2025
LT("year", 2025)Logical Operators
Combine multiple conditions using logical operators:
AND
All conditions must be true.
from agno.filters import AND, EQ
# Find sales documents from North America in 2024
AND(
EQ("data_type", "sales"),
EQ("region", "north_america"),
EQ("year", 2024)
)OR
At least one condition must be true.
from agno.filters import OR, EQ
# Find either engineering or product documents
OR(
EQ("department", "engineering"),
EQ("department", "product")
)NOT
Exclude content that matches the condition.
from agno.filters import NOT, EQ
# Find everything except draft documents
NOT(EQ("status", "draft"))Using Filters with Agents
Here's how to apply filters when running agents with knowledge.
You need a contents database with your Knowledge base to use agentic filtering.
Basic Agent Filtering
Start the database from the PgVector setup, then install the example's dependencies and set your key:
pip install agno openai pgvector "psycopg[binary]" sqlalchemy aiofiles requests
export OPENAI_API_KEY="your-api-key"Create sales_report_q1.csv in the directory where you run the script:
product,revenue
Starter,12000
Business,28000from agno.agent import Agent
from agno.db.postgres import PostgresDb
from agno.filters import EQ, IN, AND
from agno.knowledge.knowledge import Knowledge
from agno.vectordb.pgvector import PgVector
# Setup knowledge with metadata
knowledge = Knowledge(
contents_db=PostgresDb(
db_url="postgresql+psycopg://ai:ai@localhost:5532/ai",
knowledge_table="knowledge_contents",
),
vector_db=PgVector(
table_name="filtered_knowledge",
db_url="postgresql+psycopg://ai:ai@localhost:5532/ai"
)
)
# Add content with rich metadata
knowledge.insert(
path="sales_report_q1.csv",
metadata={
"data_type": "sales",
"quarter": "Q1",
"year": 2024,
"region": "north_america",
"currency": "USD"
}
)
# Create agent with knowledge
sales_agent = Agent(
knowledge=knowledge,
search_knowledge=True,
instructions="Always search knowledge before answering questions"
)
# Reused by the later filtering fragments
agent = sales_agent
knowledge_base = knowledge
# Use filters in agent responses - NOTE: filters must be in a list!
sales_agent.print_response(
"What were our Q1 sales results?",
knowledge_filters=[ # ← Must be a list!
AND(EQ("data_type", "sales"), EQ("quarter", "Q1"))
]
)Complex Filter Examples
The following fragments reuse agent and knowledge_base above. Add your own feedback, candidate, or project documents with the metadata fields each fragment filters before using it with those data types.
from agno.filters import AND, OR, NOT, EQ, IN, GT
# Find recent sales data from specific regions, but exclude drafts
complex_filter = AND(
EQ("data_type", "sales"),
IN("region", ["north_america", "europe"]),
GT("year", 2022),
NOT(EQ("status", "draft"))
)
# Search for either customer feedback or survey data from the last two years
feedback_filter = AND(
OR(
EQ("data_type", "feedback"),
EQ("data_type", "survey")
),
GT("year", 2022)
)
agent.print_response(
"What do our customers think about our new features?",
knowledge_filters=[feedback_filter] # ← List wrapper required
)Using Filters with Teams
Teams can also leverage filtered knowledge searches:
from agno.team.team import Team
from agno.agent import Agent
from agno.filters import IN, AND, NOT, EQ
# Setup team members
research_agent = Agent(
name="Research Agent",
role="Analyze candidate information",
knowledge=knowledge_base
)
# Create team with knowledge
hiring_team = Team(
name="Hiring Team",
members=[research_agent],
knowledge=knowledge_base,
instructions="Analyze candidate profiles thoroughly"
)
# Filter to specific candidates
hiring_team.print_response(
"Compare the experience of our top candidates",
knowledge_filters=[ # ← List wrapper required
AND(
EQ("document_type", "cv"),
IN("user_id", ["jordan_mitchell", "taylor_brooks"]),
NOT(EQ("status", "rejected"))
)
]
)Advanced Filtering Patterns
User-Specific Content
Filter content based on user access or preferences:
from agno.filters import OR, EQ
def get_user_filter(user_id: str, user_department: str):
"""Create filters based on user context."""
return OR(
EQ("visibility", "public"),
EQ("owner", user_id),
EQ("department", user_department)
)
# Apply user-specific filtering
user_filter = get_user_filter("john_doe", "engineering")
agent.print_response(
"Show me the latest project updates",
knowledge_filters=[user_filter] # ← List wrapper required
)The user-context example selects metadata. Keep authorization and native content ownership separate from model-selected or caller-supplied filters; see User Isolation.
Time-Based Filtering
Filter by recency or date ranges:
from datetime import datetime
from agno.filters import AND, NOT, EQ, GT
current_year = datetime.now().year
# Only search recent content
recent_filter = GT("year", current_year - 2)
# Exclude archived content
active_filter = NOT(EQ("status", "archived"))
# Combine for active, recent content
current_content = AND(recent_filter, active_filter)
# Use in agent - wrap in list
agent.print_response(
"What's new?",
knowledge_filters=[current_content]
)Progressive Filtering
Start broad, then narrow down based on results:
The ingested documents in this example have a status metadata field. The function accepts a list of filter expressions and preserves all of them when narrowing to active documents.
from agno.filters import EQ
async def progressive_search(agent, query, base_filters=None):
"""Search, then narrow to active documents if more than eight are returned."""
filters = list(base_filters or [])
broad_results = await agent.aget_relevant_docs_from_knowledge(
query=query,
filters=filters,
num_documents=10,
) or []
if len(broad_results) > 8:
return await agent.aget_relevant_docs_from_knowledge(
query=query,
filters=[*filters, EQ("status", "active")],
num_documents=5,
) or []
return broad_resultsBest Practices for Filter Expressions
Filter Design
- Start Simple: Begin with basic filters and add complexity as needed
- Test Combinations: Verify that your logical combinations work as expected
- Document Your Schema: Keep track of available metadata fields and their possible values
- Performance Considerations: Some filter combinations may be slower than others
Troubleshooting
Filter Not Working
Check that the keys you're filtering on actually exist in your knowledge base:
# Add content with explicit metadata
knowledge.insert(
path="doc.pdf",
metadata={"status": "published", "category": "tech"}
)
# Now filter will work
filter_expr = EQ("status", "published")Print the filter to verify it's constructed correctly:
from agno.filters import EQ, GT, AND
filter_expr = AND(EQ("status", "published"), GT("views", 100))
print(filter_expr.to_dict())Complex Filters Failing
Test each condition individually:
# Test each part separately
filter1 = EQ("status", "published") # Test
filter2 = GT("date", "2024-01-01") # Test
filter3 = IN("region", ["US", "EU"]) # Test
# Then combine
combined = AND(filter1, filter2, filter3)Check that nested logic is correctly structured:
import json
try:
filter_dict = filter_expr.to_dict()
json_str = json.dumps(filter_dict)
json.loads(json_str) # Verify it parses
print("Valid filter structure")
except (TypeError, ValueError) as e:
print(f"Invalid filter: {e}")Make sure nested logic is clear and well-structured:
# Clear nested structure
filter_expr = OR(
AND(EQ("a", 1), EQ("b", 2)),
EQ("c", 3)
)
# Break down complex filters for readability
condition1 = AND(EQ("a", 1), EQ("b", 2))
condition2 = EQ("c", 3)
filter_expr = OR(condition1, condition2)Vector Database Support
Filter support depends on the vector database:
- PgVector supports the filter expression DSL, including logical and comparison expressions.
- Valkey supports
EQandINexpressions. Unsupported expressions make its search method log an error and return no results. - Chroma warns and ignores a
FilterExprlist. Use its supported dictionary filters. - Cassandra and ClickHouse metadata search filters are not applied by the current adapters.
Verify the selected adapter before relying on a filter. Unsupported filters may be ignored or rejected; behavior is not uniform.
# Dictionary form for a backend that supports these metadata filters
knowledge_filters = {"department": "hr", "year": 2024}
# Expression list for PgVector
knowledge_filters = [AND(EQ("department", "hr"), EQ("year", 2024))]A dictionary is passed directly. A list must contain FilterExpr objects, not dictionaries.
Agentic Filtering Compatibility
Advanced filter expressions (FilterExpr) are not compatible with agentic filtering, where agents dynamically construct filters based on conversation context.
For agentic filtering, use dictionary format:
# Works with agentic filtering (agent decides filters dynamically)
knowledge_filters = {"department": "hr", "document_type": "policy"}
# Does not work with agentic filtering (static, predefined logic)
knowledge_filters = [AND(EQ("department", "hr"), EQ("document_type", "policy"))]When to use each approach:
| Approach | Use Case | Example |
|---|---|---|
| Dictionary format | Agent dynamically chooses filters based on conversation | User mentions "HR policies" → agent adds {"department": "hr"} |
| Filter expressions | You need complex, predetermined logic with full control | Always exclude drafts AND filter by multiple regions with OR logic |
Using Filters Through the API
All the filter expressions shown in this guide can also be used through the Agent OS API. FilterExpressions serialize to JSON and are automatically reconstructed server-side, enabling the same filtering over REST endpoints.
import requests
import json
from agno.filters import EQ, GT, AND
# Create filter expression
filter_expr = AND(EQ("status", "published"), GT("views", 1000))
# Serialize to JSON
filter_json = json.dumps(filter_expr.to_dict())
# Send through API
response = requests.post(
"http://localhost:7777/agents/my-agent/runs",
data={
"message": "Find popular published articles",
"stream": "false",
"knowledge_filters": filter_json,
}
)FilterExpressions use a dictionary format with an "op" key (e.g., {"op": "EQ", "key": "status", "value": "published"}) which tells the API to deserialize them as FilterExpr objects. Regular dict filters without the "op" key continue to work for backward compatibility.
For detailed examples, API-specific patterns, and troubleshooting, see the API Filtering Guide.
Next Steps
API Filtering Guide
Use filter expressions through the Agent OS API
Search & Retrieval
Search types, direct and agentic retrieval, and dictionary filters
Content Database
Understand how content and metadata are stored and managed
Knowledge Bases
Give agents access to documents, databases, and domain expertise
Performance Tips
Optimize your filtered searches for speed and accuracy