Jido.AI.Agent (Jido AI v0.5.2)

View Source

General purpose AI agent powered by Jido

Summary

Functions

Validates, plans and executes instructions for the agent with enhanced error handling and state management.

Removes a previously registered action module from the Agent.

Callback implementation for Jido.Agent.mount/2.

Creates a new agent instance with an optional ID and initial state.

Callback implementation for Jido.Agent.on_before_run/1.

Callback implementation for Jido.Agent.on_error/2.

Returns the number of pending actions in the agent's pending_instructions queue.

Plans one or more actions by adding them to the agent's pending instruction queue. Actions must be registered and valid for the agent. Planning updates the dirty_state? flag and triggers the on_before_plan callback.

Registers a new action module with the Agent at server.

Returns all action modules currently registered with the Agent.

Resets the agent's pending action queue.

Executes pending instructions in the agent's queue through a multi-phase process.

Updates the agent's state by deep merging the provided attributes. The update process

Callback implementation for Jido.Agent.shutdown/2.

Validates the agent's state through a three-phase process

Types

agent_result()

@type agent_result() :: Jido.Agent.agent_result()

agent_result_with_directives()

@type agent_result_with_directives() :: Jido.Agent.agent_result_with_directives()

instruction()

@type instruction() :: Jido.Agent.instruction()

instructions()

@type instructions() :: Jido.Agent.instructions()

map_result()

@type map_result() :: Jido.Agent.map_result()

t()

@type t() :: Jido.Agent.t()

Functions

actions()

boolean_response(pid, message)

category()

chat_response(pid, message)

cmd(agent, instructions, attrs \\ %{}, opts \\ [])

Validates, plans and executes instructions for the agent with enhanced error handling and state management.

Parameters

  • agent - The agent struct to act on
  • instructions - One of:
    • Single action module
    • Single action tuple {module, params}
    • List of action modules
    • List of {action_module, params} tuples
    • Mixed list of modules and tuples (e.g. [ValidateAction, {ProcessAction, %{file: "data.csv"}}])
  • context - Map of execution context data (default: %{})
  • opts - Keyword list of execution options:
    • :runner - Custom runner module (default: agent's configured runner)
    • :strict_validation - Enable/disable param validation (default: false)

Command Flow

  1. Optional parameter validation
  2. Instruction normalization
  3. State preparation and merging
  4. Action planning with context
  5. Execution with configured runner
  6. Result processing and state application through directives

Returns

  • {:ok, updated_agent, directives} - Command executed successfully
    • State modifications are handled through directives
    • Result stored in agent.result field
  • {:error, Error.t()} - Detailed error with context

Examples

# Basic command with single action
{:ok, agent, directives} = MyAgent.cmd(agent, ProcessAction)

# Single action with params
{:ok, agent, directives} = MyAgent.cmd(agent, {ProcessAction, %{file: "data.csv"}})

# Multiple actions with context
{:ok, agent, directives} = MyAgent.cmd(
  agent,
  [
    ValidateAction,
    {ProcessAction, %{file: "data.csv"}},
    StoreAction
  ],
  %{user_id: "123"}
)

# With custom options
{:ok, agent, directives} = MyAgent.cmd(
  agent,
  {ProcessAction, %{file: "data.csv"}},
  %{user_id: "123"},
  runner: CustomRunner
)

# Error handling
case MyAgent.cmd(agent, ProcessAction, %{}) do
  {:ok, updated_agent, directives} ->
    # Success case - state updated through directives
    updated_agent.state
  {:error, %Error{type: :validation_error}} ->
    # Handle validation failure
  {:error, %Error{type: :execution_error}} ->
    # Handle execution error
end

deregister_action(agent, action_module)

@spec deregister_action(t(), module()) :: agent_result()

Removes a previously registered action module from the Agent.

Parameters

  • agent - The Agent struct to update
  • action_module - The action module to remove

Returns

  • {:ok, updated_agent} - Action successfully deregistered
  • {:error, term()} - Deregistration failed

Example

{:ok, agent} = MyAgent.deregister_action(agent, MyApp.Actions.OldAction)

description()

handle_signal(signal, agent)

@spec handle_signal(Jido.Signal.t(), t()) :: {:ok, Jido.Signal.t()} | {:error, any()}

Callback implementation for Jido.Agent.handle_signal/2.

mount(state, opts)

@spec mount(Jido.Agent.Server.State.t(), opts :: keyword()) :: agent_result()

Callback implementation for Jido.Agent.mount/2.

name()

new(opts)

Creates a new agent instance with an optional ID and initial state.

Initialization

The new agent is initialized with:

  • A unique identifier (provided or auto-generated)
  • Default state values from schema
  • Empty instruction queue
  • Clean state flag (dirty_state?: false)
  • Configured actions from compile-time options
  • Empty result field

ID Generation

If no ID is provided, a UUIDv4 is generated using namespace-based deterministic generation via Jido.Util.generate_id/0. The generated ID is guaranteed to be:

  • Unique within the current server
  • Cryptographically secure
  • URL-safe string format

State Initialization

The initial state is constructed using default values from the compile-time schema:

  • Fields with defaults use their specified values
  • Required fields without defaults are initialized as nil
  • Optional fields without defaults are omitted
  • Unknown fields are ignored
  • Initial state map is merged and validated if provided

Parameters

  • id - Optional string ID for the agent. When provided:
    • Must be unique within your system
    • Should be URL-safe
    • Should not exceed 255 characters
    • Is used-as-is without validation
  • initial_state - Optional map of initial state values to merge with defaults

Returns

  • t() - A new agent struct containing:
    • :id - String, provided or generated identifier
    • :state - Map, initialized with schema defaults and initial_state
    • :dirty_state? - Boolean, set to false
    • :pending_instructions - Queue, empty :queue.queue()
    • :actions - List, configured action modules from compile-time
    • :result - Term, initialized as nil

Examples

# Create with auto-generated ID
agent = MyAgent.new()
agent.id #=> "c4b3f-..." (UUID format)
agent.dirty_state? #=> false

# Create with custom ID and initial state
agent = MyAgent.new("custom_id_123", %{status: :ready})
agent.id #=> "custom_id_123"
agent.state.status #=> :ready

# Schema defaults are applied
defmodule AgentWithDefaults do
  use Jido.Agent,
    name: "test",
    schema: [
      status: [type: :atom, default: :pending],
      retries: [type: :integer, default: 3],
      optional_field: [type: :string]
    ]
end

agent = AgentWithDefaults.new()
agent.state #=> %{
  status: :pending,     # From default
  retries: 3,          # From default
  optional_field: nil   # No default
}

