Building Intelligent Task Routers

Introduction & Context

Welcome back! In the previous lesson, you learned how to use prompt chaining to connect multiple Claude API calls in a linear sequence, breaking down complex tasks into manageable steps. This approach works well when you know exactly which sequence of operations is needed.

However, real-world applications often face unpredictable requests that require different types of expertise. Instead of building separate chains for every possible request, routing workflows use Claude to analyze incoming requests and intelligently direct them to the right specialist — such as a math expert, a writing expert, or a code expert.

This lesson will show you how to build a flexible routing system that maintains high-quality responses by leveraging specialized system prompts for each domain.

Workflow Design at a Glance

The routing workflow follows a clean two-step pattern that is more dynamic than the linear chains you have used before. When a user submits a request, your system first sends it to a router — a Claude instance with a specialized system prompt designed to classify the request type. The router analyzes the content and returns a decision about which specialist should handle it.

Once you have the routing decision, you send the original user request to a second Claude instance configured with the appropriate specialist system prompt. This specialist focuses entirely on providing the best possible response within its domain of expertise, whether that is solving equations, crafting stories, or debugging code.

The key insight here is that you are using the same Anthropic::Client and the same Claude model (claude-sonnet-4-6) for both calls, but with completely different system prompts that give each instance a distinct role and expertise. The router acts as a classifier, while the specialist acts as a domain expert. This separation of concerns makes your system both more reliable and easier to extend with new specialist types.

Crafting the Router Prompt

The router prompt is the critical component that determines how accurately your system classifies incoming requests. Unlike the open-ended prompts you might use for creative tasks, router prompts need to be strict and constrained to ensure reliable, parseable output.

Your router system prompt should establish Claude's role as a decision-maker and provide clear, unambiguous options. Here is an effective approach that limits the router to exactly three choices:

Ruby
router_prompt = <<~PROMPT
  You are a task router. Your job is to analyze user requests and determine which specialist should handle them.

  Available specialists:
  - math_specialist: For mathematical calculations, equations, and numerical problems
  - writing_specialist: For creative writing, essays, stories, and text composition
  - code_specialist: For programming questions, code review, and technical implementation

  Respond with ONLY the specialist name (math_specialist, writing_specialist, or code_specialist) that best matches the user's request.
PROMPT

Ruby's heredoc syntax (<<~PROMPT) provides a clean way to define multi-line strings with automatic indentation stripping, making your prompts easy to read and maintain. The phrase Respond with ONLY is crucial because it instructs Claude to avoid adding explanatory text or reasoning that would complicate parsing. While Claude is highly reliable at following these constraints, keep in mind that prompt engineering provides strong guidance rather than a programmatic guarantee of the output format. You want exactly one of three possible strings, nothing more. The explicit list of specialist names with their descriptions helps Claude understand the boundaries between categories and reduces ambiguous classifications.

Notice how each specialist description focuses on clear, distinct domains. Mathematical problems are clearly different from creative writing, which is clearly different from programming tasks. This separation reduces edge cases where the router might struggle to choose between specialists.

Defining Specialist System Prompts

Each specialist needs a focused system prompt that establishes its expertise and approach. Unlike general-purpose prompts, specialist prompts should be concise and role-specific to maximize performance within their domain.

Your math specialist prompt should emphasize precision and clarity in mathematical communication:

Ruby
math_specialist_prompt = "You are a mathematics expert. You excel at solving equations, performing calculations, and explaining mathematical concepts clearly."

The writing specialist focuses on creativity and engaging communication:

Ruby
writing_specialist_prompt = "You are a creative writing expert. You specialize in crafting engaging stories, essays, and helping with all forms of written communication."

The code specialist emphasizes technical accuracy and practical implementation:

Ruby
code_specialist_prompt = "You are a programming expert. You specialize in writing code, debugging, code review, and explaining technical concepts."

These prompts are intentionally brief because they need to work with a wide variety of requests within their domain. A math specialist might handle anything from basic arithmetic to complex calculus, so the prompt focuses on the general approach rather than specific techniques. This specialization improves both quality and consistency compared to using a generic "helpful assistant" prompt for all request types.

The beauty of this approach is reusability. Once you have crafted effective specialist prompts, you can use them across different projects and routing systems. They become reliable building blocks for more complex AI workflows.

Preparing and Sending the Router Request

To begin the routing workflow, you first prepare the user request and send it to the router.

Ruby
require "anthropic"

# Initialize the Anthropic client
client = Anthropic::Client.new

# Choose a model to use
model = "claude-sonnet-4-6"

