Quick Start
Mix.install([
{:jido, "~> 2.0"}
])
Overview
This guide gets you from zero to a working Jido agent in 5 minutes. You’ll learn:
- How to start Jido’s supervision tree
- How to define an agent with state
-
How to use
cmd/2for pure state transformations - How to run agents in AgentServer for real applications
Step 1: Start Jido
Jido uses instance-scoped supervisors. Define an instance module and add it to your supervision tree:
# In lib/my_app/jido.ex
defmodule MyApp.Jido do
use Jido, otp_app: :my_app
end
# In config/config.exs
config :my_app, MyApp.Jido,
max_tasks: 1000,
agent_pools: []
# In your application.ex
children = [
MyApp.Jido
]
Supervisor.start_link(children, strategy: :one_for_one)
For this Livebook, we’ll start it directly:
defmodule Demo.Jido do
use Jido, otp_app: :demo
end
{:ok, _pid} = Demo.Jido.start_link()
Step 2: Define an Agent
Agents are immutable structs with a schema defining their state:
defmodule CounterAgent do
use Jido.Agent,
name: "counter",
description: "A simple counter agent",
schema: [
count: [type: :integer, default: 0],
status: [type: :atom, default: :idle]
]
end
Step 3: Use cmd/2 for State Transformations
The core pattern in Jido is cmd/2 - a pure function that takes an agent and an action, returning the updated agent and any directives:
{agent, directives} = MyAgent.cmd(agent, action)
Let’s define an action and use it:
defmodule IncrementAction do
use Jido.Action,
name: "increment",
description: "Increments the counter by a given amount",
schema: [
amount: [type: :integer, default: 1]
]
@impl true
def run(params, context) do
current = context.state[:count] || 0
{:ok, %{count: current + params.amount}}
end
end
Now use it:
agent = CounterAgent.new()
IO.inspect(agent.state, label: "Initial state")
{agent, _directives} = CounterAgent.cmd(agent, {IncrementAction, %{amount: 5}})
IO.inspect(agent.state, label: "After increment by 5")
{agent, _directives} = CounterAgent.cmd(agent, IncrementAction)
IO.inspect(agent.state, label: "After increment by 1")
Key points:
-
cmd/2is pure - same inputs always produce same outputs - The returned agent is fully updated
- Directives describe side effects but don’t modify state
Step 4: Handle Signals with AgentServer
For real applications, agents run inside AgentServer - a GenServer that handles signals and executes directives.
First, let’s add signal handling to our agent:
defmodule CounterAgentWithSignals do
use Jido.Agent,
name: "counter_with_signals",
description: "Counter that responds to signals",
schema: [
count: [type: :integer, default: 0],
status: [type: :atom, default: :idle]
]
alias Jido.Signal
@impl true
def handle_signal(agent, %Signal{type: "counter.increment"} = signal) do
amount = signal.data[:amount] || 1
current = agent.state[:count]
{:ok, agent} = set(agent, count: current + amount)
{agent, []}
end
def handle_signal(agent, %Signal{type: "counter.decrement"} = signal) do
amount = signal.data[:amount] || 1
current = agent.state[:count]
{:ok, agent} = set(agent, count: current - amount)
{agent, []}
end
def handle_signal(agent, %Signal{type: "counter.reset"}) do
{:ok, agent} = set(agent, count: 0)
{agent, []}
end
def handle_signal(agent, _signal), do: {agent, []}
end
Now start the agent under Jido and send signals:
{:ok, pid} = Demo.Jido.start_agent(CounterAgentWithSignals, id: "my-counter")
Create and send signals:
alias Jido.Signal
increment_signal = Signal.new!("counter.increment", %{amount: 10}, source: "/user")
{:ok, agent} = Jido.AgentServer.call(pid, increment_signal)
IO.inspect(agent.state, label: "After increment by 10")
decrement_signal = Signal.new!("counter.decrement", %{amount: 3}, source: "/user")
{:ok, agent} = Jido.AgentServer.call(pid, decrement_signal)
IO.inspect(agent.state, label: "After decrement by 3")
For async processing, use cast/2:
reset_signal = Signal.new!("counter.reset", %{}, source: "/admin")
:ok = Jido.AgentServer.cast(pid, reset_signal)
Process.sleep(50)
{:ok, state} = Jido.AgentServer.state(pid)
IO.inspect(state.agent.state, label: "After reset")
Step 5: Query and Manage Agents
Your instance module provides utilities for managing agents:
Demo.Jido.list_agents()
|> IO.inspect(label: "All agents")
Demo.Jido.agent_count()
|> IO.inspect(label: "Agent count")
found_pid = Demo.Jido.whereis("my-counter")
IO.inspect(found_pid == pid, label: "Found by ID?")
Stop the agent when done:
:ok = Demo.Jido.stop_agent("my-counter")
Summary
You’ve learned the core Jido workflow:
| Concept | Purpose |
|---|---|
use Jido, otp_app: :my_app |
Define an instance module for your agents |
use Jido.Agent |
Define agents with schemas |
cmd/2 |
Pure state transformations |
MyApp.Jido.start_agent/2 |
Run agents in AgentServer |
AgentServer.call/3 |
Synchronous signal processing |
AgentServer.cast/2 |
Async signal processing |
Signal.new!/3 |
Create signals to send to agents |
Next Steps
- Core Loop — Understand “Signal → Action → cmd/2 → {agent, directives}”
- Your First Skill — Package actions with isolated state
- Your First Sensor — Feed external events into your agent
- Seeing What Happened — Debug with logs and telemetry