Why Every Serious Agent Orchestrator Is Secretly a DAG
- agentic-ai
- orchestration
- graph-theory
- multi-agent
- mcp
The agent skill system is expanding rapidly. What we used to call a "bot" — a single-purpose assistant executing one task — is quietly evolving into something far more ambitious: an orchestrator that takes a user's intent, coordinates with multiple agents, and dynamically loads skills and MCP servers on demand.

And ecosystems need rules. There's a degree of orchestration, a kind of mutual coexistence, that has to emerge between agents and servers — one that answers the question every serious builder is now circling:
How do agents and servers work together to complete tasks, without a human in the loop?
To get there, we need to break the problem into three phases:
- Communication — How do agents reliably talk to each other?
- Orchestration — Do we even need a central orchestrator, or can agents self-organize?
- Adaptability — How do we make the whole system responsive to the current state of the world, not the state we designed for last week?
Communication: how do agents responsibly talk to each other?
Before we talk about orchestration, we have to talk about wiring. In any multi-agent system, agents communicate in one of two default patterns — sequentially, where Agent A's output feeds Agent B, or in parallel through a message bus.
A message bus is a shared communication channel. Agents publish messages, other agents subscribe to the ones they care about, and the system handles delivery. Think of it as a group chat for agents — anyone can post, anyone can read, no one needs to know who else is listening. It decouples senders from receivers, which sounds clean on paper.
In practice, both patterns break down for the same underlying reason: neither one models the relationships between agents explicitly. And once you've felt that pain, you're already halfway to a graph.
You feel the breakdown in three ways:
- Silent failures. A malformed output passes through, the next agent improvises, and the error compounds three steps downstream. No stack trace, no lineage. You can't debug a dependency you never declared.
- Coordination overhead. Five agents on a message bus is a group chat. Fifty is a stadium where everyone is shouting. Without a structure that says who depends on whom, you can't reason about ordering, parallelism, or failure isolation.
- Lossy compression. Each handoff paraphrases and truncates. By the final agent, the original intent has been through a game of telephone — because the pipeline was a chain, not a graph.
The first fix is discipline at the boundary — typed contracts. A strict schema (JSON Schema, Pydantic, protobuf, or MCP) defines what an agent consumes and produces. Malformed handoffs fail loudly at the edge.
But contracts only solve the interface. They don't tell you what the edges are, who depends on whom, or what's safe to run in parallel. For that, you stop thinking about agents as a list — and start thinking about them as nodes in a graph.
Orchestration: do we need a central orchestrator?
Before we can answer this, let's revisit Amdahl's law.
The speedup you get from parallelizing a system is capped by the part of it that can't be parallelized. If 30% of your workflow is sequential, even infinite parallel workers only get you about a 3.3× speedup. The serial fraction is the ceiling.
For agent systems, this matters more than people realize. You don't need to parallelize everything — you just need to find the parts that can run together and stop running them in a line. Even a small amount of parallelism, applied at the right place, collapses agent-to-agent latency dramatically.
Now imagine you have five agents and an orchestrator. Each agent is a node in a graph. The links between them — the edges — show dependency: Agent B can't start until Agent A is done.
Let's make this concrete. Picture an orchestrator that acts as your personal assistant chef. We'll call it Stan. You ask Stan, "What's for dinner tonight?" Stan can't answer that alone. It employs a small team:
- Fridge agent — checks what's in stock right now.
- Pantry agent — checks dry goods, spices, staples.
- Preferences agent — pulls your dietary preferences and what you've eaten this week.
- Recipe agent — proposes dishes given the available ingredients and preferences.
- Shopping agent — figures out what's missing and drafts a grocery list.
Plus a couple of MCP servers — one for the smart fridge, one for your grocery delivery app.
Now, designing the communication. The fridge agent and the pantry agent don't depend on each other. They can run in parallel. The preferences agent also doesn't depend on either — it can run in parallel too. But the recipe agent depends on all three of them: it can't propose a dish until it knows what's in stock and what you'll actually eat. And the shopping agent depends on the recipe agent — it only knows what to buy once a dish is chosen.
This shape has a name. The recipe agent is a fan-in node — multiple parallel branches converge into it. The orchestrator at the start, dispatching to fridge, pantry, and preferences simultaneously, is a fan-out node — one node spawning many parallel children. Every non-trivial agent DAG is some combination of these two patterns.
A quick pitstop to refresh our memory
Directed: edges point one way. Acyclic: no path ever loops back on itself. is a directed acyclic graph where each node represents a unit of work and each edge represents a dependency — and no path through the graph ever loops back on itself. A fan-out edge is one node branching out to many — one source, many targets, parallel work dispatched. A fan-in edge is many nodes converging into one — many sources, one target, parallel work synchronized.
Which brings us to the scheduling problem: how does Stan know what to run, and when?
Kahn's topological sorting algorithm
Kahn's algorithm is a way to take a graph of dependencies and produce a valid execution order. It works by finding all the nodes with no incoming dependencies (in our case: fridge, pantry, preferences), running them, then "removing" them from the graph and repeating. The recipe agent only becomes runnable once its three parents are done. The shopping agent only becomes runnable once the recipe agent is done.
The algorithm naturally surfaces parallelism — every node that becomes available at the same step can run together — and it naturally enforces order without anyone hand-coding it.
That's the whole trick. The DAG describes what depends on what; Kahn's decides when. And here's where Amdahl's law closes the loop: the fridge, pantry, and preferences agents running in parallel is exactly the optimization Amdahl's promised — a chunk of the workflow that doesn't have to be sequential, suddenly isn't. The serial parts (the recipe agent waiting on its three parents, the shopping agent waiting on the recipe) are the unavoidable floor. Everything above that floor is yours to compress.
That's why we need a central orchestrator — and why a graph is the right shape for it to think in.
Adaptability: how does the graph respond to a world it didn't plan for?
A static DAG is a hypothesis. It assumes the agents you wired up yesterday are the right ones for today's request, that the MCP servers you registered last week are the ones this task needs, and that nothing about the world has shifted in between. That's almost never true.
Stan can plan dinner. But what happens when the smart-fridge MCP server is down? When the user adds a new dietary constraint mid-conversation? When a brand-new "leftovers agent" gets registered an hour after the workflow started?
A fixed graph can't handle any of these. It needs to be adaptable — and adaptability, in graph terms, means the graph itself is allowed to change at runtime. Building in this flexibility is of utmost importance in any real-world scenario — otherwise the entire concept of a DAG looks shiny only in theory.
Three techniques are doing the heavy lifting here:
- Dynamic skill loading. Skills and MCP servers shouldn't be hardcoded into the graph. They should be mounted into it, the way a filesystem is mounted into an OS — discovered on demand, loaded only when needed, unmounted when the task is done.
- Conditional edges and runtime DAG mutation. This is where the 2026 frameworks have moved fastest. LangGraph v0.3.0 introduced native DAG-style workflows with conditional branching, durable state management, and time-travel debugging.LangGraph v0.3.0 — native DAG-style workflows with conditional branching and durable state.https://langchain-ai.github.io/langgraph/ The graph is no longer a contract written before execution — it's a structure that rewrites itself as it learns.
- Adaptive pruning. Not every task needs the full DAG. Adaptive graph pruning learns to prune both edges and agents from a complete communication graph using task and agent embeddings, producing task-adaptive sparse topologies.
Further reading
For a deeper dive into dynamic, mutable DAGs for agent orchestration: