RAG for Knowledge-Grounded Agents

An ungrounded agent is a confident liar. Ask it about your company’s refund policy, last quarter’s numbers, or a customer’s order history, and it will produce a fluent, plausible, well-structured answer — drawn from its training data, its priors, or nothing at all. The words are right; the facts may be invented. That’s the hallucination problem, and it’s the single biggest barrier between a demo and a system you’d let talk to customers.

Retrieval-Augmented Generation (RAG) is the answer the industry converged on, and in 2026 it’s the default architecture for any agent that needs to answer from private or current data. The idea is simple: instead of hoping the knowledge is baked into the model’s weights, you retrieve the relevant facts at query time and hand them to the model as context, so its answer is grounded in real, citable documents.

RAG earns its place over the alternative — fine-tuning — for most knowledge tasks because your knowledge changes. Fine-tuning bakes information into weights: expensive, slow, and stale the moment a policy updates. RAG reads from a source you can edit in seconds. The rule of thumb: use RAG when your data changes, you need citations, or you face diverse queries; reach for fine-tuning when you need a consistent style, format, or domain-specific reasoning. Knowledge-grounding is squarely RAG’s job.

The anatomy of a RAG pipeline

Every RAG system has two halves: an offline ingestion path that prepares your knowledge, and an online query path that answers questions against it.

Diagram illustrating the anatomy of a RAG (Retrieval-Augmented Generation) pipeline, showing the processes involved in data ingestion and query handling. The pipeline includes stages for document ingestion, chunking, embedding, and querying with a focus on vector databases and language models.
  • Ingestion: split documents into chunks, convert each chunk into an embedding (a vector capturing its meaning), and store those vectors — with metadata — in a vector database.
  • Query: embed the user’s question, retrieve the most similar chunks, optionally rerank them, and pass the best ones to the LLM, which generates an answer grounded in (and ideally citing) those chunks.

That’s the whole concept. As a wise practitioner put it: the concept is simple, the execution is not. And the execution problem has a specific location.

The uncomfortable truth: retrieval is the bottleneck, not the model

Here is the most important thing to internalise before you build anything. In 2026, RAG systems don’t usually fail because the model isn’t smart enough to write a good answer. They fail because the model was handed the wrong documents. The generation step is largely solved; the retrieval step is where quality leaks out.

The numbers are sobering. A naive RAG pipeline — the kind you build in a weekend tutorial — fails at retrieval roughly 40% of the time, and the failure is the dangerous kind: the LLM produces a confident, fluent, well-structured answer grounded in the wrong chunks. Naive retrieval plateaus around 70–80% precision for anything beyond simple factual lookups. If retrieval hands over irrelevant context, no model on earth can save the answer — garbage in, confident garbage out.

This connects directly to the “AI slop” problem: an answer that looks correct but isn’t. In RAG, the root cause is almost always retrieval, and that’s where your engineering effort belongs.

The maturity ladder: naive → advanced → agentic → adaptive

RAG has evolved well past the weekend-tutorial pattern. There’s a clear ladder of sophistication, each rung buying more accuracy at more cost and complexity.

Diagram illustrating the RAG maturity ladder with four stages: Naive RAG, Advanced RAG, Agentic RAG, and Adaptive RAG, highlighting their characteristics and performance metrics.
  • Naive RAGquery → embed → top-k vector search → stuff into prompt → generate. The 2023 pattern. Fine for simple questions over a clean knowledge base; plateaus at 70–80% precision and fails ~40% of the time on harder queries. If a tutorial stops here, it’s out of date.
  • Advanced RAG — adds hybrid search (semantic + keyword), a reranker to sort retrieved chunks by true relevance, and query rewriting/decomposition. This is the rung that fixes most retrieval failures, and most production systems should live here. The cheapest upgrades win the most.
  • Agentic RAG — the LLM stops being a passive consumer of whatever chunks came back and instead controls the retrieval loop: it plans, decomposes a question into sub-queries, retrieves, evaluates whether it has enough, reformulates and retrieves again, and self-checks before answering. Worth the extra cost for complex, multi-hop questions or when accuracy is non-negotiable (legal, medical, financial).
  • Adaptive RAG — a lightweight classifier routes each query by complexity: simple lookups take the cheap Advanced path, complex multi-hop questions trigger the expensive Agentic path. You get agentic quality where it matters and advanced-RAG cost everywhere else — cost discipline built into the architecture.

The trap most teams fall into is jumping straight to agentic complexity. The right path is to get Advanced RAG solid first — most use cases never need more.

Grounding is a discipline, not a side effect

A grounded-looking answer isn’t the same as a grounded one. The practice that turns RAG from “usually right” into “verifiably right” is citation grounding: require the agent to attribute every claim in its answer to a specific retrieved chunk, by ID. Any claim it can’t cite gets flagged for human review rather than shipped. This one discipline, according to 2026 practitioners, eliminates the majority of synthesis hallucinations — the cases where the model blends real chunks into a plausible falsehood.

