Understanding Jido Runners
View SourceRunners are the execution engines that power Jido's agent system, responsible for processing instructions and managing state transitions. This guide explores both built-in runners and how to create custom implementations.
Core Concepts
Runners serve as the bridge between agent instructions and their execution. They handle:
- Instruction processing
- State management
- Directive handling
- Error recovery
- Context propagation
Built-in Runners
Jido provides two built-in runners optimized for different use cases:
Simple Runner
The Simple Runner processes one instruction at a time, providing atomic execution and clear state transitions.
defmodule MyAgent do
use Jido.Agent,
name: "simple_example",
runner: Jido.Runner.Simple
end
Key Features
- Single instruction execution
- Atomic state updates
- Clear error boundaries
- Predictable behavior
State Flow
# Simple Runner execution flow
{:ok, agent, directives} = Jido.Runner.Simple.run(agent, opts)
# Internal process:
# 1. Dequeue single instruction
# 2. Execute via action module
# 3. Update state atomically
# 4. Process any directives
# 5. Return updated agent
Chain Runner
The Chain Runner enables sequential execution of multiple instructions with state flowing between steps.
defmodule MyAgent do
use Jido.Agent,
name: "chain_example",
runner: Jido.Runner.Chain
end
Key Features
- Sequential instruction processing
- State flows between steps
- Directive accumulation
- Comprehensive error handling
State Flow
# Chain Runner execution flow
{:ok, agent, directives} = Jido.Runner.Chain.run(agent, opts)
# Internal process:
# 1. Convert queue to instruction list
# 2. Execute instructions sequentially
# 3. Flow state between steps
# 4. Accumulate directives
# 5. Apply final state
Context and State Management
Both runners handle agent state through context propagation:
# State is automatically included in context
def run(%{action: action, params: params, context: context}, _opts) do
# Context includes agent state
enhanced_context = Map.put(context, :state, agent.state)
case Jido.Workflow.run(action, params, enhanced_context) do
{:ok, result} -> handle_success(result)
{:error, reason} -> handle_error(reason)
end
end
Directive Processing
Runners handle directives that modify agent behavior:
# Example directive processing
defp handle_directive_result(agent, state_map, directives) do
case Directive.apply_agent_directive(agent, directives) do
{:ok, updated_agent, server_directives} ->
{:ok, updated_agent, server_directives}
{:error, reason} ->
{:error, Error.validation_error("Invalid directive", reason)}
end
end
Creating Custom Runners
Implement the Jido.Runner
behavior to create custom runners:
defmodule MyCustomRunner do
@behaviour Jido.Runner
@impl true
def run(agent, opts \\ []) do
# Custom execution logic here
# Must return {:ok, updated_agent, directives} | {:error, reason}
end
# Helper functions
defp process_instructions(instructions, agent) do
# Custom instruction processing
end
defp update_agent_state(agent, result) do
# Custom state update logic
end
end
Implementation Guidelines
State Management
- Handle state updates atomically
- Preserve agent state on errors
- Validate state transitions
Error Handling
- Implement comprehensive error handling
- Provide clear error messages
- Consider retry strategies
Directive Support
- Process directives consistently
- Validate directive types
- Handle directive errors gracefully
Performance
- Consider concurrency implications
- Optimize for your use case
- Handle resource cleanup
Integration with Agents
Runners are automatically integrated into Agents when specified in the configuration. This means you typically won't interact with runners directly, but rather through the Agent interface:
defmodule MyAgent do
use Jido.Agent,
name: "example_agent",
runner: Jido.Runner.Chain # Runner is automatically integrated
# All runner operations are handled internally
def process_workflow(data) do
enqueue(ProcessData, %{input: data})
end
end
This guide serves primarily as a reference for understanding runner behavior and creating custom implementations when needed.
Best Practices
Choose the Right Runner
- Use Simple Runner for atomic operations
- Use Chain Runner for complex workflows
- Create custom runners for specific needs
State Management
- Keep state transitions explicit
- Validate state changes
- Handle edge cases
Error Handling
- Implement comprehensive error handling
- Provide clear error messages
- Consider recovery strategies
Testing
- Test happy paths thoroughly
- Test error conditions
- Test state transitions
- Test directive handling
Next Steps
- Explore the source code of built-in runners
- Implement custom runners for specific needs
- Contribute improvements to the community
Remember that runners are a critical part of your agent system. Choose and implement them carefully based on your specific requirements.