Handoff Filters
Filter and transform context when handing off between agents
Handoff filters control what context passes when one agent hands off to another — reducing tokens, focusing relevance, and stripping sensitive data.
from praisonaiagents import Agent, Handoff, handoff_filters
coordinator = Agent(
name="Coordinator",
instructions="Route tasks to specialists with trimmed context",
handoffs=[
Handoff(
agent=Agent(name="Analyst", instructions="Analyse the handoff payload"),
input_filter=handoff_filters.compress_history,
)
],
)
coordinator.start("Prepare a focused brief for the analyst")The user asks the coordinator; filtered context is passed on handoff.
Overview
Handoff filters let you control what context is passed when one agent hands off to another. This helps:
- Reduce token usage by removing unnecessary messages
- Focus context on relevant information
- Protect privacy by filtering sensitive data
Quick Start
- Single filter
Single Filter from praisonaiagents import Agent, Handoff, handoff_filters analyst = Agent(name="Analyst", instructions="Analyze data") coordinator = Agent( name="Coordinator", handoffs=[ Handoff( agent=analyst, input_filter=handoff_filters.compress_history ) ] ) - Chain filters
Filter Chaining from praisonaiagents import Agent, Handoff, handoff_filters analyst = Agent(name="Analyst", instructions="Analyze data") coordinator = Agent( name="Coordinator", handoffs=[ Handoff( agent=analyst, input_filter=[ handoff_filters.remove_all_tools, handoff_filters.remove_system_messages, handoff_filters.compress_history, ] ) ] )
Available Filters
compress_history
Compresses all messages into a single summary message. Great for reducing token usage.
from praisonaiagents import handoff_filters
# Before: Multiple messages
# [
# {"role": "user", "content": "Hello"},
# {"role": "assistant", "content": "Hi there!"},
# {"role": "user", "content": "How are you?"}
# ]
# After: Single compressed message
# [{"role": "user", "content": "Previous conversation summary:\n[user]: Hello\n[assistant]: Hi there!\n[user]: How are you?"}]remove_all_tools
Removes all tool-related messages (tool calls and tool results).
# Removes:
# - Messages with role="tool"
# - Messages with tool_calls fieldkeep_last_n_messages
Keeps only the last N messages.
# Keep last 5 messages
filter_fn = handoff_filters.keep_last_n_messages(5)remove_system_messages
Removes all system messages from the history.
# Removes all messages where role="system"Filter Chaining
from praisonaiagents import Agent, Handoff, handoff_filters
# Recommended order:
# 1. Remove unwanted content
# 2. Limit message count
# 3. Compress remaining
handoff = Handoff(
agent=target_agent,
input_filter=[
handoff_filters.remove_all_tools, # First: remove tool noise
handoff_filters.remove_system_messages, # Second: remove system prompts
handoff_filters.keep_last_n_messages(10), # Third: limit to recent
handoff_filters.compress_history, # Last: compress into one
]
)How It Works
The user asks the coordinator; on handoff the input filters run in order and the filtered messages are seeded onto the target agent’s chat_history for the duration of the handoff, then restored on exit.
Custom Filters
Create your own filter function:
from praisonaiagents import HandoffInputData
def my_custom_filter(data: HandoffInputData) -> HandoffInputData:
"""Remove messages containing sensitive keywords."""
sensitive_words = ["password", "secret", "api_key"]
filtered = []
for msg in data.messages:
if isinstance(msg, dict):
content = msg.get("content", "").lower()
if not any(word in content for word in sensitive_words):
filtered.append(msg)
data.messages = filtered
return data
# Use with handoff
handoff = Handoff(
agent=target_agent,
input_filter=[
my_custom_filter,
handoff_filters.compress_history,
]
)API Reference
HandoffInputData
The data structure passed to filter functions:
@dataclass
class HandoffInputData:
messages: List[Dict[str, Any]] # Conversation messages
context: Dict[str, Any] # Additional context
source_agent: str # Name of source agent
handoff_depth: int = 0 # Current handoff depth
handoff_chain: List[str] = [] # Chain of agent namesFilter Function Signature
def filter_function(data: HandoffInputData) -> HandoffInputData:
# Modify data.messages as needed
return dataExamples
Token-Efficient Handoff
from praisonaiagents import Agent, Handoff, handoff_filters
# Minimize tokens passed to specialist
specialist = Agent(name="Specialist", instructions="Handle complex tasks")
coordinator = Agent(
name="Coordinator",
handoffs=[
Handoff(
agent=specialist,
input_filter=[
handoff_filters.remove_all_tools,
handoff_filters.keep_last_n_messages(3),
handoff_filters.compress_history,
]
)
]
)Privacy-Aware Handoff
from praisonaiagents import Agent, Handoff, handoff_filters
from praisonaiagents import HandoffInputData
def remove_pii(data: HandoffInputData) -> HandoffInputData:
"""Remove personally identifiable information."""
import re
for msg in data.messages:
if isinstance(msg, dict) and "content" in msg:
# Remove email addresses
msg["content"] = re.sub(
r'\b[\w.-]+@[\w.-]+\.\w+\b',
'[EMAIL REDACTED]',
msg["content"]
)
# Remove phone numbers
msg["content"] = re.sub(
r'\b\d{3}[-.]?\d{3}[-.]?\d{4}\b',
'[PHONE REDACTED]',
msg["content"]
)
return data
external_agent = Agent(name="External", instructions="Process request")
internal_agent = Agent(
name="Internal",
handoffs=[
Handoff(
agent=external_agent,
input_filter=[remove_pii, handoff_filters.compress_history]
)
]
)Focus on Recent Context
from praisonaiagents import Agent, Handoff, handoff_filters
# Only pass last 5 messages, no tools
analyst = Agent(name="Analyst", instructions="Analyze recent conversation")
coordinator = Agent(
name="Coordinator",
handoffs=[
Handoff(
agent=analyst,
input_filter=[
handoff_filters.remove_all_tools,
handoff_filters.keep_last_n_messages(5),
]
)
]
)Best Practices
Compress history before large handoffs
Passing a full conversation to a specialist wastes tokens on context it doesn’t need. Use handoff_filters.compress_history (or a custom trim) so the receiving agent gets a focused brief, not the entire transcript.
Strip tool calls the next agent can't use
Tool-call messages from the source agent are noise to a specialist with different tools. Filter them out (remove_tools) so the receiving model reasons over content, not stale tool invocations it will never repeat.
Redact sensitive data at the boundary
Handoffs are the natural place to drop secrets, PII, or credentials before another agent sees them. Add a redaction filter to the chain so sensitive fields never cross into an agent that doesn’t need them.
Chain filters in order of intent
Filters compose left to right, each receiving the previous filter’s output. Order them so structural cuts (remove tools) run before compression, and redaction runs last, so nothing sensitive slips through a later transform.