Grounding is also measurable, and you should measure it continuously. The standard tools: RAGAS for answer-level metrics (faithfulness, answer relevance, groundedness) and classic information-retrieval metrics (nDCG, MRR, Recall@K) for the retrieval step itself. If you only measure whether the final answer “sounds good,” you’re flying blind through exactly the step that fails 40% of the time. Instrument retrieval quality directly.

The payoff is real: a May 2026 MLOps Community benchmark across 47 production deployments found agentic RAG paired with knowledge graphs cut hallucination by roughly 62% — though, as we’ll see, the foundational fix is almost always better retrieval, not exotic architecture.

RAG is how agents stay honest

Step back and the strategic picture is clear. As foundation models commoditise, the differentiator shifts from the model to your data orchestration and retrieval strategy — what your agent can accurately look up. For knowledge-grounded agents, RAG isn’t a feature; it’s the mechanism that keeps the agent tethered to reality. It’s also increasingly a tool the agent calls rather than a fixed pre-step — retrieval exposed over an interface (often MCP), invoked when the agent decides it needs facts.

The canonical pipeline: retrieve wide, rerank narrow

The single highest-leverage pattern in production RAG is two-stage retrieval: retrieve a wide net with hybrid search, then rerank down to a precise few.

Diagram illustrating a hybrid retrieval and reranking process with two stages: Stage 1 involves dense semantic search and sparse keyword search leading to the selection of top results, while Stage 2 focuses on precision through a cross-encoder and a large language model (LLM).

Stage 1 — hybrid search (recall). Pure vector search is great at meaning but misses exact terms — product codes, error strings, proper nouns, the literal word a user typed. Pure keyword search (BM25) is the opposite. Hybrid search runs both and fuses the results, and in 2026 it’s no longer optional for production RAG. Cast a wide net here: retrieve the top ~50 candidates. You want recall — get the right chunk somewhere in the set.

Stage 2 — reranking (precision). A cross-encoder reranker (such as Cohere Rerank v3) then scores each of those 50 candidates against the query with far more nuance than the first-pass similarity, and you keep only the top ~5. Crucially, irrelevant chunks are discarded rather than stuffed into the prompt — which both improves the answer and cuts token cost. This retrieve-50-rerank-to-5 pattern consistently improves answer quality by 15–30% on RAGAS metrics.

def retrieve(query: str, k_dense=50, k_final=5) -> list[Chunk]:
# Stage 1: hybrid recall — semantic + keyword, fused
dense_hits = vector_search(embed(query), top_k=k_dense) # meaning
sparse_hits = bm25_search(query, top_k=k_dense) # exact terms
candidates = reciprocal_rank_fusion(dense_hits, sparse_hits)

# Stage 2: precision — cross-encoder rerank, discard the rest
ranked = rerank(query, candidates) # e.g. Cohere Rerank v3
return ranked[:k_final]

Get this two-stage pipeline working before anything else. It fixes the majority of retrieval failures on its own.

The inputs that set your ceiling: chunking and embeddings

Two upstream choices cap how good retrieval can ever be — get them wrong and no reranker rescues you.

Chunking. How you split documents determines what can be retrieved. Naive fixed-size splits cut sentences in half and orphan context. Prefer heading-aware or semantic chunking that respects document structure, and attach metadata to every chunk from day one: source, owner, effective dates, and — critically — access-control labels (ACLs). Metadata is what lets you filter (“only docs this user may see,” “only the current policy version”), and retrofitting ACLs later is painful. Enforce document-level access from day one.

Embeddings. The embedding model sets the ceiling on semantic retrieval quality. In 2026, OpenAI’s text-embedding-3-large (~64.6 MTEB) is the safe default; the open Qwen3-Embedding-8B tops the multilingual leaderboard (~70.58) if you self-host or need many languages. Whatever you pick, the same model must embed both your chunks and your queries.

Helping the query find the answer

Sometimes the user’s question, as typed, is a poor search query. Two cheap techniques close the gap:

  • Query rewriting and decomposition. Reformulate a vague question into a better search query, or split a multi-part question into sub-queries you retrieve for separately. “How did our refund and shipping policies change last year?” is two retrievals, not one.
  • HyDE (Hypothetical Document Embeddings). For vague or under-specified queries, have the LLM generate a hypothetical answer first, then embed that to drive retrieval — the hypothetical often sits closer in vector space to the real documents than the bare question did. You still ground the final answer on the real retrieved docs, never the hypothetical.

The agentic loop: retrieval as a decision, not a step

Everything so far is Advanced RAG — a fixed retrieve-then-generate flow. Agentic RAG turns retrieval into something the model actively controls. Instead of one pass, the agent runs a loop: plan, retrieve, reflect, and decide whether it has enough to answer or needs to search again.

