Module: ActiveAgent::Delegation

Extended by:
ActiveSupport::Concern
Included in:
Base
Defined in:
lib/active_agent/concerns/delegation.rb,
lib/active_agent/delegation/budget.rb,
lib/active_agent/delegation/ledger.rb,
lib/active_agent/delegation/runner.rb,
lib/active_agent/delegation/schema.rb,
lib/active_agent/delegation/backend.rb,
lib/active_agent/delegation/pricing.rb,
lib/active_agent/delegation/contract.rb,
lib/active_agent/delegation/definition.rb

Overview

Agent-as-tool delegation: hand part of a job to another agent.

A tool is a Ruby method the model can call. A delegation is another agent the model can call — same mechanism, but the callee has its own instructions, its own templates, its own model, and its own budget. That separation is what makes the pattern worth having as a primitive: a specialist agent stays specialist, and the generalist orchestrating it never inherits its prompt.

Delegation has three moving parts, each declared where it belongs:

  • The contract lives on the sub-agent, next to the action it describes. Callers say which agent they want; they never restate its parameters.
  • The budget lives at the call site, because only the caller knows what the work is worth. Exceeding it returns a structured result the model can reason about, not an exception that ends the conversation.
  • The backend lives at the call site too, so the same sub-agent can run on a cheap local model in one parent and a frontier model in another without either agent's code changing.

Examples:

Declaring a sub-agent's contract

class SummarizerAgent < ApplicationAgent
  generate_with :openai, model: "gpt-4o-mini"

  delegation :summarize, description: "Condense a document into key points" do
    string  :text, required: true, description: "Full document text"
    integer :limit, description: "Maximum number of key points to return"

    returns do
      string :summary, required: true, description: "One-paragraph summary"
      array  :points, of: :string, required: true, description: "The key points"
    end
  end

  def summarize(text:, limit: 5)
    prompt(message: text, limit: limit)
  end
end

Delegating to it

class ResearchAgent < ApplicationAgent
  generate_with :openai, model: "gpt-4o"

  delegation_budget max_calls: 8, max_duration: 60

  delegate_to SummarizerAgent, budget: { max_calls: 3, timeout: 20 }
  delegate_to FactCheckAgent, as: :verify, backend: { provider: :anthropic, model: "claude-haiku-4-5" }

  def research(topic:)
    prompt(message: "Research #{topic}. Summarize sources before citing them.")
  end
end

See Also:

Defined Under Namespace

Modules: Pricing Classes: Backend, Budget, BudgetExceededError, Contract, Definition, InvalidResultError, Ledger, Runner, Schema, TimeoutError

Instance Method Summary collapse

Instance Method Details

#delegation_ledgerDelegation::Ledger

Ledger covering every delegation made during this generation.

Returns:



333
334
335
# File 'lib/active_agent/concerns/delegation.rb', line 333

def delegation_ledger
  @_delegation_ledger ||= Delegation::Ledger.new
end

#delegation_ledger_for(tool_name) ⇒ Delegation::Ledger

Ledger for a single delegation during this generation.

Parameters:

  • tool_name (Symbol, String)

Returns:



341
342
343
# File 'lib/active_agent/concerns/delegation.rb', line 341

def delegation_ledger_for(tool_name)
  delegation_ledgers[tool_name.to_sym] ||= Delegation::Ledger.new
end

#delegation_ledgersHash{Symbol => Delegation::Ledger}

Returns per-delegation ledgers.

Returns:



346
347
348
# File 'lib/active_agent/concerns/delegation.rb', line 346

def delegation_ledgers
  @_delegation_ledgers ||= {}
end

#perform_delegation(tool_name, **arguments) ⇒ Object

Runs a delegated call. Providers reach this through the tool method ClassMethods#delegate_to defines.

Parameters:

  • tool_name (Symbol, String)
  • arguments (Hash)

Returns:

  • (Object)

Raises:

  • (ArgumentError)


356
357
358
359
360
361
# File 'lib/active_agent/concerns/delegation.rb', line 356

def perform_delegation(tool_name, **arguments)
  definition = self.class.delegations[tool_name.to_sym]
  raise ArgumentError, "#{self.class} has no delegation named #{tool_name}" unless definition

  Delegation::Runner.new(definition, owner: self).call(**arguments)
end