> ## Documentation Index
> Fetch the complete documentation index at: https://icepick.hatchet.run/llms.txt
> Use this file to discover all available pages before exploring further.

# Routing

> Direct different request types to specialized handlers for optimized processing

export const CallGraphSequence = props => {
  const actors = [{
    name: 'User',
    left: 20
  }, {
    name: 'Your API',
    left: 160
  }, {
    name: 'Agent',
    left: 300
  }, {
    name: 'Tool',
    left: 440
  }, {
    name: 'Business Logic',
    left: 580
  }];
  const messages = [{
    from: 0,
    to: 1,
    label: 'API Request',
    top: 90,
    dashed: false
  }, {
    from: 1,
    to: 2,
    label: 'agent.run()',
    top: 125,
    dashed: false,
    link: '/api-reference/agent'
  }, {
    from: 2,
    to: 3,
    label: 'tool.run()',
    top: 160,
    dashed: false,
    link: '/api-reference/tool'
  }, {
    from: 3,
    to: 4,
    label: 'External Call',
    top: 195,
    dashed: false
  }, {
    from: 4,
    to: 3,
    label: 'Response',
    top: 230,
    dashed: true
  }, {
    from: 3,
    to: 2,
    label: 'Tool Result',
    top: 265,
    dashed: true
  }, {
    from: 2,
    to: 1,
    label: 'Agent Response',
    top: 300,
    dashed: true
  }, {
    from: 1,
    to: 0,
    label: 'API Response',
    top: 335,
    dashed: true
  }];
  const actorData = props && props.actors ? props.actors : actors;
  const messageData = props && props.messages ? props.messages : messages;
  const height = props && props.height ? props.height : 370;
  const config = {
    actorTop: 30,
    actorWidth: 80,
    lifelineTop: 60,
    lifelineHeight: height,
    lifelineOffset: 40,
    messageSpacing: 140
  };
  return <div className="call-graph-container">
<div className="call-graph-canvas" style={{
    height: height + 30
  }}>
{actorData.map((actor, index) => <div key={index} className="call-graph-actor" style={{
    top: config.actorTop,
    left: actor.left,
    width: config.actorWidth
  }}>
{actor.name}
</div>)}

        {actorData.map((actor, index) => <div key={index} className="call-graph-lifeline" style={{
    top: config.lifelineTop,
    left: actor.left + config.lifelineOffset,
    height: config.lifelineHeight
  }}></div>)}

        {messageData.map((message, index) => {
    const fromActor = actorData[message.from];
    const toActor = actorData[message.to];
    const isReturn = message.from > message.to;
    const messageLeft = isReturn ? toActor.left + config.lifelineOffset + 6 : fromActor.left + config.lifelineOffset;
    const messageWidth = isReturn ? fromActor.left + config.lifelineOffset - (toActor.left + config.lifelineOffset + 6) : toActor.left + config.lifelineOffset - (fromActor.left + config.lifelineOffset);
    return <div key={index} className="call-graph-message" style={{
      top: message.top,
      left: messageLeft,
      width: messageWidth
    }}>
              <div className={`call-graph-message-line ${message.dashed ? 'dashed' : ''}`} style={{
      width: '100%'
    }}></div>
              <div className={`call-graph-arrow ${isReturn ? 'return' : ''}`} style={{
      position: 'absolute',
      [isReturn ? 'left' : 'right']: isReturn ? -4 : -3,
      top: -2
    }}></div>
              {message.link ? <a href={message.link} className="call-graph-message-label" style={{
      position: 'absolute',
      top: -12,
      left: '50%',
      transform: 'translateX(-50%)',
      textDecoration: 'none'
    }}>
                  {message.label}
                </a> : <div className="call-graph-message-label" style={{
      position: 'absolute',
      top: -12,
      left: '50%',
      transform: 'translateX(-50%)'
    }}>
                  {message.label}
                </div>}
            </div>;
  })}
      </div>
    </div>;
};

