AutoGen Tutorial (TypeScript): building conditional routing for advanced developers

By Cyprian AaronsUpdated 2026-04-21
autogenbuilding-conditional-routing-for-advanced-developerstypescript

This tutorial shows how to build a conditional routing layer on top of AutoGen in TypeScript, so your agent flow can branch based on message content, model output, or tool results. You need this when a single linear conversation is not enough and you want deterministic control over which agent handles which request.

What You'll Need

  • Node.js 18+
  • TypeScript 5+
  • An OpenAI API key
  • autogen installed in your project
  • A working TypeScript runtime such as tsx or ts-node
  • Basic familiarity with AutoGen agents, messages, and async/await

Step-by-Step

  1. Start with a small project and install the dependencies. I’m using tsx so you can run the file directly without a build step.
npm init -y
npm install autogen openai
npm install -D typescript tsx @types/node
  1. Create a typed router that decides which agent should receive the task. The router is just plain TypeScript: inspect the input, then return a route key you can map to an agent.
type Route = "billing" | "claims" | "general";

function routeMessage(input: string): Route {
  const text = input.toLowerCase();

  if (text.includes("invoice") || text.includes("payment") || text.includes("billing")) {
    return "billing";
  }

  if (text.includes("claim") || text.includes("incident") || text.includes("policy")) {
    return "claims";
  }

  return "general";
}
  1. Define three specialized assistants plus one user proxy. The important part is that each assistant has a narrow job, because conditional routing only works when the downstream agents are clearly separated.
import { AssistantAgent, UserProxyAgent } from "autogen";

const llmConfig = {
  model: "gpt-4o-mini",
  apiKey: process.env.OPENAI_API_KEY!,
};

const billingAgent = new AssistantAgent({
  name: "billing_agent",
  systemMessage: "You handle billing, invoices, refunds, and payment issues.",
  llmConfig,
});

const claimsAgent = new AssistantAgent({
  name: "claims_agent",
  systemMessage: "You handle insurance claims, incidents, and policy questions.",
  llmConfig,
});

const generalAgent = new AssistantAgent({
  name: "general_agent",
  systemMessage: "You handle general customer support questions.",
  llmConfig,
});

const userProxy = new UserProxyAgent({
  name: "user_proxy",
});
  1. Wire the router into an execution function. This is where conditional routing becomes useful: you choose the agent before calling AutoGen instead of asking one large assistant to figure everything out.
async function handleRequest(input: string) {
  const route = routeMessage(input);

  const agentMap = {
    billing: billingAgent,
    claims: claimsAgent,
    general: generalAgent,
  } as const;

  const selectedAgent = agentMap[route];

  console.log(`Routing to ${selectedAgent.name}`);

  const result = await userProxy.initiateChat(selectedAgent, input);
  return result;
}
  1. Add a second layer of routing for escalation or fallback. In production systems, this is where you branch on confidence, refusal text, or whether the response contains a structured flag like needs_human=true.
async function handleWithFallback(input: string) {
  const firstRoute = routeMessage(input);
  const primary =
    firstRoute === "billing" ? billingAgent :
    firstRoute === "claims" ? claimsAgent :
    generalAgent;

  const result = await userProxy.initiateChat(primary, input);
  const text = String(result);

  if (text.toLowerCase().includes("i don't have enough information")) {
    console.log("Escalating to general agent");
    return userProxy.initiateChat(generalAgent, `${input}\n\nPlease provide a safer fallback answer.`);
  }

  return result;
}
  1. Put it together in an executable entry point. This gives you a concrete script you can run and extend into a larger workflow engine later.
async function main() {
  if (!process.env.OPENAI_API_KEY) {
    throw new Error("OPENAI_API_KEY is required");
  }

  const samples = [
    "I need help with my invoice",
    "I want to file an insurance claim after an accident",
    "How do I reset my account password?",
  ];

  for (const sample of samples) {
    const response = await handleRequest(sample);
    console.log("\nINPUT:", sample);
    console.log("OUTPUT:", response);
  }
}

main().catch((err) => {
  console.error(err);
  process.exit(1);
});

Testing It

Run the script with your preferred TypeScript runner and watch the routing logs. You should see billing requests go to billing_agent, claim-related requests go to claims_agent, and everything else fall back to general_agent.

Use test prompts that intentionally overlap, like “My invoice is wrong after filing a claim,” to confirm your precedence rules behave as expected. If you need stricter behavior, move from keyword routing to structured classification using an LLM that returns a fixed JSON route label.

Also verify that your fallback path triggers when the primary agent returns an unhelpful answer. In real systems, this is where you add observability tags like route=claims and fallback=true so you can trace bad branches later.

Next Steps

  • Replace keyword routing with an LLM classifier that returns { route, confidence }
  • Add JSON-schema validation for route outputs before dispatching agents
  • Extend the pattern into multi-hop workflows with tool calls and human escalation

Keep learning

By Cyprian Aarons, AI Consultant at Topiax.

Want the complete 8-step roadmap?

Grab the free AI Agent Starter Kit — architecture templates, compliance checklists, and a 7-email deep-dive course.

Get the Starter Kit

Related Guides