Warning

While IDs are guaranteed unique when auto-generated, the function does not validate uniqueness of provided IDs. When supplying custom IDs, you must ensure uniqueness within your system's context.

See Jido.Util.generate_id/0 for details on ID generation.

new(id \\ nil, initial_state \\ %{})

@spec new(id :: String.t() | atom() | nil | keyword(), initial_state :: map() | nil) ::
  t()

on_after_run(agent, result, unapplied_directives)

@spec on_after_run(t(), map(), [Jido.Agent.Directive.t()]) :: agent_result()

Callback implementation for Jido.Agent.on_after_run/3.

on_after_validate_state(agent)

@spec on_after_validate_state(t()) :: agent_result()

Callback implementation for Jido.Agent.on_after_validate_state/1.

on_before_plan(agent, instructions, context)

@spec on_before_plan(t(), Jido.Instruction.instruction_list(), map()) ::
  agent_result()

Callback implementation for Jido.Agent.on_before_plan/3.

on_before_run(agent)

@spec on_before_run(t()) :: agent_result()

Callback implementation for Jido.Agent.on_before_run/1.

on_before_validate_state(agent)

@spec on_before_validate_state(t()) :: agent_result()

Callback implementation for Jido.Agent.on_before_validate_state/1.

on_error(agent, reason)

@spec on_error(t(), any()) :: agent_result()

Callback implementation for Jido.Agent.on_error/2.

pending?(agent)

@spec pending?(t()) :: non_neg_integer()

Returns the number of pending actions in the agent's pending_instructions queue.

Parameters

  • agent: The agent struct to check

Returns

  • Integer count of pending actions

plan(agent, instructions, context \\ %{})

@spec plan(t() | Jido.server(), instructions(), map()) :: agent_result()

Plans one or more actions by adding them to the agent's pending instruction queue. Actions must be registered and valid for the agent. Planning updates the dirty_state? flag and triggers the on_before_plan callback.

Action Registration

  • Actions must be registered via register_action/2
  • Invalid or unregistered actions fail planning

