Skip to main content
The state machine pattern describes workflows where an agent’s behavior changes as it moves through different states of a task. This tutorial shows how to implement a state machine by using tool calls to dynamically change a single agent’s configuration—updating its available tools and instructions based on the current state. The state can be determined from multiple sources: the agent’s past actions (tool calls), external state (such as API call results), or even initial user input (for example, by running a classifier to determine user intent). 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 subagents pattern where sub-agents are called as tools, the state machine pattern uses a single agent whose configuration changes based on workflow progress. Each “step” 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

Installation

This tutorial requires the langchain package:
For more details, see our Installation guide.

LangSmith

Set up LangSmith to inspect what is happening inside your agent. Then set the following environment variables:

Select an LLM

Select a chat model from LangChain’s suite of integrations:
👉 Read the OpenAI chat model integration docs

1. Define custom state

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

2. Create tools that manage workflow state

Create tools that update the workflow state. These tools allow the agent to record information and transition to the next step. The key is using Command to update state, including the current_step 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_step. This is how the state machine works - tools control workflow progression.

3. Define step configurations

Define prompts and tools for each step. First, define the prompts for each step:
Then map step names to their configurations using a dictionary:
This dictionary-based configuration makes it easy to:
  • See all steps at a glance
  • Add new steps (just add another entry)
  • Understand the workflow dependencies (requires field)
  • Use prompt templates with state variables (e.g., {warranty_status})

4. Create step-based middleware

Create middleware that reads current_step from state and applies the appropriate configuration. We’ll use the @wrap_model_call decorator for a clean implementation:
This middleware:
  1. Reads current step: Gets current_step from state (defaults to “warranty_collector”).
  2. Looks up configuration: Finds the matching entry in STEP_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 step-based middleware and a checkpointer for state persistence:
Why a checkpointer? The checkpointer maintains state across conversation turns. Without it, the current_step state would be lost between user messages, breaking the workflow.

6. Test the workflow

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

7. Understanding state transitions

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: recordWarrantyStatus("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: recordIssueType("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_step, and middleware responds by applying the appropriate configuration on the next turn.

8. Manage message history

As the agent progresses through steps, message history grows. Use summarization middleware to compress earlier messages while preserving conversational context:
See the short-term memory guide for other memory management techniques.

9. Add flexibility: Go back

Some workflows need to allow users to return to previous steps to correct information (e.g., changing warranty status or issue classification). However, not all transitions make sense—for example, you typically can’t go back once a refund has been processed. For this support workflow, we’ll add tools to return to the warranty verification and issue classification steps.
If your workflow requires arbitrary transitions between most steps, consider whether you need a structured workflow at all. This pattern works best when steps follow a clear sequential progression with occasional backwards transitions for corrections.
Add “go back” tools to the resolution step:
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