How to Build a Multi-Agent System with Claude
Building a multi-agent system with Claude means running several independent Claude instances, each with a narrow role, and coordinating them through a controller that passes messages, tool results, or structured data between them. Instead of one giant prompt trying to do research, writing, and validation at once, you split the work: one agent searches, one agent drafts, one agent critiques, and a coordinator agent (or plain code) decides what happens next.
This is not a framework you install — it's an architecture you build on top of the Claude API (or a wrapper like SubToAPI). Below is a concrete approach: the core patterns, how to structure agent communication, where tool calling fits in, and the failure modes that trip people up.
Why Split Work Across Agents at All
A single Claude call with a huge system prompt covering ten responsibilities tends to degrade: instructions get ignored, context gets diluted, and the model mixes up which rule applies when. Splitting into agents gives you:
- Focused system prompts. Each agent has one job and a short, unambiguous prompt.
- Independent context windows. A research agent's scratchpad doesn't pollute the writer agent's context.
- Retry and validation boundaries. You can re-run just the failing agent instead of the whole pipeline.
- Different models or settings per role. A classifier agent can run cheap and fast; a synthesis agent can use a longer context and lower temperature.
Core Architectures
1. Orchestrator–Worker (Sequential Pipeline)
One controller agent (or plain application code) breaks a task into steps and calls specialized worker agents in sequence. This is the easiest pattern to reason about and debug.
User request
→ Planner agent (breaks task into subtasks)
→ Research agent (gathers info, uses tools)
→ Writer agent (drafts output)
→ Reviewer agent (checks against criteria)
→ Final response
Each arrow is a separate API call with its own system prompt. The output of one agent becomes part of the input to the next.
2. Router + Specialists
A lightweight classifier agent reads the incoming request and routes it to one of several specialist agents (billing, technical support, sales) that never see traffic outside their domain. This keeps each specialist's prompt small and its behavior predictable.
3. Debate / Critique Loop
Two agents argue: a "generator" produces an answer, a "critic" points out flaws, and the generator revises. This loop runs a fixed number of rounds (2–3 is usually enough) or until the critic returns no issues. Useful for code review, fact-checking, and anything where a second pass materially improves quality.
4. Parallel Fan-Out / Fan-In
Multiple agents work on independent sub-problems simultaneously (e.g., summarizing five documents), and a final aggregator agent merges their outputs. This is the fastest pattern wall-clock-wise since calls run concurrently, but it needs a reliable merge step to avoid contradictions.
A Minimal Working Example
Here's a sequential orchestrator with two Claude-backed agents, written against the Claude Messages API shape (the same pattern works unchanged against SubToAPI's /v1/messages endpoint, since it mirrors the standard Messages API):
async function callAgent(systemPrompt, userMessage) {
const res = await fetch("https://api.subtoapi.app/v1/messages", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
model: "claude-sonnet-4-5",
max_tokens: 1024,
system: systemPrompt,
messages: [{ role: "user", content: userMessage }]
})
});
const data = await res.json();
return data.content[0].text;
}
async function runPipeline(task) {
const researchNotes = await callAgent(
"You are a research agent. Extract key facts relevant to the task. Output bullet points only, no commentary.",
task
);
const draft = await callAgent(
"You are a writer agent. Turn the given research notes into a clear, well-structured answer.",
`Task: ${task}\n\nResearch notes:\n${researchNotes}`
);
const review = await callAgent(
"You are a reviewer agent. Check the draft for factual consistency with the research notes and clarity. Reply with either 'APPROVED' or a numbered list of required fixes.",
`Research notes:\n${researchNotes}\n\nDraft:\n${draft}`
);
return { researchNotes, draft, review };
}
Each function call is a distinct agent with its own system prompt and no shared conversation history — the controller code is what threads context between them. This keeps each agent's context window small and its behavior easy to test in isolation.
Where Tool Calling Fits In
Individual agents in your system often need tools — a research agent might call a search API, a coding agent might run a linter. Claude's tool use (function calling) works the same inside a multi-agent system as it does standalone: define a tools array, let Claude request a tool call, execute it in your code, and return the result as a tool_result block. See /docs/tools for the request/response shape. The key difference in a multi-agent setup is scoping — give each agent only the tools relevant to its role, not a shared toolbox, so agents can't accidentally call something outside their responsibility.
Managing State and Message Passing
The controller, not Claude, should own the state machine. Concretely:
- Store each agent's output in your application (a database row, a job object, or just an in-memory array for short pipelines).
- Pass only what the next agent needs — don't forward entire conversation histories between agents unless the task requires it.
- Use structured output (JSON) for anything that needs to be parsed programmatically, like a router's classification or a reviewer's approve/reject decision. Ask for a specific JSON shape in the system prompt and validate it before passing it downstream.
- Cap loop iterations (debate rounds, retry attempts) explicitly in code — never let an agent decide when to stop indefinitely, or you risk runaway API usage.
Common Failure Modes
- Context loss between agents. If agent B needs information agent A saw but didn't explicitly write down, it will hallucinate. Force intermediate agents to output structured summaries, not just prose.
- Infinite critique loops. Debate patterns without a hard round limit can bounce forever. Cap at 2–3 rounds.
- Cost blowup. Five sequential agent calls cost five times as much as one. Fan-out patterns multiply that further. Track token usage per agent during development — SubToAPI's dashboard shows per-key usage metadata, which is useful for spotting which agent in a pipeline is the expensive one.
- Silent tool failures. If a worker agent's tool call fails, make sure that failure is surfaced to the controller and, if relevant, to downstream agents — don't let a broken research step silently produce an empty context for the writer agent.
Getting Started Practically
Start with the orchestrator-worker pattern and exactly two agents before adding more — it's the easiest to debug and forces you to nail down the interface between agents (what gets passed forward, in what format). Once that works reliably, add a router or a critique loop only where you have evidence a single pass isn't good enough. If you're prototyping against the API, /docs/quickstart covers authentication and your first request, and /docs/messages documents the full request format each agent call will use.
FAQ
Do I need a framework like LangGraph to build a multi-agent system with Claude? No. Frameworks add convenience for complex graphs, but a sequential pipeline of plain function calls (as shown above) is often easier to debug and sufficient for most real workloads. Add a framework only once your orchestration logic genuinely needs graph-like branching.
How many agents is too many? There's no fixed number, but each additional agent adds latency and cost linearly (or worse, in fan-out patterns). Most production systems use 2–5 agents. If you're reaching for ten, you likely have one agent doing too little useful work per call.
Should agents share the same API key? It depends on your isolation needs. Using separate application keys per agent role (for example, via SubToAPI's per-key dashboard) makes it easier to track cost and usage per agent without extra logging code — see /pricing for plan details.