Parameters

  • agent - The agent struct to plan actions for
  • instructions - One of (see Instruction.normalize/2 for details):
    • Single action module
    • Single action tuple {module, params}
    • List of action modules
    • List of {action_module, params} tuples
    • Mixed list of modules and tuples (e.g. [ValidateAction, {ProcessAction, %{file: "data.csv"}}])
  • context - Optional map of context data to include in instructions (default: %{})

Planning Process

  1. Normalizes instructions into consistent [{module, params}] format
  2. Validates action registration
  3. Builds Instruction structs with params and provided context
  4. Executes on_before_plan callback
  5. Adds instructions to pending queue
  6. Sets dirty_state? flag

Returns

  • {:ok, updated_agent} - Agent with updated pending_instructions and dirty_state?
  • {:error, reason} - Planning failed

Examples

# Plan single action
{:ok, agent} = MyAgent.plan(agent, ProcessAction)

# Plan single action with params and context
{:ok, agent} = MyAgent.plan(agent, {ProcessAction, %{file: "data.csv"}}, %{user_id: "123"})

# Plan multiple actions with shared context
{:ok, agent} = MyAgent.plan(agent, [
  ValidateAction,
  {ProcessAction, %{file: "data.csv"}},
  {SaveAction, %{path: "/tmp"}}
], %{request_id: "abc123"})

# Validation failures
{:error, reason} = MyAgent.plan(agent, UnregisteredAction)
{:error, reason} = MyAgent.plan(agent, [{InvalidAction, %{}}])

Error Handling

  • Unregistered actions return execution error with affected action details
  • Invalid instruction format returns error with expected format details
  • Failed callbacks return error with callback context
  • All errors include agent_id and relevant debugging information

See registered_actions/1 for checking available actions and run/2 for executing planned actions.

register_action(agent, action_module)

@spec register_action(t(), module()) :: agent_result()

Registers a new action module with the Agent at server.

The action module must implement the Jido.Action behavior and will be validated before registration.

Parameters

  • agent - The Agent struct to update
  • action_module - The action module to register

Returns

  • {:ok, updated_agent} - Action successfully registered
  • {:error, term()} - Registration failed (invalid module)

Example

{:ok, agent} = MyAgent.register_action(agent, MyApp.Actions.NewAction)

registered_actions(agent)

@spec registered_actions(t()) :: [module()] | []

Returns all action modules currently registered with the Agent.

This includes both compile-time configured actions and server registered ones. Actions are returned in registration order (most recently registered first).

Parameters

  • agent - The Agent struct to inspect

Returns

  • [module()] - List of registered action modules, empty if none

Example

actions = MyAgent.registered_actions(agent)
# Returns: [MyAction1, MyAction2]

reset(agent)

@spec reset(t()) :: agent_result()

Resets the agent's pending action queue.

Parameters

  • agent: The agent struct to reset

Returns

  • {:ok, updated_agent} - Queue was reset successfully

run(agent, opts \\ [])

@spec run(
  t() | Jido.server(),
  keyword()
) :: agent_result_with_directives()

Executes pending instructions in the agent's queue through a multi-phase process.

Instructions are executed with the help of a runner. Review Jido.Runner for more information.

Each phase can modify the agent's state and trigger callbacks.

Execution Flow

  1. Pre-execution callback (on_before_run/1)
  2. Runner execution of pending instructions
  3. Post-execution callback (on_after_run/3)
  4. Return agent with updated state and result

