Module: RubyLLM::Agents::Routing
- Defined in:
- lib/ruby_llm/agents/routing.rb,
lib/ruby_llm/agents/routing/result.rb,
lib/ruby_llm/agents/routing/class_methods.rb
Overview
Adds classification & routing capabilities to any BaseAgent.
Include this module in a BaseAgent subclass to get:
-
‘route` DSL for defining classification categories
-
‘default_route` for fallback classification
-
Auto-generated system/user prompts from route definitions
-
Structured output parsing to return a RoutingResult
All existing BaseAgent features (caching, reliability, retries, fallback models, instrumentation, multi-tenancy) work unchanged.
Defined Under Namespace
Modules: ClassMethods Classes: RoutingResult
Class Method Summary collapse
-
.classify(message:, routes:, default: :general, model: "gpt-4o-mini", **options) ⇒ Symbol
Classify a message without defining a router class.
- .included(base) ⇒ Object
Instance Method Summary collapse
-
#auto_delegate? ⇒ Boolean
Whether auto-delegation to the mapped agent is enabled for this call.
-
#build_result(content, response, context) ⇒ Object
Override build_result to return a RoutingResult.
-
#call(&block) ⇒ Object
Override call to capture the caller’s stream block so it can be forwarded to the delegated agent.
-
#delegation_params ⇒ Hash
Builds params to forward to the delegated agent.
-
#process_response(response) ⇒ Object
Override process_response to parse the route from LLM output.
-
#routing_categories_text ⇒ String
Helper to get formatted route categories for use in custom prompts.
-
#routing_system_prompt ⇒ String
Helper to get the auto-generated system prompt for routing.
-
#system_prompt ⇒ Object
Auto-generated system_prompt (used if subclass doesn’t override).
-
#user_prompt ⇒ Object
Auto-generated user_prompt from the :message param.
Class Method Details
.classify(message:, routes:, default: :general, model: "gpt-4o-mini", **options) ⇒ Symbol
Classify a message without defining a router class.
Creates an anonymous BaseAgent subclass with Routing included, calls it, and returns just the route symbol.
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'lib/ruby_llm/agents/routing.rb', line 63 def self.classify(message:, routes:, default: :general, model: "gpt-4o-mini", **) router_model = model router = Class.new(BaseAgent) do include Routing self.model router_model temperature 0.0 routes.each do |name, desc| route name, desc end default_route default end result = router.call(message: , **) result.route end |
.included(base) ⇒ Object
43 44 45 46 47 48 49 50 |
# File 'lib/ruby_llm/agents/routing.rb', line 43 def self.included(base) unless base < BaseAgent raise ArgumentError, "#{base} must inherit from RubyLLM::Agents::BaseAgent to include Routing" end base.extend(ClassMethods) base.param(:message, required: false) end |
Instance Method Details
#auto_delegate? ⇒ Boolean
Whether auto-delegation to the mapped agent is enabled for this call. Defaults to true. Pass ‘auto_delegate: false` to receive a classification-only RoutingResult with `delegated? == false` and `agent_class` set so the caller can invoke it manually.
166 167 168 |
# File 'lib/ruby_llm/agents/routing.rb', line 166 def auto_delegate? @options.fetch(:auto_delegate, true) end |
#build_result(content, response, context) ⇒ Object
Override build_result to return a RoutingResult. Auto-delegates to the mapped agent when the route has an ‘agent:` mapping, unless the caller opts out with `auto_delegate: false`.
145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
# File 'lib/ruby_llm/agents/routing.rb', line 145 def build_result(content, response, context) base = super agent_class = content[:agent_class] if agent_class && auto_delegate? content[:delegated_result] = if @delegation_stream_block agent_class.call(**delegation_params, &@delegation_stream_block) else agent_class.call(**delegation_params) end end RoutingResult.new(base_result: base, route_data: content) end |
#call(&block) ⇒ Object
Override call to capture the caller’s stream block so it can be forwarded to the delegated agent. Without this, chunks from the delegated agent are swallowed because build_result has no access to the original block.
122 123 124 125 |
# File 'lib/ruby_llm/agents/routing.rb', line 122 def call(&block) @delegation_stream_block = block super end |
#delegation_params ⇒ Hash
Builds params to forward to the delegated agent. Forwards original message and custom params, excludes routing internals.
174 175 176 177 178 179 |
# File 'lib/ruby_llm/agents/routing.rb', line 174 def delegation_params forward = @options.except(:dry_run, :skip_cache, :debug, :stream_events, :auto_delegate) forward[:_parent_execution_id] = @parent_execution_id if @parent_execution_id forward[:_root_execution_id] = @root_execution_id if @root_execution_id forward end |
#process_response(response) ⇒ Object
Override process_response to parse the route from LLM output.
128 129 130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/ruby_llm/agents/routing.rb', line 128 def process_response(response) raw = response.content.to_s.strip.downcase.gsub(/[^a-z0-9_]/, "") route_name = raw.to_sym valid_routes = self.class.routes.keys route_name = self.class.default_route_name unless valid_routes.include?(route_name) { route: route_name, agent_class: self.class.routes.dig(route_name, :agent), raw_response: response.content.to_s.strip } end |
#routing_categories_text ⇒ String
Helper to get formatted route categories for use in custom prompts.
102 103 104 105 106 |
# File 'lib/ruby_llm/agents/routing.rb', line 102 def routing_categories_text self.class.routes.map do |name, config| "- #{name}: #{config[:description]}" end.join("\n") end |
#routing_system_prompt ⇒ String
Helper to get the auto-generated system prompt for routing. Use this in custom system_prompt overrides to include route definitions.
85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/ruby_llm/agents/routing.rb', line 85 def routing_system_prompt default = self.class.default_route_name <<~PROMPT.strip You are a message classifier. Classify the user's message into exactly one of the following categories: #{routing_categories_text} If none of the categories clearly match, classify as: #{default} Respond with ONLY the category name, nothing else. PROMPT end |
#system_prompt ⇒ Object
Auto-generated system_prompt (used if subclass doesn’t override).
109 110 111 |
# File 'lib/ruby_llm/agents/routing.rb', line 109 def system_prompt super || routing_system_prompt end |
#user_prompt ⇒ Object
Auto-generated user_prompt from the :message param.
114 115 116 |
# File 'lib/ruby_llm/agents/routing.rb', line 114 def user_prompt @ask_message || [:message] || super end |