Class: Brute::SubAgent
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
-
#description ⇒ Object
readonly
Returns the value of attribute description.
-
#params ⇒ Object
readonly
Returns the value of attribute params.
-
#sub_agent_name ⇒ Object
readonly
Returns the value of attribute sub_agent_name.
Attributes inherited from Agent
Instance Method Summary collapse
-
#execute(arguments) ⇒ Object
Tool-shaped entry point.
-
#initialize(name:, description:, params: DEFAULT_PARAMS, **agent_opts, &block) ⇒ SubAgent
constructor
A new instance of SubAgent.
-
#name ⇒ Object
Lets ToolCall treat SubAgents the same as RubyLLM::Tool instances without checking respond_to? everywhere.
-
#to_ruby_llm ⇒ Object
Adapter so the parent agent’s LLMCall (and ruby_llm) sees this as a regular tool.
Methods inherited from Agent
Methods inherited from Pipeline
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
#description ⇒ Object (readonly)
Returns the value of attribute description.
40 41 42 |
# File 'lib/brute/sub_agent.rb', line 40 def description @description end |
#params ⇒ Object (readonly)
Returns the value of attribute params.
40 41 42 |
# File 'lib/brute/sub_agent.rb', line 40 def params @params end |
#sub_agent_name ⇒ Object (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 |
#name ⇒ Object
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_llm ⇒ Object
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 |