Jido.Skill behaviour (Jido v1.1.0-rc.2)

View Source

Defines the core behavior and structure for Jido Skills, the fundamental building blocks of agent capabilities in the Jido framework.

Overview

Skills encapsulate discrete sets of functionality that agents can use to accomplish tasks. Think of them as feature packs that give agents new abilities - similar to how a person might learn skills like "cooking" or "programming". Each Skill provides:

  • Signal routing and handling patterns
  • Isolated state management
  • Process supervision
  • Configuration validation
  • Runtime adaptation

Core Concepts

State Isolation

Skills use schema-based state isolation to prevent different capabilities from interfering with each other. Each skill defines:

  • A unique opts_key for namespace isolation
  • Validation rules for configuration

Signal Patterns

Skills define what signals they can handle through pattern matching:

use Jido.Skill,
  name: "weather_monitor",
  signal_patterns: [
    "weather.data.*",
    "weather.alert.**"
  ]

Pattern rules:

  • Exact matches: "user.created"
  • Single wildcards: "user.*.updated"
  • Multi-wildcards: "audit.**"

Configuration Management

Skills provide schema-based config validation:

config: [
  api_key: [
    type: :string,
    required: true,
    doc: "API key for weather service"
  ],
  update_interval: [
    type: :pos_integer,
    default: 60_000,
    doc: "Update interval in milliseconds"
  ]
]

Process Supervision

Skills can define child processes through the child_spec/1 callback:

def child_spec(config) do
  [
    {WeatherAPI.Client, config.api_key},
    {MetricsCollector, name: config.metrics_name}
  ]
end

Usage Example

Here's a complete skill example:

defmodule MyApp.WeatherSkill do
  use Jido.Skill,
    name: "weather_monitor",
    description: "Monitors weather conditions and generates alerts",
    category: "monitoring",
    tags: ["weather", "alerts"],
    vsn: "1.0.0",
    opts_key: :weather,
    signal_patterns: [
      "weather.data.*",
      "weather.alert.**"
    ],
    config: [
      api_key: [type: :string, required: true]
    ]

  def child_spec(config) do
    [
      {WeatherAPI.Client, config.api_key}
    ]
  end

  def handle_signal(%Signal{type: "weather.data.updated"} = signal, _skill) do
    # Handle weather updates
    {:ok, signal}
  end

  def transform_result(%Signal{} = signal, result, _skill) do
    # Transform the result
    {:ok, result}
  end
end

Callbacks

Skills implement these callbacks:

  • child_spec/1 - Returns child process specifications
  • router/0 - Returns signal routing rules
  • handle_signal/2 - Processes incoming signals
  • transform_result/3 - Post-processes signal handling results
  • mount/2 - Mounts the skill to an agent

Behavior

The Skill behavior enforces a consistent interface:

@callback child_spec(config :: map()) :: Supervisor.child_spec() | [Supervisor.child_spec()]
@callback router() :: [map()]
@callback handle_signal(signal :: Signal.t(), skill :: t()) :: {:ok, Signal.t()} | {:error, term()}
@callback transform_result(signal :: Signal.t(), result :: term(), skill :: t()) ::
            {:ok, term()} | {:error, term()}
@callback mount(agent :: Jido.Agent.t(), opts :: keyword()) :: Jido.Agent.t()

Configuration

Skills validate their configuration at compile time using these fields:

  • name - Unique identifier (required)
  • description - Human-readable explanation
  • category - Broad classification
  • tags - List of searchable tags
  • vsn - Version string
  • opts_key - State namespace key
  • signal_patterns - Input/output patterns
  • opts_schema - Configuration schema

Best Practices

  1. State Isolation

    • Use meaningful opts_key names
    • Keep state focused and minimal
    • Document state structure
  2. Signal Design

    • Use consistent naming patterns
    • Document signal formats
    • Consider routing efficiency
  3. Configuration

    • Validate thoroughly
    • Provide good defaults
    • Document all options
  4. Process Management

    • Supervise child processes
    • Handle crashes gracefully
    • Monitor resource usage

See Also

Summary

Types

t()

Represents a skill's core structure and metadata.

Functions

Implements the skill behavior and configuration validation.

Gets a skill's configuration schema.

Skills must be defined at compile time, not runtime.

Validates a skill's configuration against its schema.

Types

t()

@type t() :: %Jido.Skill{
  category: String.t() | nil,
  description: String.t() | nil,
  name: String.t(),
  opts_key: atom() | nil,
  opts_schema: map() | nil,
  signal_patterns: [String.t()],
  tags: [String.t()],
  vsn: String.t() | nil
}

Represents a skill's core structure and metadata.

Fields:

  • name: Unique identifier for the skill
  • description: Human-readable explanation of purpose
  • category: Broad classification for organization
  • tags: List of searchable tags
  • vsn: Version string for compatibility
  • opts_key: Atom key for state namespace
  • signal_patterns: Input/output signal patterns
  • opts_schema: Configuration schema

Callbacks

child_spec(config)

@callback child_spec(config :: map()) ::
  Supervisor.child_spec() | [Supervisor.child_spec()]

handle_signal(signal, skill)

@callback handle_signal(signal :: Jido.Signal.t(), skill :: t()) ::
  {:ok, Jido.Signal.t()} | {:error, term()}

mount(agent, opts)

@callback mount(agent :: Jido.Agent.t(), opts :: keyword()) ::
  {:ok, Jido.Agent.t()} | {:error, Jido.Error.t()}

router(skill_opts)

@callback router(skill_opts :: keyword()) :: [Route.t()]

transform_result(signal, result, skill)

@callback transform_result(signal :: Jido.Signal.t(), result :: term(), skill :: t()) ::
  {:ok, term()} | {:error, any()}

Functions

__using__(opts)

(macro)

Implements the skill behavior and configuration validation.

This macro:

  1. Validates configuration at compile time
  2. Defines metadata accessors
  3. Provides JSON serialization
  4. Sets up default implementations

Example

defmodule MySkill do
  use Jido.Skill,
    name: "my_skill",
    opts_key: :my_skill,
    signals: [
      input: ["my.event.*"],
      output: ["my.result.*"]
    ]
end

get_opts_schema(skill_module)

@spec get_opts_schema(module()) :: {:ok, map()} | {:error, Jido.Error.t()}

Gets a skill's configuration schema.

Parameters

  • skill_module: The skill module to inspect

Returns

  • {:ok, schema}: The skill's config schema
  • {:error, reason}: Schema not found

Example

Skill.get_config_schema(WeatherSkill)

new()

@spec new() :: {:error, Jido.Error.t()}

Skills must be defined at compile time, not runtime.

This function always returns an error to enforce compile-time definition.

validate_opts(skill_module, config)

@spec validate_opts(module(), map()) :: {:ok, map()} | {:error, Jido.Error.t()}

Validates a skill's configuration against its schema.

Parameters

  • skill_module: The skill module to validate
  • config: Configuration map to validate

Returns

  • {:ok, validated_config}: Successfully validated config
  • {:error, reason}: Validation failed

Example

Skill.validate_opts(WeatherSkill, %{
  api_key: "abc123",
  interval: 1000
})