criogenai

Multi-agent

Router

One call picks the specialist; only that one runs.

A router reads the request once and sends it down exactly one branch. The specialists never see requests that are not theirs, and the branches that are not chosen cost nothing.

The choice can be a model call, as here, or plain code (keywords, a classifier). Either way the paths are fixed in the graph, so the run is easy to predict and test.

Use it when
Clear categories of request
Specialists with different tools or permissions
Predictable cost: one routing call plus one branch
Skip it when
A request needs several specialists (Supervisor)
Categories overlap heavily

Run it

Scripted model · no key needed · CriogenAI engine in your browser
Ready
billingtechnicalsalesRequestRouterBillingTechnicalSalesReply
The runmodel 0 · tools 0
  1. Press Run to watch the pattern work, step by step.
Try it with your own key optional: a real model makes the decisions; the tools stay simulated
  • Your key stays in this tab's memory: not saved, not in cookies or the address bar. Reload or close the tab and it is gone.
  • Your browser calls OpenAI directly. Nothing passes through criogenai.com, and nothing is logged.
  • Each run makes at most 16 short model calls, billed to your account. Tools, data and side effects are simulated.

This run is the TypeScript code below, running on the CriogenAI engine in your browser (WebAssembly). Without a key, a scripted model stands in for MODEL; with your key, your provider answers, called from this tab. The tools return made-up data.

Build it with CriogenAI

Built from primitives: Workflow.route
class Route(BaseModel):
    team: str  # "billing" | "technical" | "sales"

classify = Agent(MODEL, instructions="Classify the request: billing, technical or sales.", output=Route)
teams = {name: Agent(MODEL, instructions=f"You are the {name} support specialist.") for name in ("billing", "technical", "sales")}

async def route(state: Dict[str, Any]) -> Dict[str, Any]:
    return {"team": (await classify.run(state["input"])).output.team}

wf = Workflow()
wf.add_node("route", route)
for name, agent in teams.items():
    wf.add_node(name, agent)  # an agent node reads and writes `messages`
    wf.edge(name, END)
wf.start("route").route("route", lambda s: s["team"], {n: n for n in teams})
result = await wf.compile().run({"input": "I was charged twice", "messages": [{"role": "user", "content": [{"type": "text", "text": "I was charged twice"}]}]})
const teams = ["billing", "technical", "sales"] as const;
const classify = new Agent<{ team: (typeof teams)[number] }>({
  model: MODEL,
  instructions: "Classify the request: billing, technical or sales.",
  output: { type: "object", properties: { team: { enum: [...teams] } }, required: ["team"] },
});

const wf = new Workflow().node("route", async (state) => ({ team: (await classify.run(String(state.input))).output?.team }));
for (const name of teams) {
  wf.addNode(name, new Agent({ model: MODEL, instructions: `You are the ${name} support specialist.` })); // reads and writes `messages`
  wf.edge(name, END);
}
wf.start("route").route("route", (s) => String(s.team), Object.fromEntries(teams.map((n) => [n, n])));
const result = await wf.compile().run({ input: task, messages: [{ role: "user", content: [{ type: "text", text: task }] }] });