Choose the Topology by Where the Decision Is Made
Router, supervisor, handoff, and a code-driven graph differ not in the external drawing but in the owner of the control flow and the way the result is assembled. A router makes one choice: it classifies the input and sends it to one or several independent executors. A supervisor holds the goal, calls workers as tools, gathers outputs, and decides whether a next step is needed. A handoff transfers ownership: the active specialist changes and continues the work in its own narrow context.
The OpenAI Agents SDK highlights two common patterns: in agents-as-tools the manager keeps control and combines outputs, in a handoff the specialist becomes the active agent for the next part of the interaction. LangChain separately stresses the router/supervisor difference - a router is usually a single dispatch step without long conversational state, a supervisor supports dynamic multi-step coordination.
The key fork is simple. If the decision is known in advance - write an ordinary graph in code: the model executes the substantive nodes but doesn't choose obvious branches, retry policy, and gate. If the decision depends on the meaning of the result - let the supervisor pick a worker from a short allowlist, but check the route, budget, and termination in the control plane. And even when the model chooses, routing stays a checkable policy.
import type { AgentId, TaskKind, WorkItem } from "./types.js";
const ROUTES: Readonly<Record<TaskKind, AgentId>> = {
plan: "planner",
test: "tester",
security: "security",
docs: "docs",
review: "reviewer"
};
export function routeTask(task: WorkItem): AgentId {
return ROUTES[task.kind];
}
export function explainRoute(task: WorkItem): string {
return `${task.kind} -> ${routeTask(task)} by deterministic policy`;
}A swarm is not a goal. Decentralized transfer of control is useful when the local specialist knows the next recipient better. If the path is known to the application, a swarm only makes tracing and cost limits harder.
The topology is chosen. Let's cover its variants one by one - and the first principle: keep a known order in ordinary code.