Diagram illustrating the agentic RAG loop, featuring stages: Adaptive Router, Plan, Retrieve, Reflect, and Answer with citations, with arrows indicating flow and decision points.

The loop, drawn from ReAct-style reasoning, looks like this:

def agentic_rag(question: str, max_steps=4) -> Answer:
if not is_complex(question): # adaptive routing — cost control
return advanced_rag(question) # simple query: one cheap pass

goals = plan(question) # decompose into sub-goals
evidence = []
for _ in range(max_steps): # bounded loop — no runaway cost
q = rewrite(next_open_goal(goals), evidence)
evidence += retrieve(q) # hybrid + rerank from above
if reflect(question, evidence).is_sufficient:
break # stop when grounded enough
return synthesize_with_citations(question, evidence)

Three things make this production-grade rather than a runaway token-burner:

  • Adaptive routing sends simple queries to the cheap path — you don’t pay agentic cost ($0.02–0.10/query versus ~$0.005 for advanced) on questions that don’t need it.
  • A bounded loop (max_steps) caps retrieval iterations, the same resource-bounding discipline that keeps any agent from looping forever.
  • Reflection lets the agent recognise when retrieval came back empty and try a different query instead of confidently answering from nothing — directly attacking the 40% failure mode.

Citation grounding, made concrete

Above, we named citation grounding as the discipline that kills synthesis hallucinations. In the pipeline it’s a hard rule on the generation step: every claim must carry the chunk ID it came from, and uncited claims are flagged rather than shipped.

SYSTEM = (
"Answer ONLY from the provided chunks. After every claim, cite its chunk "
"id like [c3]. If the chunks don't support an answer, say so — do not "
"use outside knowledge. Uncited claims will be rejected."
)
# post-process: parse citations, verify each maps to a real retrieved chunk,
# and route any uncited sentence to human review instead of the user.

This is the RAG-specific form of the verification gate from the code-review: don’t trust the output, check it — here, by confirming every sentence traces to real evidence.

When to add GraphRAG (and when not to)

GraphRAG (Microsoft, open-sourced July 2024) supplements vector retrieval with a knowledge graph of entity relationships. It’s genuinely powerful for cross-document, “connect-the-dots” questions that require reasoning over relationships (“which suppliers are affected if this factory closes?”). But it earns its cost only there — for ordinary lookups it’s expensive over-engineering. Reach for it when theme-level, multi-entity reasoning is a real requirement, not before.

Measure it, or you’re guessing

Build the eval harness before you add agentic complexity, not after. Use RAGAS for faithfulness, answer relevance, and groundedness; use IR metrics (nDCG, MRR, Recall@K) to measure retrieval directly. Profile your actual query distribution to decide whether you even need the agentic path. Without this, you can’t tell whether a change helped — and in a system that fails 40% of the time at one specific step, measuring that step is the whole game.

On frameworks: use LlamaIndex when retrieval quality is your focus (its ingestion and retrieval tooling is strong), LangGraph when you need durable, stateful agentic orchestration, and LangChain to assemble something quickly. Many teams combine LlamaIndex ingestion with LangGraph control.

Start with the caveat that saves you a month

Before comparing anything, the single most useful fact in this entire post: your vector database choice accounts for maybe 5–10% of your RAG system’s quality. Chunking strategy, embedding model, retrieval pipeline, and prompting matter far more. Teams routinely agonize over Pinecone-versus-Qdrant while shipping naive retrieval that fails 40% of the time. Don’t be that team. Pick a reasonable default, get the pipeline right, and switch databases later only if scale or features force you to.

With that said, picking the wrong database can create real operational pain, so here’s how the three honestly compare.

A useful framing: as context windows have grown to a million-plus tokens, the vector DB’s role has shifted from “essential storage” to a smart retrieval layer that controls cost and improves quality. All three options below do that competently. They differ in who runs them and what they cost as you scale.

Three databases, three philosophies

Comparison table of Pinecone, Qdrant, and pgvector highlighting their philosophy, operational burden, cost at scale, sweet spot, filtering methods, and lock-in.

pgvector — “use the database you already have”

pgvector is a PostgreSQL extension that adds vector search to the database you’re probably already running. Its whole philosophy is don’t add infrastructure: vectors live in the same tables as the rest of your data, so you get joins, transactions, and role-based access control for free, with no separate service to operate or sync.

  • Best for: teams already on Postgres, under roughly 5–10 million vectors. This is the right default for most builds.
  • Performance: with an HNSW index, queries return in ~5–8ms — at typical scale the database is not your latency bottleneck (embedding generation usually dominates). Performance degrades past ~10M vectors, though the pgvectorscale extension pushes that ceiling dramatically (benchmarks show 471 QPS at 99% recall on 50M vectors).
  • Cost lever: running pgvector on serverless Postgres like Neon (which scales compute to zero when idle) can cut a bursty workload’s bill from ~$260/month on RDS to ~$30–50/month. One database for app data and vectors is an underrated simplification.