Parameters

  • agent - The agent struct containing pending instructions
  • opts - Keyword list of options:
    • :runner - Module implementing the Runner behavior (default: agent's configured runner)

State Management

State modifications are handled through StateModification directives:

  • :set - Set a value at a path: %StateModification{op: :set, path: [:config, :mode], value: :active}
  • :update - Update with function: %StateModification{op: :update, path: [:counter], value: &(&1 + 1)}
  • :delete - Remove value at path: %StateModification{op: :delete, path: [:temp_data]}
  • :reset - Set path to nil: %StateModification{op: :reset, path: [:cache]}

Directives

  • Directives are applied after runner execution
  • Directives can modify agent state and result, such as adding or removing actions or enqueuing new instructions
  • Review Jido.Agent.Directive for more information

Returns

  • {:ok, updated_agent, directives} - Execution completed successfully
  • {:error, %Error{}} - Execution failed with specific error type:
    • :execution_error - Runner execution failed
    • Any other error wrapped as execution_error

Examples

# Set up your agent, register and plan some actions to fill the instruction queue
agent = MyAgent.new()
{:ok, agent} = MyAgent.plan(agent, [BasicAction, {NoSchema, %{value: 2}}])

# Basic execution with state modification directives
{:ok, agent, directives} = MyAgent.run(agent)

# Using custom runner
{:ok, agent, directives} = MyAgent.run(agent, runner: CustomRunner)

# Error handling
case MyAgent.run(agent) do
  {:ok, agent, directives} ->
    # Success - state updated through directives
    agent.state

  {:error, %Error{type: :validation_error}} ->
    # Handle validation failure

  {:error, %Error{type: :execution_error}} ->
    # Handle execution failure
end

Callbacks

See Jido.Runner for implementing custom runners and plan/2 for queueing actions.

runner()

schema()

set(agent, attrs, opts \\ [])

@spec set(t() | Jido.server(), keyword() | map(), keyword()) :: agent_result()

Updates the agent's state by deep merging the provided attributes. The update process:

  1. Deep merges new attributes with existing state
  2. Validates the merged state against schema
  3. Sets dirty_state? flag for tracking changes
  4. Triggers validation callbacks

Parameters

  • agent - The agent struct to update
  • attrs - Map or keyword list of attributes to merge into state
  • opts - Optional keyword list of options:
    • strict_validation - Boolean, whether to perform strict validation (default: false)

Returns

  • {:ok, updated_agent} - Agent with merged state and dirty_state? = true
  • {:error, reason} - If validation fails or callbacks return error

State Management

  • Empty updates return success without changes
  • Updates trigger on_before_validate_state and on_after_validate_state callbacks
  • Unknown fields are preserved during deep merge
  • Nested updates are supported via deep merging

Field Validation

Only fields defined in the schema are validated. Unknown fields are preserved during deep merge.

Examples

# Simple update
{:ok, agent} = MyAgent.set(agent, status: :running)

# Deep merge update
{:ok, agent} = MyAgent.set(agent, %{
  config: %{retries: 3},
  metadata: %{started_at: DateTime.utc_now()}
})

# Validation failure
{:error, "Invalid status value"} = MyAgent.set(agent, status: :invalid)

See validate/1 for validation details and Jido.Agent callbacks for lifecycle hooks.

shutdown(state, reason)

@spec shutdown(Jido.Agent.Server.State.t(), reason :: any()) :: agent_result()

Callback implementation for Jido.Agent.shutdown/2.

tags()

to_json()

tool_response(pid, message)

transform_result(signal, result, agent)

@spec transform_result(Jido.Signal.t(), term() | {:ok, term()} | {:error, any()}, t()) ::
  {:ok, term()} | {:error, any()}

Callback implementation for Jido.Agent.transform_result/3.

validate(agent, opts \\ [])

@spec validate(
  t() | Jido.server(),
  keyword()
) :: agent_result()

Validates the agent's state through a three-phase process:

  1. Executes pre-validation callback (on_before_validate_state/1)
  2. Validates known fields against schema using NimbleOptions
  3. Executes post-validation callback (on_after_validate_state/1)

Validation Process

  • Only schema-defined fields are validated
  • Unknown fields are preserved unchanged
  • NimbleOptions performs type and constraint checking

Parameters

  • agent - The agent struct to validate
  • opts - Optional keyword list of options:
    • strict_validation - Boolean, whether to perform strict validation (default: false)

Returns

  • {:ok, validated_agent} - Agent with validated state
  • {:error, reason} - Validation failed with reason

Examples

# Successful validation with schema
defmodule MyAgent do
  use Jido.Agent,
    name: "my_agent",
    schema: [
      status: [type: :atom, values: [:pending, :running]],
      retries: [type: :integer, minimum: 0]
    ]

  # Optional validation hooks
  def on_before_validate_state(agent) do
    # Pre-validation logic
    {:ok, agent}
  end
end

{:ok, agent} = MyAgent.validate(agent)

# Failed validation
{:error, "Invalid status value"} = MyAgent.validate(%{
  agent | state: %{status: :invalid}
})

# Unknown fields preserved
{:ok, agent} = MyAgent.validate(%{
  agent | state: %{status: :pending, custom_field: "preserved"}
})

Validation Flow

  1. on_before_validate_state - Preprocess state
  2. Schema validation via NimbleOptions
  3. on_after_validate_state - Postprocess validated state

See NimbleOptions documentation for supported validation rules.

vsn()