*Based on [Anthropic's "Building Effective Agents"](https://www.anthropic.com/engineering/building-effective-agents) framework.*

Routing directs different types of requests to specialized handlers optimized for specific domains. A classification step determines the request type, then routes it to the appropriate specialized agent or tool. This pattern improves response quality by using domain-specific prompts and context while maintaining a single entry point for users.

export const routingActors = [{
  name: 'Client',
  left: 20
}, {
  name: 'Router Agent',
  left: 160
}, {
  name: 'Classification',
  left: 300
}, {
  name: 'Support Handler',
  left: 440
}, {
  name: 'Sales Handler',
  left: 580
}];

export const routingMessages = [{
  from: 0,
  to: 1,
  label: 'request',
  top: 90,
  dashed: false
}, {
  from: 1,
  to: 2,
  label: 'classify',
  top: 119,
  dashed: false
}, {
  from: 2,
  to: 1,
  label: 'category',
  top: 148,
  dashed: true
}, {
  from: 1,
  to: 3,
  label: 'route to handler',
  top: 177,
  dashed: false
}, {
  from: 3,
  to: 1,
  label: 'specialized response',
  top: 206,
  dashed: true
}, {
  from: 1,
  to: 0,
  label: 'response',
  top: 235,
  dashed: true
}];

<CallGraphSequence actors={routingActors} messages={routingMessages} height={260} />

## When to Use

Use routing when you have distinct request types that benefit from specialized handling, such as customer service systems with support, sales, and billing inquiries. It's ideal when different domains require different prompts, tools, or context. Avoid routing when requests are too similar to benefit from specialization or when the classification overhead outweighs the benefits of specialized handling.

## Implementation

This example demonstrates a customer service router that classifies incoming messages as Support, Sales, or Other requests, then directs them to specialized handlers optimized for each domain.

### Agent Code

```typescript theme={null}
import { icepick } from "@hatchet-dev/icepick";
import z from "zod";
import { classificationTool } from "@tools/classification.tool";
import { supportTool, salesTool } from "@tools/calls.tool";

const RoutingAgentInput = z.object({
  message: z.string(),
});

const RoutingAgentOutput = z.object({
  message: z.string(),
  canHelp: z.boolean(),
});

export const routingAgent = icepick.agent({
  name: "routing-agent",
  executionTimeout: "1m",
  inputSchema: RoutingAgentInput,
  outputSchema: RoutingAgentOutput,
  description: "Routes messages to specialized handlers based on classification",
  fn: async (input, ctx) => {
    // STEP 1: Classify the incoming message
    const { classification } = await classificationTool.run({
      message: input.message,
    });

    // STEP 2: Route to specialized handler based on classification
    switch (classification) {
      case "support": {
        const { response } = await supportTool.run({
          message: input.message,
        });
        return {
          message: response,
          canHelp: true,
        };
      }
      case "sales": {
        const { response } = await salesTool.run({
          message: input.message,
        });
        return {
          message: response,
          canHelp: true,
        };
      }
      case "other": {
        return {
          message: "I'm not sure how to help with that. Could you provide more details or rephrase your question?",
          canHelp: false,
        };
      }
      default: {
        throw new Error(`Invalid classification: ${classification}`);
      }
    }
  },
});
```

<AccordionGroup>
  <Accordion title="Classification Tool: Request Categorization">
    ```typescript theme={null}
    import { icepick } from "@hatchet-dev/icepick";
    import z from "zod";
    import { generateObject } from "ai";

    const Classification = z.enum(["support", "sales", "other"]);

    export const classificationTool = icepick.tool({
      name: "classification-tool",
      description: "Classifies incoming messages into categories",
      inputSchema: z.object({
        message: z.string(),
      }),
      outputSchema: z.object({
        classification: Classification,
      }),
      fn: async (input) => {
        const result = await generateObject({
          model: icepick.defaultLanguageModel,
          prompt: `Classify the following message into one of these categories:
          - support: Technical issues, bugs, how-to questions
          - sales: Pricing, features, purchasing questions  
          - other: Everything else
          
          Message: ${input.message}`,
          schema: z.object({
            classification: Classification,
          }),
        });

        return {
          classification: result.object.classification,
        };
      },
    });
    ```

    This tool uses structured output to ensure reliable classification into predefined categories. The LLM analyzes the message content and returns a valid enum value, preventing invalid classifications that could break the routing logic.
  </Accordion>

  <Accordion title="Support Handler: Technical Assistance">
    ```typescript theme={null}
    import { icepick } from "@hatchet-dev/icepick";
    import z from "zod";
    import { generateText } from "ai";

    export const supportTool = icepick.tool({
      name: "support-tool",
      description: "Handles technical support requests",
      inputSchema: z.object({
        message: z.string(),
      }),
      outputSchema: z.object({
        response: z.string(),
      }),
      fn: async (input) => {
        const result = await generateText({
          model: icepick.defaultLanguageModel,
          prompt: `You are a technical support specialist. Help the user with their technical issue. If it's a common problem, suggest turning it off and on again as a first step.

    User message: ${input.message}`,
        });

        return {
          response: result.text,
        };
      },
    });
    ```

    This specialized handler focuses on technical support scenarios, using domain-specific prompts and suggesting common troubleshooting steps. The context is optimized for support interactions rather than general conversation.
  </Accordion>

  <Accordion title="Sales Handler: Commercial Inquiries">
    ```typescript theme={null}
    import { icepick } from "@hatchet-dev/icepick";
    import z from "zod";
    import { generateText } from "ai";

    export const salesTool = icepick.tool({
      name: "sales-tool",
      description: "Handles sales inquiries and pricing questions",
      inputSchema: z.object({
        message: z.string(),
      }),
      outputSchema: z.object({
        response: z.string(),
      }),
      fn: async (input) => {
        const result = await generateText({
          model: icepick.defaultLanguageModel,
          prompt: `You are a sales representative. Help the user with their sales inquiry. Our product costs $42. Be helpful and informative about features and pricing.

    User message: ${input.message}`,
        });

        return {
          response: result.text,
        };
      },
    });
    ```

    This handler specializes in sales conversations, including specific product information like pricing (\$42) in the context. The prompt is optimized for sales scenarios rather than technical support, providing more relevant responses for commercial inquiries.
  </Accordion>
</AccordionGroup>

The pattern uses a two-step process: classification determines the request type, then specialized handlers provide domain-optimized responses. Each handler uses prompts and context tailored to its specific domain, improving response quality compared to a general-purpose agent. The `canHelp` boolean indicates whether the system can assist with the request.

## Related Patterns

This pattern complements prompt chaining for complex multi-step workflows and can be combined with parallelization when multiple handlers need to process the same request simultaneously.
