Class: Brute::SubAgent

Inherits:
Agent show all
Defined in:
lib/brute/sub_agent.rb

Overview

A SubAgent is an Agent that exposes a tool-shaped facade so it can be dropped into another agent’s tools list. The parent agent’s LLMCall passes it to ruby_llm as a regular tool; when invoked, the SubAgent runs its own pipeline against a fresh Session built from the tool arguments, then returns the final assistant message as the tool result.

Usage:

researcher = Brute::SubAgent.new(
  name:        "research",
  description: "Delegate a research task to a read-only sub-agent.",
  provider:    Brute.provider,
  model:       Brute.provider.default_model,
  tools:       [Brute::Tools::FSRead, Brute::Tools::FSSearch],
) do
  use Brute::Middleware::SystemPrompt
  use Brute::Middleware::MaxIterations, max_iterations: 10
  use Brute::Middleware::ToolCall
  run Brute::Middleware::LLMCall.new
end

main_agent = Brute::Agent.new(
  provider: ...,
  tools: [Brute::Tools::FSRead, researcher],   # SubAgent IS a tool
) { ... }

Constant Summary collapse

DEFAULT_PARAMS =
{
  task: { type: "string", desc: "A clear, detailed description of the task", required: true },
}.freeze

Instance Attribute Summary collapse

Attributes inherited from Agent

#model, #provider, #tools

Instance Method Summary collapse

Methods inherited from Agent

#call

Methods inherited from Pipeline

#build, #call, #run, #use

Constructor Details

#initialize(name:, description:, params: DEFAULT_PARAMS, **agent_opts, &block) ⇒ SubAgent

Returns a new instance of SubAgent.



42
43
44
45
46
47
# File 'lib/brute/sub_agent.rb', line 42

def initialize(name:, description:, params: DEFAULT_PARAMS, **agent_opts, &block)
  @sub_agent_name = name.to_s
  @description    = description
  @params         = params
  super(**agent_opts, &block)
end

Instance Attribute Details

#descriptionObject (readonly)

Returns the value of attribute description.



40
41
42
# File 'lib/brute/sub_agent.rb', line 40

def description
  @description
end

#paramsObject (readonly)

Returns the value of attribute params.



40
41
42
# File 'lib/brute/sub_agent.rb', line 40

def params
  @params
end

#sub_agent_nameObject (readonly)

Returns the value of attribute sub_agent_name.



40
41
42
# File 'lib/brute/sub_agent.rb', line 40

def sub_agent_name
  @sub_agent_name
end

Instance Method Details

#execute(arguments) ⇒ Object

Tool-shaped entry point. Builds a session from arguments, runs the agent loop, returns the last assistant message as a string.



51
52
53
54
55
# File 'lib/brute/sub_agent.rb', line 51

def execute(arguments)
  session = build_session(arguments)
  call(session)
  extract_result(session)
end

#nameObject

Lets ToolCall treat SubAgents the same as RubyLLM::Tool instances without checking respond_to? everywhere.



72
73
74
# File 'lib/brute/sub_agent.rb', line 72

def name
  @sub_agent_name
end

#to_ruby_llmObject

Adapter so the parent agent’s LLMCall (and ruby_llm) sees this as a regular tool. ToolCall middleware should call ‘to_ruby_llm` when building the tools hash if a tool responds to it.



60
61
62
63
64
65
66
67
68
# File 'lib/brute/sub_agent.rb', line 60

def to_ruby_llm
  sub = self
  Class.new(RubyLLM::Tool) do
    description sub.description
    sub.params.each { |k, opts| param k, **opts }
    define_method(:name) { sub.sub_agent_name }
    define_method(:execute) { |**args| sub.execute(args) }
  end.new
end