Orchestration
Orchestration is the pattern above the single agent: pipeline (fixed sequence), supervisor (one agent commands N) or swarm (N agents self-organize). It defines who talks to whom, when and in what order.
What it is
Orchestration coordinates multiple agents (or multiple steps of one agent) in a larger flow. While agent frameworks deal with an agent's inner loop, orchestration deals with the meta-loop: who comes in, who goes out, how long it lasts, where the state is persisted.
The question orchestration answers is not 'what does this agent do', but 'how does this agent talk to the next one'. Without orchestration, each agent becomes an island and the only glue between them is copy and pasting text.
The three classic patterns
Three patterns cover 90% of real cases. The choice among them depends on who decides the next step:
pipeline: fixed sequence. Each step receives the output of the previous one; the order is known at design time. Good for ETL with LLMs, preprocessing, or any case where the division of labor is clear.
supervisor: one agent (the supervisor) looks at the state and decides which subagent to call now. The structure exists, but the path is decided at runtime. Good for flows with context-dependent branches.
swarm: N agents coordinate via messages, with no central owner. More flexible, harder to audit; reserved for exploration or ill-defined tasks.
pipeline A -> B -> C -> D
fixed order, predictable cost, easy to audit
supervisor A (decides) -> B or C -> A (decides again)
dynamic branch, variable cost, supervisor is a single point of failure
swarm A <-> B <-> C <-> D
no owner, more expensive, harder to reproduceState and checkpoint: how to survive failures
Agents are flaky by nature: external APIs go down, models return bad responses, timeouts happen in the middle of the flow. Without a checkpoint, a 5-minute failure means starting from scratch, with the prompt spent and money burned.
A checkpoint is the act of persisting the flow's state to durable storage after each step, with a unique ID. When execution restarts (due to a crash, deploy or manual resume), the runtime reads the last checkpoint and continues from where it stopped, without repeating work already done.
Temporal and Prefect model this as a durable workflow: every function can be async, and the runtime knows how to re-execute from the point of failure. LangGraph exposes the same concept as thread_id with persistence in Redis or Postgres.
from temporalio import workflow, activity
from datetime import timedelta
@activity.defn
async def extract(pdf_url: str) -> dict:
return {'text': '...'}
@activity.defn
async def summarize(text: str) -> str:
return 'summary'
@workflow.defn
class ReportFlow:
@workflow.run
async def run(self, pdf_url: str) -> str:
# Each execute_activity becomes a step persisted by Temporal;
# if the worker goes down midway, the workflow resumes from the point of failure
# without repeating the activity already completed.
extracted = await workflow.execute_activity(
extract,
pdf_url,
start_to_close_timeout=timedelta(minutes=2),
)
return await workflow.execute_activity(
summarize,
extracted['text'],
start_to_close_timeout=timedelta(minutes=2),
)Pipeline with LangGraph: supervisor + subagents
LangGraph lets you model supervisor and pipeline in the same graph: each node is a function (or a subagent), and each conditional edge carries the 'which node now' logic. The graph is deterministic enough for auditing and flexible enough to react to the state.
In the example below, an orchestrator decides between calling 'researcher' or 'writer' depending on the state; each of them is a subagent with its own inner loop.
import { StateGraph, END } from '@langchain/langgraph';
type S = { messages: any[]; stage: 'pending' | 'researched' | 'written' };
async function orchestrator(s: S) {
if (s.stage === 'pending') return 'researcher';
if (s.stage === 'researched') return 'writer';
return END;
}
const graph = new StateGraph<S>({ channels: { messages: [], stage: 'pending' } })
.addNode('orchestrator', orchestrator)
.addNode('researcher', async (s) => ({ ...s, stage: 'researched' as const }))
.addNode('writer', async (s) => ({ ...s, stage: 'written' as const }))
.addEdge('__start__', 'orchestrator')
.addConditionalEdges('orchestrator', (s) => s.stage,
{ researcher: 'researcher', writer: 'writer', written: END, [END]: END })
.addEdge('researcher', 'orchestrator')
.addEdge('writer', 'orchestrator')
.compile({ checkpointer: redisCheckpointer });Where orchestration meets the rest of the system
- queues and workers: orchestration turns long calls into asynchronous jobs; each step becomes a worker
- idempotency by design: a checkpoint without idempotency means re-executing and duplicating side effects
- observability: each step emits metrics, logs and spans; without that, debugging multi-agent systems is guesswork
- human-in-the-loop: in critical flows, the supervisor pauses and waits for approval before the next step
Limitations
- the orchestrator is a place for bad decisions to accumulate; a poorly calibrated supervisor becomes an infinite loop
- checkpoints have a cost: each persistence adds latency and coupling to a database
- swarm and flexible supervisor are hard to reproduce; the same prompt can produce different flows
- tooling is still young: Temporal is solid for deterministic code, but LLM support inside it is recent
connections on the map
- Agent frameworkscoordinates withOrchestration
updated on 14/10/2026 · sources to go deeper · report an error
Study before
test what you learned
Loading questions…