# Jido Execution Strategies: Direct vs FSM and When to Use Each

> Explore Jido execution strategies: Direct for immediate tasks and FSM for state-machine workflows. Learn when to use each and discover custom strategy options.

- Repository: [agentjido/jido](https://github.com/agentjido/jido)
- Tags: deep-dive
- Published: 2026-03-09

---

**Jido provides two built-in execution strategies—Direct for immediate action execution and FSM for state-machine workflows—plus support for custom strategies via the `Jido.Agent.Strategy` behaviour.**

The **agentjido/jido** repository separates pure decision logic from runtime side-effects through pluggable execution strategies. When you define an agent, you select a strategy module that determines how actions are dispatched, how state transitions occur, and how execution progress is tracked.

## Built-in Execution Strategies

Jido ships with two production-ready strategies in `lib/jido/agent/strategy/`. Each implements the callbacks defined in [`lib/jido/agent/strategy.ex`](https://github.com/agentjido/jido/blob/main/lib/jido/agent/strategy.ex): `init/2`, `cmd/3`, `tick/2`, and `snapshot/2`.

### Direct Strategy

**`Jido.Agent.Strategy.Direct`** is the default execution strategy. It runs every normalized instruction immediately and sequentially via `Jido.Exec.run/1`, merges results into the agent's state, applies any `StateOp`s, and returns only external directives.

Use **Direct** when:
- You need simple, one-shot actions where results apply right away
- You don't require multi-step orchestration, retry loops, or explicit state-machine transitions
- You want optional thread-level tracing via the `thread?: true` option

When `thread?: true` is passed (or a thread already exists), the strategy records instruction start/end checkpoints in the thread managed by `Jido.Thread.Agent`.

### FSM Strategy

**`Jido.Agent.Strategy.FSM`** provides a finite-state-machine execution model. It maintains an internal `Machine` struct (tracking status, processed count, last result, error, and transition map) inside `agent.state.__strategy__`.

Use **FSM** when:
- Your workflow requires explicit states (e.g., "idle → processing → completed/failed")
- You need to enforce allowed state transitions, handle retries, or batch-process instruction collections
- You want observable progress via `Strategy.Snapshot` for supervisors or monitoring tools

The FSM dispatches instructions one-by-one, transitions states after each result, and can auto-transition back to the initial state. Thread checkpoints are added automatically when `thread?: true`.

## Creating Custom Execution Strategies

You can implement domain-specific orchestration by creating a module that `use Jido.Agent.Strategy` and implements at least `cmd/3`. Store custom data in `agent.state.__strategy__` via `Jido.Agent.Strategy.State` helpers, and expose tailored snapshots via `snapshot/2`.

Common use cases for custom strategies include:
- LLM chain-of-thought loops
- Behavior trees
- Tool-calling cycles
- External scheduler integration
- Event-driven execution loops

## How Execution Strategies Work

The strategy architecture follows a strict separation between pure logic and side-effects:

1. **Agent definition** – `use Jido.Agent` injects a `strategy` field selected via the `strategy:` option
2. **Command normalization** – `MyAgent.cmd/2` normalizes incoming actions into `Instruction` lists, then delegates to `strategy.cmd/3`
3. **Strategy execution** – The strategy module runs instructions, manages state transitions, and emits directives
4. **Snapshot exposure** – `snapshot/2` returns a stable `Strategy.Snapshot` struct without leaking internal representation
5. **Thread tracking** – Optional execution threads live under `agent.state.__thread__` when enabled

## Code Examples

### Basic Direct Strategy

```elixir
defmodule SimpleAgent do
  use Jido.Agent,
    name: "simple",
    strategy: Jido.Agent.Strategy.Direct
end

# Execute an action immediately

{:ok, agent} = SimpleAgent.new()
{agent, directives} = SimpleAgent.cmd(agent, MyAction, foo: "bar")

```

### Direct Strategy with Thread Tracking

```elixir
defmodule TracedAgent do
  use Jido.Agent,
    name: "traced",
    strategy: {Jido.Agent.Strategy.Direct, thread?: true}
end

{:ok, agent} = TracedAgent.new()
{agent, _} = TracedAgent.cmd(agent, MyAction, opts: 1)

# Inspect the thread via Jido.Thread.Agent.fetch/1

```

### FSM Strategy for Workflow States

```elixir
defmodule OrderProcessor do
  use Jido.Agent,
    name: "order",
    strategy: {
      Jido.Agent.Strategy.FSM,
      initial_state: "idle",
      transitions: %{
        "idle"       => ["processing"],
        "processing" => ["completed", "failed"]
      }
    }
end

{:ok, agent} = OrderProcessor.new()

# Transition from idle to processing

{agent, dirs} = OrderProcessor.cmd(agent, StartOrder, order_id: 123)

```

### Custom LLM Chain-of-Thought Strategy

```elixir
defmodule ChainOfThoughtStrategy do
  use Jido.Agent.Strategy

  @impl true
  def init(agent, _ctx), do: {agent, []}

  @impl true
  def cmd(agent, instructions, _ctx) do
    Enum.reduce(instructions, {agent, []}, fn instr, {ag, dirs} ->
      {:ok, result} = MyLLM.run(instr.params.prompt)
      ag = Jido.Agent.Strategy.State.update(ag, fn s -> 
        Map.put(s, :thoughts, [result | Map.get(s, :thoughts, [])]) 
      end)
      {ag, dirs}
    end)
  end
end

defmodule ThoughtfulAgent do
  use Jido.Agent,
    name: "thoughtful",
    strategy: ChainOfThoughtStrategy
end

```

## Summary

- **Direct strategy** ([`lib/jido/agent/strategy/direct.ex`](https://github.com/agentjido/jido/blob/main/lib/jido/agent/strategy/direct.ex)) executes instructions immediately and sequentially, ideal for simple one-shot actions with optional thread tracking.
- **FSM strategy** ([`lib/jido/agent/strategy/fsm.ex`](https://github.com/agentjido/jido/blob/main/lib/jido/agent/strategy/fsm.ex)) manages finite-state-machine workflows with explicit transitions, perfect for multi-step processes requiring state enforcement and observability.
- **Custom strategies** implement the `Jido.Agent.Strategy` behaviour to handle domain-specific execution patterns like LLM chains or behavior trees.
- All strategies use `agent.state.__strategy__` for internal state via `Jido.Agent.Strategy.State` helpers and expose snapshots via `snapshot/2`.

## Frequently Asked Questions

### What is the default execution strategy in Jido?

**`Jido.Agent.Strategy.Direct`** is the default strategy when you don't specify a `strategy:` option in your agent definition. It executes all instructions immediately using `Jido.Exec.run/1` and applies results directly to the agent state.

### When should I use the FSM strategy instead of Direct?

Use the **FSM strategy** when your workflow requires explicit state management, such as enforcing valid transitions between "pending" and "completed" states, handling retries, or batch-processing collections where each item moves through a defined lifecycle. The Direct strategy cannot enforce state transitions or pause between steps.

### How do I enable execution tracing with strategies?

Pass `thread?: true` in the strategy options tuple when defining your agent: `strategy: {Jido.Agent.Strategy.Direct, thread?: true}`. Both Direct and FSM strategies support this option, which records instruction start/end checkpoints in `agent.state.__thread__` via `Jido.Thread.Agent`.

### Can I combine different strategies in the same application?

Yes. Strategy selection happens at the individual agent level via the `strategy:` option in `use Jido.Agent`. You can have some agents using Direct for simple tasks while others use FSM for complex workflows, or implement custom strategies for specific domains like LLM orchestration.