Qdrant — best performance per dollar

Qdrant is an open-source vector database written in Rust, built specifically for high-throughput vector search. You can self-host it or use Qdrant Cloud, and its defining trait is economics: self-hosted on a ~$30–50/month VPS it comfortably handles 10M+ vectors — roughly 10× cheaper than equivalent Pinecone capacity.

  • Best for: cost-conscious teams that can run a container, wanting the best price-performance and strong filtering.
  • Strengths: very fast HNSW with excellent payload/metadata filtering (“vectors where tenant_id = X”) — important for multi-tenant and ACL-aware retrieval. Published benchmarks show ~850 QPS at p95 ~8ms on 1M vectors.
  • Watch-outs: the ecosystem is smaller than Pinecone’s (though LangChain and LlamaIndex both integrate cleanly), and you should verify behaviour at your scale if you have very large datasets or heavy concurrent writes — at 50M vectors a single Qdrant node trailed pgvectorscale badly in one benchmark. Test before committing at the high end.

Pinecone — zero-ops managed scale

Pinecone is fully managed and serverless-first: there’s no infrastructure to run, indexes partition and replicate themselves, and you get consistent low-millisecond latency at essentially any scale. Multi-tenant isolation via namespaces is a clean first-class feature, and it supports dense+sparse hybrid search.

  • Best for: teams with no infrastructure team, or workloads past ~5M vectors where its purpose-built scaling shines (sub-20ms p95 at 5M+).
  • The trade-offs are real: you cannot tune the index to control the recall/latency trade-off — their docs say so plainly, and for some applications that opacity is fine, for others disqualifying. Cost scales steeply (often 3–8× pgvector past ~2M vectors, and “budget surprises are common”). There’s no self-hosting and meaningful vendor lock-in — the API, index format, and data are tied to Pinecone. You’re buying operational simplicity, and at scale it’s a genuine premium.

The decision, in one pass

The choice is mostly mechanical once you’re honest about scale and ops appetite.

Flowchart guiding the selection of a vector database based on criteria such as existing Postgres usage, infrastructure capability, and cost-performance needs, highlighting options like pgvector, Qdrant, and Pinecone.
  • Already on Postgres and under ~5–10M vectors?pgvector. The migration cost is near zero and you avoid a second system entirely. This is most teams.
  • Want the best cost-performance and can run a container?Qdrant (self-hosted). ~10× cheaper than Pinecone at small-to-mid scale, with first-class filtering.
  • No infra team, or past ~5M vectors and want zero ops?Pinecone, accepting the cost curve and lock-in for genuine operational simplicity.

All three support cosine/dot-product/L2 distance and metadata filtering, so the differentiator isn’t features — it’s the operational model and the cost curve.

Keep the database swappable

One architectural habit pays for itself: hide the vector store behind a thin retrieval interface. Your pipeline should call retrieve(query), not Pinecone-or-Qdrant-or-pgvector-specific code. Then the database becomes an implementation detail you can change when scale demands — start on pgvector, move to Qdrant or Pinecone if and when you cross the thresholds above — without rewriting your application.

class Retriever(Protocol):
def search(self, query_vec: list[float], top_k: int,
filters: dict) -> list[Chunk]: ...

# PgvectorRetriever, QdrantRetriever, PineconeRetriever all implement this.
# The rest of the RAG pipeline neither knows nor cares which one is wired in.

The bottom line

This post traced grounding from principle to production. Retrieval, not the model, is where RAG fails — naive pipelines miss ~40% of the time, so grounding and measurement are the disciplines that matter. The fix is unglamorous and cheap — hybrid search, a reranker, good chunking, citation grounding, and an adaptive agentic loop, all measured with RAGAS and IR metrics. The database under it is a real but secondary decision — pgvector by default, Qdrant for cost-performance, Pinecone for zero-ops scale — and a thin interface keeps it swappable.

The strategic point ties back to where we started: as models commoditise, your edge is the quality of what your agent can accurately retrieve. Get the retrieval pipeline right and a knowledge-grounded agent stops being a confident liar and becomes something you can trust in production — which is the entire goal.

Free Booklet: Claude AI The Complete Guide

Anthropic ships major Claude updates almost weekly, which makes staying current with the platform a genuine full-time problem — even the best deep-dive guides go stale within weeks of publication. Claude AI: The Complete Guide solves that by treating currency as a feature: every pricing figure, benchmark, and model spec is timestamped, every correction from prior editions is called out explicitly rather than quietly fixed, and this August 2026 revision covers everything from the new Claude Opus 5 to the Fable 5/Mythos 5 export-control saga that briefly took two flagship models offline worldwide. Whether you’re writing your first API call or architecting a production agentic system, this is the rare technical reference built to be re-read as it’s updated, not just read once and left to rot on a shelf.

