Class: Boxcars::StationAgent

Inherits:
ToolTrain show all
Defined in:
lib/boxcars/train/station_agent.rb

Overview

Agent abstraction that wraps ToolTrain with ergonomic DSL and agent-as-tool semantics.

Provides agent-friendly vocabulary (‘instructions`, `tools`, `model`) and proper agent-as-tool nesting for multi-agent composition. When passed as a tool to another agent, the outer agent sees a single `input` string parameter and receives the inner agent’s final answer as an observation.

Defined Under Namespace

Classes: HandoffBoxcar

Constant Summary collapse

DEFAULT_NAME =
"Station Agent"
DEFAULT_DESCRIPTION =
"A helpful AI agent"

Constants inherited from ToolTrain

ToolTrain::CTEMPLATE

Constants inherited from Boxcar

Boxcar::SCHEMA_KEY_ALIASES, Boxcar::TYPE_ALIASES

Instance Attribute Summary collapse

Attributes inherited from ToolTrain

#wants_next_actions

Attributes inherited from Train

#boxcars, #early_stopping_method, #engine_prefix, #final_answer_prefix, #max_iterations, #name_to_boxcar_map, #observation_prefix, #question_prefix, #return_intermediate_steps, #return_values, #thought_prefix, #using_xml

Attributes inherited from EngineBoxcar

#engine, #prompt, #stop, #top_k

Attributes inherited from Boxcar

#description, #name, #parameters, #return_direct

Instance Method Summary collapse

Methods inherited from Train

#boxcar_descriptions, #boxcar_names, #construct_scratchpad, #extract_boxcar_and_input, #finish_boxcar_name, #get_boxcar_result, #get_next_action, #init_prefixes, #input_keys, #key_and_value_text, #next_actions, #observation_text, #plan, #pre_return, #prepare_for_new_call, #return_stopped_response, #should_continue?

Methods inherited from EngineBoxcar

#apply, #extract_code, #generate, #input_keys, #output_key, #predict, #prediction_variables

Methods inherited from Boxcar

#apply, assi, #conduct, #conduct_result, hist, #input_keys, #parameters_json_schema, #run, #run_result, #schema, syst, #tool_call_name, #tool_definition, #tool_spec, user, #validate_inputs, #validate_outputs

Constructor Details

#initialize(instructions:, tools: [], engine: nil, name: DEFAULT_NAME, description: DEFAULT_DESCRIPTION, **kwargs) ⇒ StationAgent

Returns a new instance of StationAgent.

Parameters:

  • instructions (String)

    System prompt text (required)

  • tools (Array<Boxcars::Boxcar>) (defaults to: [])

    Boxcar or StationAgent instances to use as tools

  • engine (Boxcars::Engine) (defaults to: nil)

    Engine instance; takes priority over model

  • name (String) (defaults to: DEFAULT_NAME)

    Agent name (also used to derive tool_call_name when nested)

  • description (String) (defaults to: DEFAULT_DESCRIPTION)

    Used as tool_spec description when nested as a tool

  • kwargs (Hash)

    Additional options forwarded to ToolTrain. Also accepts:

    • model: [String] Model string (e.g., “sonnet”) resolved via Engines.engine

    • mcp_clients: [Array] MCP client instances; tools auto-discovered

    • on_tool_call: [Proc] Called before tool execution; return false to block

    • on_tool_result: [Proc] Called after tool execution (informational)

    • on_complete: [Proc] Called when agent finishes without a handoff

    • on_event: [Proc] Called with AgentEvent for each lifecycle event

    • handoffs: [Array<StationAgent>] Agents available as handoff targets



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/boxcars/train/station_agent.rb', line 58

def initialize(instructions:, tools: [], engine: nil, name: DEFAULT_NAME, description: DEFAULT_DESCRIPTION, **kwargs)
  @instructions = instructions

  model = kwargs.delete(:model)
  mcp_clients = kwargs.delete(:mcp_clients) || []
  @on_tool_call = kwargs.delete(:on_tool_call)
  @on_tool_result = kwargs.delete(:on_tool_result)
  @on_complete = kwargs.delete(:on_complete)
  @on_event = kwargs.delete(:on_event)
  handoff_agents = kwargs.delete(:handoffs) || []
  resolved_engine = engine || (model ? Boxcars::Engines.engine(model: model) : nil)

  combined_tools = Array(tools)
  Array(mcp_clients).each do |client|
    combined_tools.concat(Boxcars::MCP.boxcars_from_client(client))
  end

  prompt = build_prompt

  kwargs[:parameters] ||= {
    input: { type: :string, description: "The task or question for this agent", required: true }
  }

  super(boxcars: combined_tools, engine: resolved_engine, name: name, description: description,
        prompt: prompt, **kwargs)

  install_handoffs(handoff_agents)
end

Instance Attribute Details

#handoffsObject (readonly)

Returns the value of attribute handoffs.



11
12
13
# File 'lib/boxcars/train/station_agent.rb', line 11

def handoffs
  @handoffs
end

#instructionsObject (readonly)

Returns the value of attribute instructions.



11
12
13
# File 'lib/boxcars/train/station_agent.rb', line 11

def instructions
  @instructions
end

#on_completeObject (readonly)

Returns the value of attribute on_complete.



11
12
13
# File 'lib/boxcars/train/station_agent.rb', line 11

def on_complete
  @on_complete
end

#on_eventObject (readonly)

Returns the value of attribute on_event.



11
12
13
# File 'lib/boxcars/train/station_agent.rb', line 11

def on_event
  @on_event
end

#on_tool_callObject (readonly)

Returns the value of attribute on_tool_call.



11
12
13
# File 'lib/boxcars/train/station_agent.rb', line 11

def on_tool_call
  @on_tool_call
end

#on_tool_resultObject (readonly)

Returns the value of attribute on_tool_result.



11
12
13
# File 'lib/boxcars/train/station_agent.rb', line 11

def on_tool_result
  @on_tool_result
end

Instance Method Details

#call(inputs:) ⇒ Object

Override ToolTrain#call to track handoffs, fire callbacks, and emit events.



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/boxcars/train/station_agent.rb', line 93

def call(inputs:)
  @pending_handoff = nil
  @current_iteration = 0
  @total_tool_calls = 0
  emit_event(:agent_start, input: inputs[:input], agent_name: name)
  result = super
  if @pending_handoff
    result[:handoff] = @pending_handoff
    emit_event(:handoff, from_agent: name, to_agent: @pending_handoff[:agent].name,
                         reason: @pending_handoff[:reason])
  else
    on_complete&.call(result)
    emit_event(:agent_complete, output: result[:output], iterations: @current_iteration,
                                tool_calls_count: @total_tool_calls)
  end
  result
end

#output_keysObject



87
88
89
90
# File 'lib/boxcars/train/station_agent.rb', line 87

def output_keys
  base = super
  base.include?(:handoff) ? base : base + [:handoff]
end

#prediction_additional(_inputs) ⇒ Object

No template placeholders beyond %<input>s, so no extra variables needed.



129
130
131
# File 'lib/boxcars/train/station_agent.rb', line 129

def prediction_additional(_inputs)
  {}
end

#run_stream(input) {|AgentEvent| ... } ⇒ ConductResult, Enumerator<AgentEvent>

Stream agent events during execution.

Parameters:

  • input (String)

    User input

Yields:

  • (AgentEvent)

    Each lifecycle event as it occurs

Returns:



116
117
118
119
120
121
122
123
124
125
126
# File 'lib/boxcars/train/station_agent.rb', line 116

def run_stream(input, &block)
  if block
    previous_on_event = @on_event
    @on_event = block
    conduct(input)
  else
    Enumerator.new { |y| run_stream(input) { |event| y << event } }
  end
ensure
  @on_event = previous_on_event if block
end