AI Agents and MCP in Production · Running Agents in Production

Multi-Agent Systems: Orchestrator and Workers

Agents chatting freely duplicate work and loop; an orchestrator with clear subtasks and a shared budget gets the benefit without the chaos.

Create a free account to save this page, take notes and track your progress. Sign in

Scenario

Example company: a research assistant answers market questions. To make it "smarter", the team splits it into four agents (researcher, analyst, critic, writer) that talk to each other in a shared chat until the critic is satisfied.

What you see in production

Answers are slightly better but cost six times as much and sometimes never finish: the critic and analyst argue for 40 turns. Logs show two researcher agents fetching the same sources. When an answer is wrong, nobody can tell which agent introduced the error.

The naive code

agents = [researcher, researcher_2, analyst, critic, writer]
chat = [task]
while not critic.satisfied(chat):                  # no turn limit, no ownership
    for agent in agents:
        chat.append(agent.respond(chat))           # everyone sees and reacts to everything

Reproduce it

Free-form collaboration versus an orchestrator that splits the task into independent subtasks:

Duplicate work and turns: free chat versus orchestrator-workersTry it yourself
SOURCES = {"pricing": ["s1", "s2"], "competitors": ["s3", "s4"], "regulation": ["s5"]}

def free_chat(max_turns=40):
    fetched, turns = [], 0
    for turn in range(max_turns):
        turns += 1
        for researcher in ("r1", "r2"):                    # both researchers chase whatever is mentioned
            topic = list(SOURCES)[turn % len(SOURCES)]
            fetched += SOURCES[topic]
        if turn == max_turns - 1:
            break                                          # critic never quite satisfied
    return turns, len(fetched), len(set(fetched))

def orchestrated():
    plan = [{"worker": f"w{i}", "topic": t, "budget": 3} for i, t in enumerate(SOURCES)]   # independent subtasks
    fetched = [s for sub in plan for s in SOURCES[sub["topic"]]]                           # run in parallel
    turns = 1 + len(plan) + 1                                                              # plan, workers, synthesis
    return turns, len(fetched), len(set(fetched))

for name, (turns, total, unique) in [("free chat", free_chat()), ("orchestrator", orchestrated())]:
    print(f"{name:12} model turns={turns:3}  sources fetched={total:3}  unique={unique}")

Output

free chat    model turns= 40  sources fetched=134  unique=5
orchestrator model turns=  5  sources fetched=  5  unique=5

Root cause

Multiple agents multiply cost, and without structure they also multiply coordination problems: no one owns a subtask, everyone reads everything, and there is no stopping rule. Multi-agent designs pay off when work splits into independent parts that can run in parallel with separate contexts; they hurt when agents must constantly share state.

The fix

Use the orchestrator-workers pattern:

  • An orchestrator plans: it splits the task into independent subtasks, each with a clear objective, output format, tools and budget.
  • Workers run in parallel with their own focused context and return compact results (not their whole transcript).
  • The orchestrator synthesises, checks for gaps and may launch a follow-up round, within a global budget.
  • Every result is attributed to the worker that produced it, so errors are traceable.

Start with a single agent and add workers only when tasks are clearly parallel (broad research, independent files, separate data sources).

Trade-offs

  • Multi-agent systems use several times more tokens than one agent; worth it only when the task's value justifies it.
  • Workers do not see each other's findings; tasks with tight dependencies (one step needs the previous step's result) suit a single agent better.
  • More moving parts mean more to trace and evaluate.

Detect it in production

  • Tokens and model calls per task, broken down by agent.
  • Duplicate tool calls across workers (same tool, same arguments).
  • Tasks hitting the global budget or turn limit.

Key points

  • Use multiple agents for independent, parallel subtasks; otherwise prefer one agent.
  • An orchestrator assigns clear subtasks with budgets; workers return compact, attributed results.
  • Free-form agent chat duplicates work and needs a stopping rule it rarely has.

Further reading: How we built our multi-agent research system · Building effective agents

Practice questions

When does a multi-agent design beat a single agent?

Answer

When the task splits into independent subtasks that can run in parallel with separate contexts, such as broad research across many sources. With tight dependencies between steps, a single agent is usually better and cheaper.

What should an orchestrator give each worker?

Answer

A clear objective, the expected output format, the tools it may use, boundaries that avoid overlap with other workers, and a budget.

Why do free-form multi-agent chats tend to be expensive and unreliable?

Answer

Every agent reads and reacts to everything, nobody owns a subtask, work is duplicated, and there is no reliable stopping rule, so turns and tokens grow while errors become hard to attribute.

Open in the app to mark lessons complete, take notes and track your progress.

Was this page helpful?