Multi-Agent Orchestration: From One Agent to Many (and When Not To)

You start with one coding assistant and it’s great. So you add a second agent to review the first. Then a planner. Then a tester. Each addition feels reasonable, and somewhere around the fourth one the system starts to wobble: the logs balloon, the agents make contradictory decisions, costs spike, and you’re back babysitting it at 2 a.m. wondering whether all this orchestration actually bought you anything.

That tension — more power versus more pain — produced one of 2025’s sharpest engineering disagreements. On June 12, Cognition (the team behind Devin) published “Don’t Build Multi-Agents,” arguing that parallel subagents make independent decisions on shared problems and produce conflicting, fragile output. Less than 24 hours later, Anthropic published “How we built our multi-agent research system,” reporting a multi-agent architecture that outperformed a single agent by 90.2% on their research evaluation. Two of the most respected teams in the field, opposite conclusions, same week.

Here’s the thing: they’re both right. Resolving that apparent contradiction is the key to using multi-agent systems well — and it starts by recognising what these systems actually are.

Multi-agent orchestration is distributed systems

The most useful reframe in this whole topic: you are not inventing something new. A team of agents passing messages, delegating work, and sharing state is a distributed system, and the patterns have names that predate LLMs by decades — orchestrator-worker, pipeline, message bus, blackboard, the actor model. The failure modes are the classic distributed-systems failure modes too: race conditions, cascading failures, coordination overhead, partial failure.

Separation of concerns, single responsibility, clear interfaces, idempotency, error handling, observability, bounded resources — everything you already know about building reliable systems still applies, and applies harder, because you’ve added a new source of nondeterminism on top. The teams that fail with multi-agent treat it as a vibe. The teams that succeed treat it as an engineering problem with known patterns and known failure modes. As one widely-shared 2026 benchmark put it bluntly: the gap between a good agent system and a bad one is almost never the framework — it’s the eval pipeline, the observability, and the failure-recovery logic.

When multi-agent actually helps

Anthropic and Cognition don’t actually disagree on the principle; they were describing different workloads. Anthropic’s own write-up is explicit about the boundary. Multi-agent systems earn their keep when a task has:

  • Heavy parallelism — it splits into independent strands that don’t need to talk to each other.
  • Information that exceeds a single context window — more material than one agent can hold, so you spread it across several windows.
  • Many complex tools to coordinate.
  • High enough value to justify the cost — and the cost is real: their multi-agent system used roughly 15× the tokens of a single chat.

And they’re equally explicit about when it doesn’t fit — which, notably, includes most coding:

“Most coding tasks involve fewer truly parallelisable tasks than research, and LLM agents are not yet great at coordinating and delegating to other agents in real time.”

That’s the resolution of the debate. Research parallelises beautifully — five subagents investigate five subtopics in isolation and report back. Coding often doesn’t, because changes are tightly interdependent: the function you’re writing depends on the type the other agent just changed. Cognition’s point is that splitting tightly-coupled work across agents fragments the context each one needs, and they make conflicting decisions. Anthropic’s point is that splitting loosely-coupled work across agents is a superpower. Both true.

Flowchart illustrating when to use multiple agents for tasks, detailing conditions for single and multi-agent usage.

The default is still one agent

Before any pattern, internalise the most important rule: start with a single, well-prompted agent with good tools. It handles something like 80% of what people reach for frameworks to solve. Multi-agent is the exception you reach for when a single agent hits a hard wall — a context-window limit, a genuinely parallel workload, a tool-coordination problem — not the default architecture. This is the same instinct as “start with the monolith”: don’t pay distributed-systems complexity until a real constraint forces you to.

The pattern ladder

When you do go multi-agent, you’re choosing a topology, and there’s a clear ladder from simplest to most complex. Each rung adds capability and cost, and each has a characteristic failure mode worth knowing before you climb.

A diagram illustrating the 'pattern ladder' with various stages including Single Agent, Router, Pipeline, Orchestrator-worker, and Swarm. Each stage describes its characteristics, complexity, capabilities, and potential failures, emphasising the progression and relationships between different patterns.
  • Single agent — one agent, one context window, a good toolset. The baseline and the right answer most of the time.
  • Router — a lightweight classifier inspects each request and sends it to the right specialist agent. Still single-agent execution; you’ve just picked which one. Cheap and reliable. Failure mode: misrouting.
  • Pipeline (sequential) — agents in a fixed chain, each transforming the previous one’s output: writer → editor → fact-checker. Deterministic and easy to reason about. Failure mode: errors compound down the chain.
  • Orchestrator-worker (supervisor) — a lead agent decomposes a task and delegates sub-tasks to specialised workers, then synthesises their results. This is what production looks like in 2026, with the widest framework support (Claude Agent SDK, LangGraph, OpenAI Agents SDK, CrewAI’s hierarchical process). Failure mode: over-delegation — the orchestrator spawning workers endlessly — which you bound with iteration ceilings.
  • Swarm — a dynamic, open-ended population of peer agents that coordinate through shared memory or a message bus rather than a fixed coordinator. The frontier, powerful for massively parallel work. Failure modes: runaway spawning and shared-state race conditions, which you bound with population caps and concurrency safeguards.

