Skip to content

Routing

Routing classifies input and dispatches it to the most appropriate specialist agent. A cheap classification model makes the routing decision; specialist agents handle the actual work.

Input → Classifier → Route A (Specialist A)
                   → Route B (Specialist B)
                   → Route C (Specialist C)

When to use it

  • Inputs vary significantly in type, topic, or required expertise
  • Different inputs benefit from different instructions or models
  • You want to use cheaper models for simple queries and powerful models for complex ones

Basic example

ts
import { agent } from '@daedalus-ai-dev/ai-sdk';

async function handleSupportQuery(query: string): Promise<string> {
  // Step 1: Classify the query (cheap model, fast)
  const classification = await agent({
    instructions: 'Classify customer support queries. Return JSON only.',
    model: 'openai/gpt-4o-mini',
    schema: (s) => ({
      type:       s.enum(['general', 'refund', 'technical']).required(),
      complexity: s.enum(['simple', 'complex']).required(),
    }),
  }).prompt<{ type: string; complexity: string }>(`Classify: "${query}"`);

  const { type, complexity } = classification.structured;

  // Step 2: Route to the appropriate specialist
  const instructions = {
    refund:    'You are a customer service specialist handling refund requests. Be empathetic and follow the 30-day return policy.',
    technical: 'You are a technical support engineer. Ask clarifying questions and provide step-by-step solutions.',
    general:   'You are a friendly customer service agent. Be helpful and concise.',
  }[type] ?? 'You are a helpful customer service agent.';

  // Step 3: Use a more capable model for complex queries
  const model = complexity === 'complex'
    ? 'anthropic/claude-3-5-sonnet'
    : 'openai/gpt-4o-mini';

  const response = await agent({ instructions, model }).prompt(query);
  return response.text;
}

Multi-dimensional routing

Route on multiple axes simultaneously:

ts
type Classification = {
  language: 'en' | 'es' | 'de' | 'fr' | 'other';
  intent: 'question' | 'complaint' | 'feedback' | 'purchase';
  urgency: 'low' | 'medium' | 'high';
};

const classification = await agent({
  instructions: 'Classify incoming customer messages.',
  model: 'openai/gpt-4o-mini',
  schema: (s) => ({
    language: s.enum(['en', 'es', 'de', 'fr', 'other']).required(),
    intent:   s.enum(['question', 'complaint', 'feedback', 'purchase']).required(),
    urgency:  s.enum(['low', 'medium', 'high']).required(),
  }),
}).prompt<Classification>(`Classify: "${message}"`);

// Route to language-specific agent for non-English
if (classification.structured.language !== 'en') {
  return handleLocalisedQuery(message, classification.structured.language);
}

// Escalate high-urgency complaints immediately
if (classification.structured.intent === 'complaint' && classification.structured.urgency === 'high') {
  return escalateToHuman(message);
}

// Handle normally
return handleQuery(message, classification.structured.intent);

Cost optimisation with routing

A powerful application of routing is using it to avoid paying for expensive models when simpler queries can be handled cheaply:

ts
async function smartAgent(query: string): Promise<string> {
  // Assess complexity first
  const assessment = await agent({
    instructions: 'Assess whether a query requires deep reasoning or is straightforward.',
    model: 'openai/gpt-4o-mini',  // Always use cheap model for assessment
    schema: (s) => ({
      requiresDeepReasoning: s.boolean().required(),
      reason: s.string().required(),
    }),
  }).prompt<{ requiresDeepReasoning: boolean; reason: string }>(`Query: "${query}"`);

  const model = assessment.structured.requiresDeepReasoning
    ? 'anthropic/claude-3-5-sonnet'   // ~$3 / 1M tokens
    : 'openai/gpt-4o-mini';           // ~$0.15 / 1M tokens

  const response = await agent({
    instructions: 'You are a helpful assistant.',
    model,
  }).prompt(query);

  return response.text;
}

Fallback chains

Handle unknown routes gracefully:

ts
const handlers: Record<string, (q: string) => Promise<string>> = {
  billing: handleBilling,
  technical: handleTechnical,
  sales: handleSales,
};

const handler = handlers[type] ?? handleGeneral;
return handler(query);

Tips

  • Keep classifiers narrow. Ask only for the classification decision — not the answer.
  • Use structured output for routing. It prevents the classifier from hallucinating category names.
  • Log routing decisions. Tracking which routes are chosen reveals what users actually need.
  • Validate unknown routes. Always have a default/fallback route.

Released under the MIT License.