Jido Execution Strategies: Direct vs FSM and When to Use Each
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: 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 StateOps, 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?: trueoption
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.Snapshotfor 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:
- Agent definition –
use Jido.Agentinjects astrategyfield selected via thestrategy:option - Command normalization –
MyAgent.cmd/2normalizes incoming actions intoInstructionlists, then delegates tostrategy.cmd/3 - Strategy execution – The strategy module runs instructions, manages state transitions, and emits directives
- Snapshot exposure –
snapshot/2returns a stableStrategy.Snapshotstruct without leaking internal representation - Thread tracking – Optional execution threads live under
agent.state.__thread__when enabled
Code Examples
Basic Direct Strategy
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
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
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
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) executes instructions immediately and sequentially, ideal for simple one-shot actions with optional thread tracking. - FSM strategy (
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.Strategybehaviour to handle domain-specific execution patterns like LLM chains or behavior trees. - All strategies use
agent.state.__strategy__for internal state viaJido.Agent.Strategy.Statehelpers and expose snapshots viasnapshot/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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →