View Source Jido.Agent behaviour (Jido v1.0.0)
Defines an Agent within the Jido system - a compile-time defined entity for managing complex workflows through a sequence of Actions.
Overview
An Agent represents a stateful workflow executor that can plan and execute a series of Actions in a type-safe and composable way. Agents provide a consistent interface for orchestrating complex operations while maintaining state validation, error handling, and extensibility through lifecycle hooks.
Architecture
Key Concepts
- Agents are defined at compile-time using the
use Jido.Agent
macro - Each Agent instance is created at server with a guaranteed unique ID
- Agents maintain their own state schema for validation
- Actions are registered with Agents and executed through a Runner
- Lifecycle hooks enable customization of validation, planning and execution flows
All operations follow a consistent pattern returning
{:ok, result} | {:error, reason}
Type Safety & Validation
- Configuration validated at compile-time via NimbleOptions
- Server state changes validated against defined schema
- Action modules checked for behavior implementation
- Consistent return type enforcement across operations
Features
- Compile-time configuration validation
- Server parameter validation via NimbleOptions
- Comprehensive error handling with recovery hooks
- Extensible lifecycle callbacks for all operations
- JSON serialization support for persistence
- Dynamic Action planning and execution
- State management with validation and dirty tracking
Usage
Basic Example
defmodule MyAgent do
use Jido.Agent,
name: "my_agent",
description: "Performs a complex workflow",
category: "processing",
tags: ["example", "demo"],
vsn: "1.0.0",
schema: [
input: [type: :string, required: true],
status: [type: :atom, values: [:pending, :running, :complete]]
],
actions: [MyAction1, MyAction2]
end
# Create and configure agent
{:ok, agent} = MyAgent.new()
{:ok, agent} = MyAgent.set(agent, input: "test data")
# Plan and execute actions
{:ok, agent} = MyAgent.plan(agent, MyAction1, %{value: 1})
{:ok, agent} = MyAgent.run(agent) # Result stored in agent.result
Customizing Behavior
defmodule CustomAgent do
use Jido.Agent,
name: "custom_agent",
schema: [status: [type: :atom]]
# Add pre-execution validation
def on_before_run(agent) do
if agent.state.status == :ready do
{:ok, agent}
else
{:error, "Agent not ready"}
end
end
# Custom error recovery
def on_error(agent, error) do
Logger.warning("Agent error", error: error)
{:ok, %{agent | state: %{status: :error}}}
end
end
Callbacks
The following optional callbacks can be implemented to customize agent behavior.
All callbacks receive the agent struct and return {:ok, agent} | {:error, reason}
.
on_before_validate_state/1
- Pre-validation processingon_after_validate_state/1
- Post-validation processingon_before_plan/3
- Pre-planning processing with paramson_before_run/1
- Pre-execution validation/setupon_after_run/2
- Post-execution processingon_after_directives/2
- Post-directive applicationon_error/2
- Error handling and recovery
Default implementations pass through the agent unchanged.
Important Notes
Each Agent module defines its own struct type and behavior. Agent functions must be called on matching agent structs:
# Correct usage: agent = MyAgent.new() {:ok, agent} = MyAgent.set(agent, attrs)
# Incorrect usage: agent = MyAgent.new() {:ok, agent} = OtherAgent.set(agent, attrs) # Will fail - type mismatch
Runner Architecture
The Runner executes actions in the agent's pending_instructions queue.
- Default implementation:
Jido.Runner.Simple
- Custom runners must implement the
Jido.Runner
behavior - Runners handle instruction execution and state management
- Support for different execution strategies (simple, chain, parallel)
Error Handling
Errors are returned as tagged tuples: {:error, reason}
Common error types:
:validation_error
- Schema/parameter validation failures:execution_error
- Action execution failures:directive_error
- Directive application failures
See Jido.Action
for implementing compatible Actions.
Type Specifications
t()
- The Agent struct typeinstruction()
- Single action with paramsinstructions()
- List of instructionsagent_result()
-{:ok, t()} | {:error, term()}
Summary
Callbacks
Called after successful directive application. Allows post-processing of directive results.
Called after successful action execution. Allows post-processing of execution results.
Called after state validation but before saving changes. Allows post-processing of validated state.
Called before planning actions, allows preprocessing of action parameters and potential action routing/transformation.
Called after action planning but before execution. Allows inspection/modification of planned actions.
Called before validating any state changes to the Agent. Allows custom preprocessing of state attributes.
Called when any error occurs during the agent lifecycle. Provides error handling and recovery strategies.
Functions
Raises an error indicating that Agents cannot be defined at server.
Registers one or more action modules with the agent at server. Registered actions
can be used in planning and execution. Action modules must implement the Jido.Action
behavior and pass validation checks.
Returns all action modules registered with the agent in registration order. Includes both compile-time and server-registered actions.
Types
@type agent_result() :: {:ok, t()} | {:error, Jido.Error.t()}
@type instructions() :: instruction() | [instruction()]
@type map_result() :: {:ok, map()} | {:error, Jido.Error.t()}
@type t() :: %Jido.Agent{ actions: [module()], category: String.t() | nil, description: String.t() | nil, dirty_state?: boolean(), id: String.t() | nil, name: String.t() | nil, pending_instructions: :queue.queue(instruction()) | nil, result: term(), runner: module() | nil, schema: NimbleOptions.schema() | nil, state: map(), tags: [String.t()] | nil, vsn: String.t() | nil }
Callbacks
@callback on_after_directives(agent :: t(), result :: map()) :: agent_result()
Called after successful directive application. Allows post-processing of directive results.
@callback on_after_run(agent :: t(), result :: map()) :: agent_result()
Called after successful action execution. Allows post-processing of execution results.
@callback on_after_validate_state(agent :: t()) :: agent_result()
Called after state validation but before saving changes. Allows post-processing of validated state.
@callback on_before_plan(agent :: t(), action :: module(), params :: map()) :: agent_result()
Called before planning actions, allows preprocessing of action parameters and potential action routing/transformation.
@callback on_before_run(agent :: t()) :: agent_result()
Called after action planning but before execution. Allows inspection/modification of planned actions.
@callback on_before_validate_state(agent :: t()) :: agent_result()
Called before validating any state changes to the Agent. Allows custom preprocessing of state attributes.
@callback on_error(agent :: t(), reason :: any()) :: agent_result()
Called when any error occurs during the agent lifecycle. Provides error handling and recovery strategies.
Functions
@spec new() :: {:error, Jido.Error.t()}
Raises an error indicating that Agents cannot be defined at server.
This function exists to prevent misuse of the Agent system, as Agents are designed to be defined at compile-time only.
Returns
Always returns {:error, reason}
where reason
is a config error.
Examples
iex> Jido.Agent.new()
{:error, %Jido.Error{type: :config_error, message: "Agents should not be defined at server"}}
@spec new(String.t()) :: {:error, Jido.Error.t()}
@spec register_action(t(), module() | [module()]) :: {:ok, t()} | {:error, Jido.Error.t()}
Registers one or more action modules with the agent at server. Registered actions
can be used in planning and execution. Action modules must implement the Jido.Action
behavior and pass validation checks.
Action Requirements
- Must be valid Elixir modules implementing
Jido.Action
behavior - Must be loaded and available at server
- Must pass validation via
Jido.Util.validate_actions/1
Parameters
agent
- The agent struct to updateaction_module
- Single action module or list of action modules to register
Returns
{:ok, updated_agent}
- Agent struct with newly registered actions prepended{:error, String.t()}
- If action validation fails
Examples
# Register single action
{:ok, agent} = MyAgent.register_action(agent, MyApp.Actions.ProcessFile)
# Register multiple actions
{:ok, agent} = MyAgent.register_action(agent, [Action1, Action2])
Server Considerations
- Actions persist only for the agent's lifecycle
- Duplicates are prevented during validation
- Most recently registered actions take precedence
See Jido.Action
for implementing actions and Jido.Util.validate_actions/1
for validation details.
Returns all action modules registered with the agent in registration order. Includes both compile-time and server-registered actions.
Parameters
agent
- The agent struct to inspect
Returns
[module()]
- Action modules ordered by registration time (newest first)
Examples
agent = MyAgent.new()
MyAgent.registered_actions(agent) #=> [DefaultAction, CoreAction]
{:ok, agent} = MyAgent.register_action(agent, CustomAction)
MyAgent.registered_actions(agent) #=> [CustomAction, DefaultAction, CoreAction]
See register_action/2
for adding actions and plan/3
for using them.