Docs
Orchestrator & Routing

Orchestrator & Routing

How determineAgentRoute() classifies intent and routes to the right agent.

How the Router Works

The orchestrator makes a single generateText call with structured output (Output.object) to classify the user's intent:

// lib/ai/orchestrator.ts
const result = await generateText({
  model: openai("gpt-4o-mini"),
  temperature: 0,
  messages: await convertToModelMessages(messages),
  system: `
    You are the core Orchestrator Router. Your ONLY job is to analyze the user's message and determine the domain.
    Today's date is ${today}.
    Domains:
      ${Object.entries(AGENT_REGISTRY)
        .map(([key, meta]) => `- ${key}: ${meta.description}`)
        .join("\n")}
    Return the chosen domain.
  `,
  output: Output.object({
    schema: z.object({
      domain: z.enum(routingKeys),
    }),
  }),
});

temperature: 0 makes routing deterministic. The model receives only a list of domain keys and their descriptions — nothing else.

The Fast-Path Bypass

If the frontend passes a valid agentId (other than "general"), the LLM routing step is skipped entirely:

if (
  requestedAgentId &&
  requestedAgentId !== "general" &&
  Object.values(AGENT_REGISTRY).some((agent) => agent.id === requestedAgentId)
) {
  return { domain: requestedAgentId as AgentId, usage: null };
}

This is the correct approach for dedicated agent pages — zero routing latency, zero token cost.

The AGENT_REGISTRY

All routing is driven by the description field in AGENT_REGISTRY:

// lib/ai/agents/registry.ts
export const AGENT_REGISTRY = {
  "content-creation": {
    id: "content-creation",
    name: "Content Strategist",
    description: "Expert at writing copy, SEO, and generating social assets.",
    icon: "PenTool",
  },
  "reel-generator": {
    id: "reel-generator",
    name: "Reel Generator",
    description:
      "Creates complete short-form video content packages for Instagram Reels, TikTok, and YouTube Shorts.",
    icon: "Video",
  },
  // ...
} as const;
 
export type AgentId = keyof typeof AGENT_REGISTRY;

The description is used verbatim in the routing system prompt. It is the only thing the router sees about each agent — write it carefully.

Function Signatures

// Determines which agent should handle the request
async function determineAgentRoute(
  messages: UIMessage[],
  requestedAgentId?: string,
): Promise<{ domain: AgentId; usage: any }>
 
// Dispatches to the correct agent implementation
async function executeAgent(
  domain: AgentId,
  messages: UIMessage[],
  team: any,
  conversationId: string,
  modelId?: string,
): Promise<StreamTextResult<any, any>>

usage is null on the fast path (no LLM call made).

Date Injection

Today's date is injected into the routing prompt (Today's date is ${today}) to prevent the model from making date-sensitive routing errors (e.g., misidentifying a time-sensitive news request).