Prerequisites
Complete Installation and setup before starting this tutorial. Run this notebook top to bottom. You do not need a Mix project.
Setup
Mix.install([
{:jido, "~> 2.0"}
])
Define the agent
An agent is an immutable struct backed by a typed schema. The schema enforces types and defaults so state transitions stay consistent across runs.
defmodule CounterAgent do
use Jido.Agent,
name: "counter_agent",
description: "Tracks a simple counter",
schema: Zoi.object(%{
count: Zoi.integer() |> Zoi.default(0)
})
end
This module provides new/1, set/2, validate/2, and cmd/2. The agent is data, not a process. When you need supervision or long-lived runtime state, wrap it in a Jido.AgentServer.
Define an action
Actions are the only way to change agent state. Each action defines a schema for its inputs and implements run/2. The first argument is validated params. The second is the execution context, which includes context.state - the current agent state.
defmodule IncrementAction do
use Jido.Action,
name: "increment",
description: "Increments the counter by a specified amount",
schema: Zoi.object(%{
by: Zoi.integer() |> Zoi.default(1)
})
@impl true
def run(params, context) do
current = Map.get(context.state, :count, 0)
{:ok, %{count: current + params.by}}
end
end
Params are schema-validated before run/2 is called, so params.by is always an integer. The return value is a partial state map that cmd/2 merges into the agent.
Create an agent and run a command
cmd/2 takes the current agent and an action instruction, runs the action, and returns a two-element tuple: the updated agent and a list of directives. The original agent is unchanged.
agent = CounterAgent.new()
{updated_agent, directives} =
CounterAgent.cmd(agent, {IncrementAction, %{by: 3}})
Inspect the results
The updated agent reflects the new state. Directives are outbound instructions that describe side effects for the runtime to handle later.
updated_agent.state
directives
The directives list is empty because this action is pure. If an action needed to emit an event or schedule work, it would return Jido.Agent.Directive structs alongside the state changes.
Handle validation errors
If you pass params that fail schema validation, cmd/2 returns the original agent unchanged and a list containing a Directive.Error struct.
{error_agent, error_directives} =
CounterAgent.cmd(agent, {IncrementAction, %{by: "not_a_number"}})
error_agent.state
error_directives
The agent state remains %{count: 0} - the same as before the failed command. The error directive wraps a Jido.Error with context about what went wrong.
Next steps
- Agents for the full conceptual model
- Actions for deeper action design patterns
- Your first LLM agent to add language model capabilities