Class: Ask::Agent::SubAgent

Inherits:
Object
  • Object
show all
Defined in:
lib/ask/agent/sub_agent.rb

Overview

A tool that delegates a task to a specialized sub-agent.

The coordinator agent sees this as a regular tool — when the LLM calls it, a fresh sub-agent session runs independently with its own model, tools, and instructions, and returns the result.

Satisfies the tool duck type (name, description, params_schema, call) so it can be passed directly in the tools array.

Examples:

From a filesystem definition

# agents/web_search/agent.rb defines a WebSearch agent.
# Use it as a sub-agent by passing its name:

search = Ask::Agent::SubAgent.new("web_search")

coordinator = Ask::Agent::Session.new(
  model: "gpt-4o",
  tools: [search, Ask::Tools::Shell::Bash]
)

Inline configuration

search = Ask::Agent::SubAgent.new(
  name: "web_search",
  description: "Search the web for current information",
  model: "gpt-4o-mini",
  tools: [Ask::Tools::WebSearch],
  system_prompt: "You are a research assistant."
)

Using with a different provider

review = Ask::Agent::SubAgent.new(
  name: "code_review",
  model: "claude-sonnet-4",
  provider: :anthropic,
  tools: [Ask::Tools::Shell::Read, Ask::Tools::Shell::Grep],
  system_prompt: "You are a senior code reviewer."
)

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(definition_name) ⇒ SubAgent #initialize(name:, description: nil, model:, tools: []) ⇒ SubAgent

Create a new SubAgent tool.

When given a String, looks up a filesystem agent definition by name (matching the convention used by Ask::Agent.new). Model, tools, instructions, and other settings are read from the definition files.

When given keyword arguments, configures the sub-agent inline.

@param name [String] Tool name (e.g. "web_search").
@param description [String, nil] Tool description. Auto-generated
from the model and tools count if not provided.
@param model [String] Model identifier for the sub-agent session.
@param tools [Array<Class, Object>] Tools available to the sub-agent.
@param system_prompt [String, nil] Instructions for the sub-agent.
@param provider [Symbol, nil] Provider override.
@param max_turns [Integer] Max conversation turns for the sub-agent.
@param session_opts [Hash] Additional options forwarded to Session.new.

Overloads:

  • #initialize(definition_name) ⇒ SubAgent

    Parameters:

    • definition_name (String)

      Name of a filesystem agent definition.

    Raises:

  • #initialize(name:, description: nil, model:, tools: []) ⇒ SubAgent

    system_prompt: nil, provider: nil, max_turns: 10, **session_opts)



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/ask/agent/sub_agent.rb', line 72

def initialize(definition_name = nil, name: nil, description: nil, model: nil,
               tools: [], system_prompt: nil, provider: nil,
               max_turns: 10, **session_opts)
  if definition_name
    from_definition(definition_name, **session_opts)
  else
    @name = name
    @description = description || default_description(model, tools)
    @model = model
    @tools = tools.map { |t| t.is_a?(Class) ? t.new : t }
    @system_prompt = system_prompt
    @provider = provider
    @max_turns = max_turns
    @session_opts = session_opts
  end
end

Instance Attribute Details

#descriptionString (readonly)

Returns tool description visible to the LLM.

Returns:

  • (String)

    tool description visible to the LLM



47
48
49
# File 'lib/ask/agent/sub_agent.rb', line 47

def description
  @description
end

#nameString (readonly)

Returns tool name visible to the LLM.

Returns:

  • (String)

    tool name visible to the LLM



44
45
46
# File 'lib/ask/agent/sub_agent.rb', line 44

def name
  @name
end

Instance Method Details

#call(args = {}, abort_controller = nil) ⇒ Ask::Result

Execute the sub-agent with the given task.

Creates a fresh session for each call, runs the task, and returns the result. If the sub-agent fails, returns an error result — the coordinator can decide how to proceed.

Parameters:

  • args (Hash, String) (defaults to: {})

    Arguments from the LLM.

  • abort_controller (Object, nil) (defaults to: nil)

    Optional abort controller.

Returns:

  • (Ask::Result)


122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/ask/agent/sub_agent.rb', line 122

def call(args = {}, abort_controller = nil)
  task = extract_task(args)

  session_opts = {
    model: @model,
    tools: @tools.map(&:class),
    max_turns: @max_turns
  }
  session_opts[:provider] = @provider if @provider
  session_opts[:system_prompt] = @system_prompt if @system_prompt
  session_opts.merge!(@session_opts)

  session = Session.new(**session_opts)
  result = session.run(task.to_s)
  Ask::Result.ok(data: result.to_s)
rescue StandardError => e
  Ask::Result.failure("SubAgent '#{@name}' error: #{e.message}")
end

#inspectString

Human-readable representation.

Returns:

  • (String)


144
145
146
# File 'lib/ask/agent/sub_agent.rb', line 144

def inspect
  "#<Ask::Agent::SubAgent name=#{@name.inspect}>"
end

#params_schemaHash

JSON Schema for the tool's parameter.

Returns:

  • (Hash)


92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/ask/agent/sub_agent.rb', line 92

def params_schema
  {
    type: "object",
    properties: {
      "task" => {
        type: "string",
        description: "The task to delegate to the sub-agent"
      }
    },
    required: ["task"],
    additionalProperties: false
  }
end

#provider_paramsHash

Provider-specific parameters (none by default).

Returns:

  • (Hash)


109
110
111
# File 'lib/ask/agent/sub_agent.rb', line 109

def provider_params
  {}
end