Start at the bottom and climb only when forced. Most teams that succeed land on orchestrator-worker; most teams that fail jumped to a swarm because it sounded impressive.

A preview of the failure surface

One sobering data point to carry forward: a Berkeley research team catalogued 14 distinct failure modes specific to multi-agent LLM systems — things like agents losing track of who’s responsible for what, conversations derailing, and one agent’s error propagating through the whole team. These sit on top of every normal software failure mode, not instead of them. Multi-agent doesn’t replace your reliability engineering; it demands more of it. That’s the subject we keep returning to.

Building a Team of Coding Agents

We’ve just hit an uncomfortable truth: most coding isn’t parallelisable, so a team of coding agents is the exception, not the rule. This part is about finding the exceptions — the coding work that genuinely benefits from multiple agents — and building it correctly with the orchestrator-worker pattern.

First, find the parallel work

Before writing any orchestration code, do the analysis that most teams skip: figure out which parts of your task are actually independent. Coding work falls into two buckets, and only one belongs in a multi-agent system.

Tightly coupled (keep single-threaded): A refactor where every change depends on the last. Implementing a feature whose pieces share evolving types and assumptions. Anything where worker B needs to know what worker A just decided. Splitting this across agents fragments the context each needs and produces the conflicting decisions Cognition warned about. Use one agent.

Loosely coupled (parallelise): Work that decomposes into independent strands sharing little context:

  • Broad, read-only exploration. “Find every call site of this deprecated API across the monorepo.” “How do these eight modules each handle caching?” Each strand is independent and mostly read-only — the ideal multi-agent workload, exactly like Anthropic’s research case.
  • Independent implementation units. Three unrelated bug fixes in three unrelated modules. A set of similar-but-separate endpoints. Each can be a worker on its own branch.
  • The role split. Author, tester, reviewer, and security as separate agents with separate contexts — not parallel on the same code, but a pipeline of specialised perspectives.

The discipline here is just good old task decomposition. If you can’t write down sub-tasks that are genuinely independent, you don’t have a multi-agent problem — you have a single-agent task you’re overcomplicating.

The decision that makes or breaks it: the isolation boundary

Once you have independent sub-tasks, the single most important design decision is the isolation boundary: what does each worker need to know about what the others are doing? Anthropic’s answer for research was radical and worth copying: almost nothing. Each subagent gets a self-contained task description, an expected output format, and a fresh context window. It doesn’t know the other workers exist and can’t coordinate with them mid-task.

That isolation is not a limitation — it’s the whole point. It’s what lets workers run in true parallel, and it’s what keeps the orchestrator’s context window from drowning in cross-talk. In software terms, you’re enforcing single responsibility (each worker owns one job) and a clean interface (the task description is the worker’s API contract; its structured result is the return value). The workers are pure functions with respect to each other.

Diagram illustrating the Orchestrator-worker model for coding, showing the Orchestrator leading and delegating tasks to Workers A, B, and C, each with isolated environments and scoped tools.

For coding, isolation has a concrete, beautiful implementation: one git worktree per worker. Each agent gets its own checked-out branch in its own directory, so parallel workers physically cannot collide on the filesystem, and merging back is an ordinary pull request. Isolation boundary and execution isolation become the same mechanism.

Building the orchestrator

Here’s the shape of an orchestrator-worker system for coding. It’s deliberately framework-agnostic — the pattern matters more than the library, and you can map it onto LangGraph, CrewAI, or the Claude/OpenAI SDKs (more on that below).

from dataclasses import dataclass

@dataclass
class SubTask:
id: str
description: str # the worker's entire contract — self-contained
output_schema: dict # the expected structured result

# ---- ORCHESTRATOR ---------------------------------------------------------
def orchestrate(task: str, max_workers: int = 6) -> str:
plan = decompose(task) # lead agent: split into sub-tasks
if len(plan) <= 1:
return single_agent(task) # not parallel — don't multi-agent it
plan = plan[:max_workers] # bound fan-out (over-delegation guard)

results = run_parallel(spawn_worker, plan) # workers run isolated, in parallel
return synthesize(task, results) # lead agent merges the findings