# User request to route
user_request = "Write me a short story about robots"

# Step 1: Send request to router to determine which specialist to use
router_response = client.messages.create(
  model: model,
  max_tokens: 100,  # Short response expected
  system: router_prompt,
  messages: [{ role: "user", content: user_request }]
)

Here, you set up the Anthropic client with Anthropic::Client.new, specify the model, and construct the message payload using Ruby hash syntax. The max_tokens: 100 parameter ensures the router's response is concise, as you expect only a short specialist name. Notice how the messages parameter takes an array of hashes, with each hash containing role: and content: keys that define the conversation structure.

Extracting and Handling the Router's Response

After receiving the router's response, you need to extract the specialist decision and prepare it for use in the next step of the workflow.

Ruby
# Extract the routing decision
specialist_choice = router_response.content.first.text.strip
puts "Router decision: #{specialist_choice}"

The response structure from the Anthropic Ruby SDK provides a content array, and you access the first text block with .first.text. The .strip method removes any leading or trailing whitespace to ensure clean string matching in the next step. The puts statement with string interpolation (#{}) is useful for debugging and verifying that the router is making the correct classification. For the example request "Write me a short story about robots", you should see:

text
Router decision: writing_specialist

This confirms that the router correctly identified the request as a creative writing task.

Mapping the Router Decision to a Specialist Prompt

Once you have the router's decision, you need to map it to the appropriate specialist system prompt. This ensures that the user request is handled by the expert best suited for the task.

Ruby
# Step 2: Route to the appropriate specialist based on the router's decision
specialist_prompt =
  case specialist_choice
  when "math_specialist"    then math_specialist_prompt
  when "writing_specialist" then writing_specialist_prompt
  when "code_specialist"    then code_specialist_prompt
  else "You are a helpful assistant."
  end

The case statement provides a clean, Ruby-idiomatic way to map router decisions to specialist prompts. Each when clause checks for an exact string match with the expected specialist names from your router prompt. This approach is straightforward and easy to debug when routing decisions do not match expectations.

The else clause provides a crucial fallback mechanism. If the router returns an unexpected response — perhaps due to a prompt engineering issue or an edge case you did not anticipate — the system defaults to a generic helpful assistant prompt rather than raising an error. This graceful degradation keeps your system functional even when routing does not work perfectly.

Sending the User Request to the Specialist

After selecting the correct specialist prompt, you send the original user request to the chosen specialist. The specialist uses its domain expertise to generate the final response.

Ruby
# Step 3: Send the original request to the chosen specialist
specialist_response = client.messages.create(
  model: model,
  max_tokens: 2000,
  system: specialist_prompt,
  messages: [{ role: "user", content: user_request }]
)

Notice that you send the original user_request to the specialist, not the router's response. The router's job is purely classification; the specialist needs to see the actual user question to provide a helpful answer. This separation keeps the workflow clean and ensures the specialist has all the context needed to respond effectively. The max_tokens: 2000 parameter allows the specialist room to provide a detailed, comprehensive response appropriate to its domain.

Extracting and Displaying the Specialist's Response

Finally, extract the specialist's response and display it. This is the answer that will be returned to the user.

Ruby
# Extract and display the specialist's response
final_response = specialist_response.content.first.text
puts "Specialist Response:"
puts final_response

When you run this code with the robot story request, you should see output like:

text
Specialist Response:
# The Last Garden

The old greenhouse sat at the edge of the city, where concrete met forgotten soil.

Unit Seven had been assigned there seventeen years ago, tasked with maintaining the botanical collection after the last human gardener retired. It had never questioned the assignment. Robots didn't question things...

This demonstrates that the writing specialist correctly understood the creative writing request and produced an engaging story opening rather than trying to solve it as a math problem or write code.

Summary and Prep for Practice

You have successfully learned to implement intelligent task routing that uses Claude to classify requests and direct them to specialized system prompts optimized for specific domains. Your routing workflow follows a clean two-step pattern: analyze the request with a router prompt to get a classification decision, then send the original request to the appropriate specialist for the final response.

The key Ruby techniques you applied include using heredocs for clean multi-line prompt definitions, the case statement for elegant routing logic, and the Anthropic Ruby SDK's hash-based message structure for API calls. The .content.first.text pattern for extracting responses and .strip for cleaning string data are patterns you will use throughout your Ruby AI applications.

The routing patterns you have learned here form the foundation for much more sophisticated AI workflows. As you continue through this course, you will see how these routing concepts extend to dynamic workflows and complex agent behaviors that can handle real-world business problems with multiple types of expertise working together.

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal