Skip to main content
The handoffs pattern is a multi-agent architecture where agents pass control to each other through state transitions. This tutorial demonstrates how to build an online customer support system where different “agents” (really, different configurations of a single agent) handle specific stages of the support workflow. In this tutorial, you’ll build a customer support agent that does the following:
  • Collects warranty information before proceeding.
  • Classifies issues as hardware or software.
  • Provides solutions or escalates to human support.
  • Maintains conversation state across multiple turns.
Unlike the supervisor pattern where sub-agents are called as tools, handoffs create a state machine where the active stage changes based on workflow progress. Each “stage” is just a different configuration (system prompt + tools) of the same underlying agent, selected dynamically based on state. Here’s the workflow we’ll build:

Setup

  1. Install the langchain package:
    For more details, see our Installation guide.
  2. Set up LangSmith to inspect what is happening inside your agent:
  3. Select a chat model. For this tutorial, we’ll use Claude 3.5 Sonnet:

1. Define custom state

First, define a custom state schema that tracks which stage is currently active:
The current_stage field is the core of the handoffs pattern - it determines which configuration (prompt + tools) is loaded on each turn.

2. Create handoff tools

Create tools that update the workflow state. These tools allow the agent to record information and transition to the next stage. The key to handoffs is using Command to update state, including the current_stage field:
Notice how record_warranty_status and record_issue_type return Command objects that update both the data (warranty_status, issue_type) AND the current_stage. This is the handoff mechanism - tools control the workflow.

3. Define stage configurations

Now define prompts and tools for each stage. We’ll use a simple dictionary to map stage names to their configurations:
This dictionary-based configuration makes it easy to:
  • See all stages at a glance
  • Add new stages (just add another entry)
  • Understand the workflow dependencies (requires field)
  • Use prompt templates with state variables (e.g., {warranty_status})

4. Create stage-based middleware

Create middleware that reads current_stage from state and applies the appropriate configuration. We’ll use the @wrap_model_call decorator for a clean implementation:
This middleware:
  1. Reads current stage: Gets current_stage from state (defaults to “warranty_collector”).
  2. Looks up configuration: Finds the matching entry in STAGE_CONFIG.
  3. Validates dependencies: Ensures required state fields exist.
  4. Formats prompt: Injects state values into the prompt template.
  5. Applies configuration: Overrides the system prompt and available tools.
The request.override() method is key - it allows us to dynamically change the agent’s behavior based on state without creating separate agent instances.

5. Create the agent

Now create the agent with the stage-based middleware and a checkpointer for state persistence:
Why a checkpointer? The checkpointer maintains state across conversation turns. Without it, the current_stage state would be lost between user messages, breaking the handoff flow.

6. Test the workflow

Test the complete handoff workflow:
Expected flow:
  1. Warranty verification stage: Asks about warranty status
  2. Issue classification stage: Asks about the problem, determines it’s hardware
  3. Resolution stage: Provides warranty repair instructions

7. Understanding the handoff mechanism

Let’s trace what happens at each turn:

Turn 1: Initial message

Middleware applies:
  • System prompt: WARRANTY_COLLECTOR_PROMPT
  • Tools: [record_warranty_status]

Turn 2: After warranty recorded

Tool call: record_warranty_status("in_warranty") returns:
Next turn, middleware applies:
  • System prompt: ISSUE_CLASSIFIER_PROMPT (formatted with warranty_status="in_warranty")
  • Tools: [record_issue_type]

Turn 3: After issue classified

Tool call: record_issue_type("hardware") returns:
Next turn, middleware applies:
  • System prompt: RESOLUTION_SPECIALIST_PROMPT (formatted with warranty_status and issue_type)
  • Tools: [provide_solution, escalate_to_human]
The key insight: Tools drive the workflow by updating current_stage, and middleware responds by applying the appropriate configuration.

8. Manage message history

A critical aspect of production handoffs is managing what each agent sees in the conversation history. Without proper management, message history grows unbounded and agents can get confused by conversations from other stages. After a few agent transitions, the full message history includes conversations from all previous agents. The key is to separate message history from cross-agent memory:
  • Message history: Agent-scoped, bounded, ephemeral (what the agent sees)
  • State dict: Cross-agent, persistent, structured data (what the agent knows)
  • System messages: Bridge between state and context (injected summaries)
Use @before_model middleware to trim messages before each agent sees them:
Since agents only see recent messages, inject context about what previous agents learned via the system prompt:

9. Add flexibility: Go back

A key UX requirement is allowing users to correct mistakes. Add “go back” tools:
Update the resolution specialist’s prompt to mention these tools:
Now the agent can handle corrections:

Complete example

Here’s everything together in a runnable script:

Next steps


Connect these docs programmatically to Claude, VSCode, and more via MCP for real-time answers.