def decompose(task: str) -> list[SubTask]:
"""Lead agent returns ONLY independent sub-tasks. If it can't, return one."""
system = ("Split this task into INDEPENDENT sub-tasks that share no state and "
"can run in parallel. Each must be self-contained. If the work is "
"tightly coupled, return a single task. Output JSON only.")
return [SubTask(**t) for t in call_model(system, task)["subtasks"]]

# ---- WORKER ---------------------------------------------------------------
def spawn_worker(sub: SubTask) -> dict:
"""A worker: fresh context, isolated worktree, self-contained brief."""
wt = make_worktree() # its own branch + directory
agent = Agent( # fresh context window
system=f"You are a focused coding agent. Task:\n{sub.description}\n"
f"Return a result matching this schema: {sub.output_schema}",
tools=scoped_tools(wt), # least privilege, confined to worktree
)
return bounded(agent.run, max_iterations=25) # cap iterations — no runaway loops

Read what’s not in there: no worker-to-worker channel, no shared mutable state, no unbounded loops. Every choice is a software fundamental:

  • decompose returns one task when the work is coupled — the system refuses to multi-agent a single-agent problem. This one guard prevents most multi-agent disasters.
  • max_workers and max_iterations bound resources — the direct fix for the over-delegation failure mode.
  • output_schema makes each worker’s result a typed contract the orchestrator can rely on, not free text it has to re-parse.
  • scoped_tools(wt) gives each worker least-privilege access confined to its worktree — containment, exactly as a single agentic worker needs.

Synthesis is where quality is won or lost

The orchestrator’s hardest job isn’t delegating — it’s synthesising. Workers return partial, independently-derived results; the lead agent has to reconcile them into one coherent answer, catch contradictions, and decide what to keep. This is the step Cognition was worried about: independent workers can reach incompatible conclusions. The mitigation is to keep synthesis single-threaded and authoritative — one orchestrator with the full picture decides, rather than letting workers negotiate. For coding specifically, synthesis often ends not in merged code but in N pull requests the orchestrator opens for human review — keeping a person at the merge boundary.

Mapping to frameworks

You can hand-roll the above, but in production most teams use a framework. The pattern maps cleanly onto all of them; pick by your needs, not hype:

  • LangGraph — models the orchestrator as a graph with conditional edges and durable execution, so a long-running team survives a server restart, with checkpointing and the most mature tracing (LangSmith). The production default for complex flows; steeper learning curve.
  • CrewAI — role-based “crews” with a hierarchical process; fastest to prototype (a team in ~20 lines), lighter on production observability.
  • OpenAI Agents SDK — explicit handoffs that pass context between agents; clean and opinionated, but model-locked and light on checkpointing.
  • Claude Agent SDK — subagents with tool-use chains, wired to data and tools via MCP.

But heed the consensus from every serious 2026 comparison: the framework is the least consequential choice. What determines whether your team survives its first production incident is the eval pipeline, the observability, and the failure-recovery logic.

Keeping the fundamentals intact

Notice that nothing in this build is novel computer science. It’s classic engineering applied to a new substrate:

  • Single responsibility → one job per worker.
  • Interface/contract → the self-contained task description and output schema.
  • Statelessness / isolation → fresh context window + dedicated worktree per worker.
  • Bounded resources → caps on workers and iterations.
  • Least privilege → tools scoped to each worker’s worktree.
  • A single authoritative integrator → the orchestrator owns synthesis; workers don’t negotiate.

Get those right and orchestrator-worker is robust. Skip them and you get the wobble.

Swarms, Coordination & Keeping It Production-Safe

Orchestrator-worker has a fixed coordinator and a bounded set of workers. The top rung of the ladder removes both constraints — and that’s exactly why it’s powerful and dangerous in equal measure.

Swarms: the frontier, and its sharp edges

A swarm dynamically spawns an open-ended population of peer agents based on workload, and they coordinate through shared memory or a message bus rather than through a central orchestrator. There’s no single agent holding the whole plan; coordination is emergent. This is the frontier of multi-agent design, and it shines for massively parallel, open-ended work where you can’t predict up front how many workers you’ll need or how they should divide labour.

It’s also where the distributed-systems bill comes due in full. Remove the fixed coordinator and you inherit every hard problem in concurrent computing:

  • Runaway spawning. A peer agent decides it needs help and spawns more agents, which spawn more — an unbounded fork bomb made of LLM calls, each one expensive. The fix is a hard population cap enforced outside the agents’ control.
  • Shared-state race conditions. Multiple agents reading and writing the same memory concurrently produce exactly the corruption you’d expect from any unsynchronised concurrent system. You need the classic safeguards: locks, atomic operations, or append-only logs with conflict resolution.
  • Nondeterminism and emergence. With no central plan, behaviour emerges from interactions and is hard to reproduce or debug. Two identical runs can diverge.
Diagram illustrating a swarm system with peer agents and emergent coordination, featuring a shared memory/message bus with no fixed coordinator, and indicating a population cap.

