Agent Handoffs

Agent handoffs enable seamless task delegation between specialised agents, allowing you to build sophisticated multi-agent systems where each agent focuses on its area of expertise.

Overview

Handoffs in PraisonAI allow agents to transfer control to other agents based on the conversation context. This pattern is particularly useful for:

  • Customer service routing
  • Specialised task delegation
  • Complex workflow orchestration
  • Escalation scenarios

How Handoffs Work

When you specify handoffs for an agent, PraisonAI automatically:

  1. Converts each handoff into a tool that the agent can use
  2. Adds instructions to the agent’s prompt about available handoffs
  3. Passes the full conversation history when transferring control

Basic Usage

Simple Handoff

from praisonaiagents import Agent

# Create specialised agents
billing_agent = Agent(
    name="Billing",
    role="Billing specialist",
    goal="Handle billing inquiries and process payments",
    backstory="Expert in billing systems and payment processing"
)

refund_agent = Agent(
    name="Refunds",
    role="Refund specialist", 
    goal="Process refund requests",
    backstory="Specialist in refund policies and processing"
)

# Create triage agent with handoffs
triage_agent = Agent(
    name="Triage",
    role="Customer service triage",
    goal="Route customer inquiries to the right specialist",
    backstory="Expert at understanding customer needs",
    handoffs=[billing_agent, refund_agent]
)

# The triage agent can now transfer to billing or refund agents
response = triage_agent.chat("I need a refund for my order")

Advanced Features

Custom Callbacks

Execute custom code when handoffs occur:

def log_handoff(from_agent, to_agent, context):
    print(f"Transferring from {from_agent.name} to {to_agent.name}")
    # Log to monitoring system, send notifications, etc.

from praisonaiagents import handoff

handoff_config = handoff(
    agent=billing_agent,
    on_handoff=log_handoff
)

triage_agent = Agent(
    name="Triage",
    handoffs=[handoff_config]
)

Structured Input

Use Pydantic models to pass typed data during handoffs:

from pydantic import BaseModel

class CustomerContext(BaseModel):
    customer_id: str
    order_id: str
    issue_type: str
    priority: str

handoff_config = handoff(
    agent=billing_agent,
    input_model=CustomerContext
)

Input Filters

Control what conversation history is passed to the target agent:

handoff_config = handoff(
    agent=technical_agent,
    filter={
        "remove_all_tools": True,  # Remove tool calls from history
        "keep_last_n_messages": 5,  # Only pass last 5 messages
        "remove_system_messages": True  # Remove system prompts
    }
)

Custom Tool Names

Override the default handoff tool name and description:

handoff_config = handoff(
    agent=escalation_agent,
    name="escalate_to_manager",
    description="Escalate complex issues to a manager for resolution"
)

Complete Example

from praisonaiagents import Agent, handoff
from pydantic import BaseModel

# Define context model
class CustomerInfo(BaseModel):
    name: str
    account_id: str
    issue_description: str

# Create specialised agents
billing_agent = Agent(
    name="BillingExpert",
    role="Billing specialist",
    goal="Resolve billing issues",
    backstory="10 years experience in billing",
    tools=[check_balance, process_payment]
)

technical_agent = Agent(
    name="TechSupport",
    role="Technical support",
    goal="Solve technical problems",
    backstory="Expert in troubleshooting",
    tools=[check_logs, restart_service]
)

manager_agent = Agent(
    name="Manager",
    role="Customer service manager",
    goal="Handle escalations",
    backstory="Experienced in conflict resolution"
)

# Configure handoffs with advanced features
handoffs = [
    # Simple handoff
    billing_agent,
    
    # Handoff with callback
    handoff(
        agent=technical_agent,
        on_handoff=lambda f, t, c: log_transfer(f.name, t.name)
    ),
    
    # Handoff with structured input and filters
    handoff(
        agent=manager_agent,
        name="escalate_to_management",
        description="Escalate serious issues to management",
        input_model=CustomerInfo,
        filter={"keep_last_n_messages": 3}
    )
]

# Create main agent with handoffs
support_agent = Agent(
    name="Support",
    role="Customer support",
    goal="Help customers with their issues",
    backstory="First point of contact",
    handoffs=handoffs
)

# Use in conversation
result = support_agent.chat("My payment failed and now I can't access my account")

Best Practices

  1. Clear Agent Responsibilities: Each agent should have a well-defined role and expertise area
  2. Minimal Context: Use filters to pass only relevant conversation history
  3. Structured Data: Use Pydantic models for complex handoff scenarios
  4. Monitoring: Implement callbacks to track handoff patterns
  5. Graceful Fallbacks: Always have a path for unhandled scenarios

Implementation Details

Under the hood, handoffs are converted to tools:

# This handoff configuration:
agent.handoffs = [billing_agent]

# Becomes a tool like:
{
    "name": "transfer_to_billing",
    "description": "Transfer conversation to Billing agent: Billing specialist...",
    "function": <transfer_function>
}

The agent’s prompt is also updated to include handoff instructions, making the agent aware of when and how to delegate tasks.

Common Patterns

Customer Service Router

# Specialised agents for different departments
agents = {
    "billing": billing_agent,
    "technical": technical_agent,
    "sales": sales_agent,
    "refunds": refund_agent
}

router = Agent(
    name="Router",
    handoffs=list(agents.values())
)

Escalation Chain

level1 = Agent(name="L1Support", handoffs=[level2])
level2 = Agent(name="L2Support", handoffs=[level3])
level3 = Agent(name="L3Support", handoffs=[manager])

Skill-based Routing

# Agents with specific skills
python_expert = Agent(name="PythonExpert", ...)
javascript_expert = Agent(name="JSExpert", ...)
database_expert = Agent(name="DBExpert", ...)

tech_lead = Agent(
    name="TechLead",
    handoffs=[python_expert, javascript_expert, database_expert]
)

Troubleshooting

Handoff Not Triggering

  • Ensure the agent’s LLM supports tool calling
  • Check that handoff agents are properly configured
  • Verify the conversation context clearly indicates need for transfer

Context Loss

  • Use input filters to preserve important context
  • Consider using structured input models
  • Implement callbacks to track what information is passed

Circular Handoffs

  • Design clear handoff hierarchies
  • Implement logic to prevent infinite loops
  • Use callbacks to detect circular patterns