A delightful provider agnostic Ruby SDK for building multi-agent AI workflows with seamless handoffs tool calling, and shared context.
- π€ Multi-Agent Orchestration: Create specialized AI agents that work together
- π Seamless Handoffs: Transparent agent-to-agent transfers (users never know!)
- π οΈ Tool Integration: Agents can use custom tools and functions
- π Structured Output: JSON schema-validated responses for reliable data extraction
- πΎ Shared Context: State management across agent interactions
- π Provider Agnostic: Works with OpenAI, Anthropic, and other LLM providers
Add to your Gemfile:
gem 'ai-agents'require 'agents'
# Configure with your API key
Agents.configure do |config|
config.openai_api_key = ENV['OPENAI_API_KEY']
end
# Create agents
weather_agent = Agents::Agent.new(
name: "Weather Assistant",
instructions: "Help users get weather information",
tools: [WeatherTool.new]
)
# Create a thread-safe runner (reusable across conversations)
runner = Agents::Runner.with_agents(weather_agent)
# Use the runner for conversations
result = runner.run("What's the weather like today?")
puts result.outputThe real power comes from multi-agent workflows with automatic handoffs:
# Create specialized agents
triage = Agents::Agent.new(
name: "Triage Agent",
instructions: "Route customers to the right specialist"
)
sales = Agents::Agent.new(
name: "Sales Agent",
instructions: "Answer details about plans",
tools: [CreateLeadTool.new, CRMLookupTool.new]
)
support = Agents::Agent.new(
name: "Support Agent",
instructions: "Handle account realted and technical issues",
tools: [FaqLookupTool.new, TicketTool.new]
)
# Wire up handoff relationships - clean and simple!
triage.register_handoffs(sales, support)
sales.register_handoffs(triage) # Can route back to triage
support.register_handoffs(triage) # Hub-and-spoke pattern
# Create runner with all agents (triage is default entry point)
runner = Agents::Runner.with_agents(triage, sales, support)
# Run conversations with automatic handoffs and persistence
result = runner.run("Do you have special plans for businesses?")
# User gets direct answer from sales agent without knowing about the handoff!
# Continue the conversation seamlessly
result = runner.run("What is the pricing for the premium fibre plan?", context: result.context)
# Context automatically tracks conversation history and current agent- Agent: Individual AI assistants configured with specific instructions, tools, and handoff relationships. Agents are immutable and thread-safe.
- AgentRunner: Thread-safe execution manager that coordinates multi-agent conversations. Create once and reuse across multiple threads safely.
- Runner: Internal orchestrator that handles individual conversation turns and manages the execution loop (used internally by AgentRunner).
- Context & State: Shared conversation state that persists across agent handoffs. Fully serializable for database storage and session management.
- Tools: Custom functions that agents can execute to interact with external systems (APIs, databases, etc.).
- Handoffs: Automatic transfers between specialized agents based on conversation context, completely transparent to users.
# Create agents as instances
agent = Agents::Agent.new(
name: "Customer Service",
instructions: "You are a helpful customer service agent.",
model: "gpt-4o",
tools: [EmailTool.new, TicketTool.new]
)
# Register handoffs after creation
agent.register_handoffs(technical_support, billing)class EmailTool < Agents::Tool
description "Send emails to customers"
param :to, type: "string", desc: "Email address"
param :subject, type: "string", desc: "Email subject"
param :body, type: "string", desc: "Email body"
def perform(tool_context, to:, subject:, body:)
# Send email logic here
"Email sent to #{to}"
end
end# Central triage agent routes to specialists (hub-and-spoke pattern)
triage = Agents::Agent.new(name: "Triage")
billing = Agents::Agent.new(name: "Billing")
support = Agents::Agent.new(name: "Support")
# Triage can route to any specialist
triage.register_handoffs(billing, support)
# Specialists only route back to triage
billing.register_handoffs(triage)
support.register_handoffs(triage)# Context is automatically managed and serializable
runner = Agents::Runner.with_agents(triage, billing, support)
# Start a conversation
result = runner.run("I need billing help")
# Context is automatically updated with conversation history and current agent
context = result.context
puts context[:conversation_history]
puts context[:current_agent] # Agent name (string), not object!
# Serialize context for persistence (Rails, databases, etc.)
json_context = JSON.dump(context)
# Later: restore and continue conversation
restored_context = JSON.parse(json_context, symbolize_names: true)
result = runner.run("Actually, I need technical support too", context: restored_context)
# System automatically determines correct agent from conversation historyCheck out the examples/ folder for complete working demos showcasing multi-agent workflows.
# Run the ISP support demo
ruby examples/isp-support/interactive.rbAgents.configure do |config|
# Provider API keys
config.openai_api_key = ENV['OPENAI_API_KEY']
config.azure_api_base = ENV['AZURE_API_BASE']
config.azure_api_key = ENV['AZURE_API_KEY']
config.anthropic_api_key = ENV['ANTHROPIC_API_KEY']
config.gemini_api_key = ENV['GEMINI_API_KEY']
# Defaults
config.default_model = 'gpt-4o'
# Performance
config.request_timeout = 120
# Debugging
config.debug = true
endAgents.configure do |config|
config.azure_api_base = ENV["AZURE_API_BASE"]
config.azure_api_key = ENV["AZURE_API_KEY"]
end
agent = Agents::Agent.new(
name: "Support",
model: "my-azure-deployment",
provider: :azure,
assume_model_exists: true
)provider is optional for known, unambiguous registry models. Set it for custom deployment names and for model IDs that can exist under multiple providers, such as Azure and OpenAI deployments.
Optional OpenTelemetry instrumentation for tracing agent execution, compatible with Langfuse and other OTel backends.
require 'agents/instrumentation'
tracer = OpenTelemetry.tracer_provider.tracer('my-app')
runner = Agents::Runner.with_agents(triage, billing, support)
Agents::Instrumentation.install(runner, tracer: tracer)See the Instrumentation Guide for setup details.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Run tests (
rake test) - Run linter (
rake rubocop) - Commit your changes (
git commit -am 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
- Inspired by OpenAI's Agents SDK
- Built on top of RubyLLM for LLM integration