The honest guidance: most teams should not build a swarm. If orchestrator-worker can express your problem — and it usually can — use it. Reach for a swarm only when the workload is genuinely open-ended and massively parallel, and only with the safeguards above wired in from the start. A swarm without a population cap is not an architecture; it’s an outage waiting for a trigger.

Coordination and shared state

Whether you’re running a careful swarm or a more dynamic orchestrator-worker variant, the moment agents share state you’ve re-entered concurrent-systems territory. Two classic patterns cover most needs:

  • The blackboard. Agents read from and write to a shared “blackboard” of state. Simple, but every write is a potential race; guard it like any shared resource.
  • The message bus / actor model. Agents are actors that communicate only by passing messages — no shared mutable state at all. This is what AutoGen’s v0.4 rewrite adopted (an event-driven actor core), and it’s the more robust choice precisely because it sidesteps shared-memory races by design. If you’re building coordination from scratch, prefer message-passing over shared memory for the same reason you would in any concurrent system.

And recall Cognition’s core warning, which applies most sharply here: agents making independent decisions on a shared problem produce conflicting outputs, and the fragility compounds. The mitigation is to keep the parts that need shared context single-threaded, and only parallelise the parts that are genuinely independent. Coordination you can avoid is coordination you can’t get wrong.

The failure surface you’re signing up for

A Berkeley research team catalogued 14 distinct failure modes unique to multi-agent LLM systems — among them agents losing track of responsibilities, derailing into unproductive loops, one agent’s hallucination propagating through the team, and premature termination where the system stops believing it’s done before it is. The critical point for production: these are additional to every failure mode your software already has. Multi-agent doesn’t simplify your reliability engineering — it multiplies what you must account for. Which is why the rest of this part is about scaffolding, not agents.

Keeping the software fundamentals intact

Whatever pattern you land on — pipeline, supervisor, or swarm — the disciplines that keep it production-safe are the same disciplines that keep any distributed system reliable. The framework debate is a distraction; this scaffolding is the actual work, and it’s where the gap between a system that survives its first incident and one that doesn’t really lives.

Diagram illustrating key concepts of classic reliability engineering related to a multi-agent core, featuring elements such as Observability & tracing, Durable execution, Evals, and Bounded resources on one side and Structured contracts, Least privilege, and Human checkpoints on the other.
  • Observability and tracing. You cannot debug what you cannot see, and a multi-agent run is a distributed trace across many context windows. Capture every agent, every delegation, every tool call, every hand-off — this is what mature tooling (LangSmith and peers) exists to provide. Without it, a misbehaving fleet is a black box.
  • Durable execution and checkpointing. Agent runs are long; servers restart. Checkpoint state so a team can resume rather than restart from zero — a headline reason LangGraph leads in production. This is just write-ahead logging and crash recovery, applied to agents.
  • Evals — the real differentiator. Every serious practitioner converges on the same point: the eval pipeline matters more than the framework. You need a suite that measures whether the whole system produces good outcomes, because in multi-agent systems “minor changes cascade into large behavioural changes” — a one-line prompt tweak can reshape the whole team’s behaviour. Without evals you’re flying blind through a non-linear system.
  • Bounded resources. Iteration ceilings (against over-delegation), population caps (against runaway spawning), and token budgets (against the 15× cost blowing up silently). Every loop and every spawn needs a bound enforced outside the agent’s judgement.
  • Structured contracts between agents. Typed task descriptions in, structured results out — so a hand-off is a checked interface, not a hopeful paragraph. The same reason you type your function signatures.
  • Least privilege. Each agent gets only the tools, data, and credentials its job requires. One compromised or confused agent in a fleet shouldn’t be able to touch production — and at fleet scale, this is non-negotiable.
  • Human checkpoints. For anything consequential — merging code, spending money, touching customers — a human sits at the boundary. Autonomy scales the work; human checkpoints scale the trust.

None of these is AI-specific. They’re the reliability practices distributed systems have always needed. Multi-agent orchestration doesn’t let you skip them — it raises the price of skipping them.

The whole picture

The shape is clear. Start with one good agent. Climb to multiple agents only when a real constraint — parallelism, context limits, tool coordination, justified by value — forces you up the ladder. Prefer the simplest pattern that works: router, then pipeline, then orchestrator-worker, and only rarely a swarm. Isolate workers aggressively so they don’t have to coordinate. Keep the parts that need shared context single-threaded. And wrap the whole thing in the ordinary scaffolding of reliable software — observability, durable execution, evals, bounded resources, contracts, least privilege, human checkpoints.

The promise of agent swarms is real: for the right workload, a team of agents does things no single agent can. But the teams that realise that promise aren’t the ones with the most agents or the trendiest framework. They’re the ones who remembered that an agent fleet is a distributed system, and that we already know how to build those well. The intelligence is new. The engineering is not — and the engineering is what keeps it standing.