n4nAI

Five multi-agent orchestration patterns explained

Engineer-focused explainer of five multi-agent orchestration patterns with runnable code: supervisor, hierarchical, blackboard, pipeline, and debate.

n4n Team4 min read896 words

Audio narration

Coming soon — every post will get a voice note here.

Most production LLM systems outgrow a single prompt. The five multi-agent orchestration patterns below show how to decompose work, share state, and resolve conflicts between specialized agents without boiling the ocean. Each pattern trades off latency, token cost, and debuggability differently; pick based on the shape of your task.

1. Supervisor-Worker Routing

A supervisor agent inspects the incoming request and dispatches it to a pool of worker agents, each with a narrow skill. This pattern fits help desks, triage, or any workload where the task type is identifiable up front but the handling logic diverges sharply. The supervisor stays dumb on purpose: it routes, it does not execute.

The supervisor is just a model call with structured output. Define a JSON schema that lists available workers, and parse the response to route. Workers can be other model endpoints, retrieval pipelines, or code executors. When calling multiple providers, an OpenAI-compatible gateway such as n4n.ai gives you one client and automatic fallback when a provider is rate-limited, which keeps the supervisor resilient without custom retry code.

from openai import OpenAI
import json

client = OpenAI(base_url="https://api.n4n.ai/v1", api_key="KEY")

WORKERS = {"refund": refund_agent, "tech": tech_agent, "sales": sales_agent}

def supervise(task: str) -> str:
    resp = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "system", "content": "Route to worker: 'refund'|'tech'|'sales'"},
                  {"role": "user", "content": task}],
        response_format={"type": "json_object"}
    )
    route = json.loads(resp.choices[0].message.content)["worker"]
    return WORKERS[route](task)

Keep the supervisor stateless. If you need context across turns, persist a session id and feed history to the worker, not the router. The win is isolation: a bad worker prompt can’t corrupt the router, and you can swap a worker for a cheaper model without touching orchestration logic. The failure mode is misrouting—log every decision and sample-audit the supervisor weekly.

2. Hierarchical Decomposition

When a task is too large for one worker, a parent agent splits it into subtasks, spawns child agents, and reduces their results. Think of a planning agent that breaks “write a market analysis” into research, drafting, and citation-checking branches. This is the classic tree-of-thought approach, but with explicit agent boundaries instead of a single model sampling.

Implement this as recursive function calls. Each node decides whether to handle directly or delegate. Set a max depth to avoid runaway trees, and return intermediate artifacts so you can inspect the breakdown.

def decompose(task, depth=0, max_depth=3):
    if depth >= max_depth or is_atomic(task):
        return execute_worker(task)
    subtasks = plan_subtasks(task)  # LLM call returning list[str]
    results = [decompose(s, depth+1, max_depth) for s in subtasks]
    return synthesize(results)  # another LLM or rule-based merge

The failure mode is fan-out cost. Each level multiplies token spend, and a vague planner explodes the tree. Cap breadth at three to five children per node and cache intermediate outputs by content hash. In practice, hierarchical multi-agent orchestration patterns shine for multi-step generation but need hard token budgeting and a timeout per branch. Use a structured reduce step rather than concatenating raw child text.

3. Blackboard Shared Memory

Blackboard flips the messaging model: agents don’t talk to each other directly. They read and write a shared state object (the blackboard). A coordinator triggers agents when their input conditions are met. This decouples producers from consumers—an extraction agent writes entities, a later summarizer reads them, and neither knows the other exists.

Use a simple dict behind an async lock for single-process runs, or Redis if distributed. Agents declare preconditions; the loop wakes them when those hold.

import asyncio, redis

r = redis.Redis()
async def extractor(text):
    ents = await llm_extract(text)
    r.hset("bb", "entities", json.dumps(ents))

async def summarizer():
    if r.hexists("bb", "entities"):
        ents = json.loads(r.hget("bb", "entities"))
        return await llm_summarize(ents)

Blackboard suits long-running jobs where partial progress matters—document ingestion, multi-modal assembly, or human-in-the-loop review. The tradeoff is a global state that can become a coordination bottleneck if every agent polls it. Mitigate with pub/sub notifications instead of polling, and version keys so a stale reader doesn’t poison a new writer. Among multi-agent orchestration patterns, this one has the highest ops overhead but best survivability across crashes.

4. Sequential Pipeline Handoff

The pipeline pattern chains agents like Unix filters. Each stage takes an artifact, transforms it, and passes it on. No agent sees the full picture; they own a slice of the transform. A typical text pipeline: outline → draft → critique → rewrite.

You can express it as plain function composition, which makes unit testing trivial. Validate the artifact schema between stages with pydantic to fail fast.

from pydantic import BaseModel

class Draft(BaseModel):
    text: str

def pipeline(task):
    outline = agent_outline(task)
    draft = Draft(text=agent_draft(outline))
    critique = agent_critique(draft.text)
    return agent_rewrite(draft.text, critique)

Because stages are independent, you can swap any for a cheaper model or a deterministic rule. Use a small model for outline and a large one for rewrite. This is among the most cost-effective multi-agent orchestration patterns for fixed workflows. Parallelize stages only when they truly don’t depend on each other; most pipelines are inherently serial. Add a dead-letter stage that captures malformed output instead of letting it silently propagate.

5. Adversarial Debate and Consensus

When correctness matters more than latency, run agents that argue opposing positions, then synthesize. One agent proposes, another attacks, a judge picks or merges. This surfaces hidden assumptions and reduces single-model sycophancy.

Structure as a fixed number of rounds. Collect transcripts and feed them to a final aggregator with explicit instructions to resolve contradictions, not average them.

def debate(topic, rounds=2):
    prop = propose(topic)
    opp = oppose(prop)
    for _ in range(rounds):
        prop = rebut(prop, opp)
        opp = rebut(opp, prop)
    return judge(prop, opp)

Debate improves reasoning on math, policy, and security review, but doubles or triples token cost. Set temperature asymmetrically—lower for judge, higher for advocates—to avoid collusion. Use it only where the cost of error is high; it is the heaviest of the multi-agent orchestration patterns covered here. Keep round count odd to avoid ties, and log the full transcript for audit.

Synthesis

Pattern Best for State Cost risk
Supervisor-Worker Triage, routing Stateless Low
Hierarchical Complex generation Tree High fan-out
Blackboard Long jobs, partial progress Shared store Contention
Pipeline Fixed transforms Artifact pass Low
Debate High-stakes reasoning Round transcripts High

Pick based on whether you need isolation, decomposition, shared context, linear transform, or verification. Most real systems mix two: a supervisor fronting a pipeline, or a hierarchy with blackboard leaves. Start with the simplest pattern that bounds your failure domain, then add complexity only after measuring where agents actually break down.

Tagsmulti-agent-orchestrationai-agentsdesign-patterns

Written by

n4n Team

The team building n4n — a single OpenAI-compatible API in front of 240+ models, with automatic fallback, load balancing and pay-per-token metering.

More from n4n Team →

All multi-agent orchestration patterns posts →