How to Configure Signal Routes for Jido Agents: Complete Routing Guide

You configure signal routes for Jido agents by declaring static {type, ActionModule} tuples via the signal_routes: option or implementing the signal_routes/1 callback in your agent module, with the framework's router resolving signals at runtime through a priority-based system that evaluates strategy, agent, and plugin definitions in descending order of precedence.

Jido is an Elixir framework for building autonomous agents that process CloudEvents-style signals and route them to specific actions. Understanding how to configure these signal routes is essential for controlling agent behavior, as the routing table determines which action module executes when an agent receives a signal of a particular type.

Understanding the Signal Router Priority System

The Signal Router (implemented in Jido.AgentServer) constructs a routing table by merging definitions from three sources. When a signal arrives, the router matches its type field against this table and executes the corresponding action. The framework resolves conflicts through a strict priority hierarchy:

  • Strategy routes (Priority 50+): Custom strategy modules can override all other routes.
  • Agent routes (Priority 0): Static declarations or dynamic callbacks defined directly in the agent.
  • Plugin routes (Priority -10): Pattern-based routes exposed by plugins.

Higher priority routes take precedence. If no match exists at any priority level, the signal is ignored.

Strategy Routes

Custom strategies implement the signal_routes/1 callback to inject their own routing logic. According to the source in lib/jido/agent/strategy.ex (lines 296-301), these routes automatically receive priority 50 or higher, allowing strategies to intercept signals before they reach the agent's own handlers.

Agent Routes

Every agent module receives default signal_routes/0 and signal_routes/1 callbacks injected automatically by use Jido.Agent. The source in lib/jido/agent.ex (lines 36-42) provides these defaults, but you can override them through two mechanisms:

  1. Static declaration: Pass a list of tuples to the signal_routes: option in use Jido.Agent
  2. Dynamic routing: Override def signal_routes(ctx) to compute routes at runtime based on state or configuration

Plugin Routes

Plugins contribute routes through the signal_patterns attribute and an optional signal_routes/1 callback defined in lib/jido/plugin.ex (lines 10-13). These typically use wildcard matching and evaluate last in the priority chain.

Configuring Static Signal Routes

The simplest approach defines routes at compile time using the signal_routes: option. This method is ideal for agents with fixed behavior patterns that do not change based on runtime conditions.

defmodule MyApp.CounterAgent do
  use Jido.Agent,
    name: "counter",
    schema: [counter: [type: :integer, default: 0]],
    signal_routes: [
      {"increment", MyApp.Actions.Increment},
      {"decrement", MyApp.Actions.Decrement},
      {"reset",     MyApp.Actions.Reset}
    ]
end

This example mirrors the Agent Signal Routes section of the official guide (lines 99-107 in guides/signals.md). Each tuple maps a signal type string to a module that use Jido.Action and implements the run/2 function.

Implementing Dynamic Signal Routes

For agents that must adapt their behavior based on configuration, environment variables, or internal state, override the signal_routes/1 callback. This function receives the agent's context (ctx) and returns a list of route tuples computed at runtime.

defmodule MyApp.ConfigurableAgent do
  use Jido.Agent,
    name: "configurable",
    schema: [mode: [type: :atom, default: :default]]

  @impl true
  def signal_routes(_ctx) do
    case Application.get_env(:my_app, :feature_flag) do
      true  -> [{"feature", MyApp.Actions.FeatureEnabled}]
      false -> [{"feature", MyApp.Actions.FeatureDisabled}]
    end
  end
end

Dynamic routing enables feature flags, A/B testing, and environment-specific behavior without recompiling the agent module.

Strategy-Based Signal Routing

When an agent runs with a custom strategy, that strategy's routes take precedence over all others. Strategies implement signal_routes/1 to handle protocol-specific signals or translate external events into internal action dispatches.

defmodule MyApp.ReactiveStrategy do
  use Jido.Agent.Strategy

  @impl true
  def signal_routes(_ctx) do
    [
      {"react.user_query", {:strategy_cmd, :react_start}},
      {"ai.llm_result",   {:strategy_cmd, :react_llm_result}}
    ]
  end
end

With priority 50+, these routes intercept signals before they reach the agent's static or dynamic route definitions, making strategies ideal for implementing reusable interaction patterns across multiple agent types.

Complete End-to-End Example

The following example demonstrates the full workflow: defining an action, configuring an agent with static routes, starting the server, and dispatching a signal. This pattern is validated in the test suite at test/examples/signals/signal_routing_test.exs (lines 88-94).


# Define an action

defmodule MyApp.Actions.Increment do
  use Jido.Action,
    name: "increment",
    schema: [amount: [type: :integer, default: 1]]

  def run(%{amount: amt}, ctx) do
    current = Map.get(ctx.state, :counter, 0)
    {:ok, %{counter: current + amt}}
  end
end

# Agent with static routes

defmodule MyApp.CounterAgent do
  use Jido.Agent,
    name: "counter",
    schema: [counter: [type: :integer, default: 0]],
    signal_routes: [{"increment", MyApp.Actions.Increment}]
end

# Start the agent

{:ok, pid} = Jido.AgentServer.start_link(agent: MyApp.CounterAgent, id: "counter-1")

# Send a signal

signal = Jido.Signal.new!("increment", %{amount: 10}, source: "/ui")
{:ok, agent} = Jido.AgentServer.call(pid, signal)

IO.inspect(agent.state.counter)   # => 10

The Jido.AgentServer.call/2 function processes the signal synchronously, executing the matched action and returning the updated agent state. For asynchronous processing, use cast/2 instead.

Summary

  • Signal routes map incoming CloudEvents-style messages to specific action modules based on the signal's type field.
  • The router evaluates three sources in priority order: Strategy (50+) > Agent (0) > Plugin (-10).
  • Configure routes statically via the signal_routes: option in use Jido.Agent or dynamically by implementing the signal_routes/1 callback.
  • Routes are merged at runtime by Jido.AgentServer, which resolves the routing table before executing the matched action.
  • Source definitions reside in lib/jido/agent.ex (default callbacks), lib/jido/agent/strategy.ex (strategy behavior), and lib/jido/plugin.ex (plugin contributions).

Frequently Asked Questions

How does the Jido router resolve conflicting signal routes?

The router assigns a numeric priority to each route source. Strategy routes receive priority 50 or higher, agent routes receive priority 0, and plugin routes receive priority -10. When multiple routes match a signal's type, the highest priority route wins. This ensures that strategies can override agent defaults, and agents can override plugin patterns.

Can I change signal routes at runtime?

Yes. While static routes defined via the signal_routes: option are fixed at compile time, implementing the signal_routes/1 callback allows dynamic route computation on every signal evaluation. The callback receives the current agent context, enabling routes to change based on state, configuration, or external conditions without restarting the agent.

What is the difference between signal_routes/0 and signal_routes/1?

signal_routes/0 is a simple callback that takes no arguments and returns the route list, suitable for static configurations. signal_routes/1 receives the agent context (ctx) and allows runtime computation of routes. The use Jido.Agent macro injects default implementations of both callbacks (lines 36-42 in lib/jido/agent.ex), which you can override as needed.

How do plugins contribute to signal routing?

Plugins define routes through the signal_patterns module attribute and optionally implement signal_routes/1 as shown in lib/jido/plugin.ex (lines 10-13). These routes evaluate at priority -10, making them a fallback when no strategy or agent route matches. Plugins typically use this mechanism to provide wildcard pattern matching for cross-cutting concerns like logging or telemetry.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →