An agentic workflow is an LLM loop that chooses its own next tool call. That one-line definition hides every interesting design decision: how much autonomy you grant, how state survives between steps, and what you do when the model picks badly — which it will.
This page is a working engineer's map of the design space. It is not neutral. Opinions are marked. If you want a gentler introduction, start at AgentLoops for the mechanical shape and AiAgentArchitectures for the framework comparison; come back here when you need to make real architectural choices.
A plain prompt-response call is not agentic. A chained sequence of LLM calls where each step is hardcoded (retrieve → rerank → summarize) is not agentic either — that's a pipeline.
The system becomes agentic when the model decides the shape of the next step at runtime. The minimum you need for that:
With those three pieces you have a working ReAct-style agent in about 100 lines of Python. With those three pieces you also have roughly ten ways to fail, which is what the rest of this page is about.
Most agentic systems are one of these four patterns. Pick deliberately — the failure modes differ.
| Pattern | When to pick | When it fails | Reference implementation |
|---|---|---|---|
| ReAct loop | Single-agent tasks, ≤ 10 steps, tools that are read-only and cheap to retry | Long horizons (context blows up), any action with side effects (duplicate writes), tool schemas the model misformats | LangChain AgentExecutor, OpenAI responses.create with tool_choice="auto" |
| Plan-and-execute | Tasks where the plan is obvious up front and steps are mostly independent | Dynamic environments where the plan is wrong after step 1; premature optimisation of a problem ReAct solves faster | LlamaIndex ReActAgentWorker in plan mode, Plan-and-Solve prompting |
| Graph / state-machine | Multi-agent or multi-role workflows, checkpoint/resume, human-in-loop approval gates | Anything simple — the graph tooling itself becomes the majority of your code | LangGraph, CrewAI flows, SWE-agent's ACI |
| Hierarchical / supervisor | Genuinely multi-specialist tasks (e.g. research + code + review as separate agents) | Small teams just reinventing an API; most tasks are single-agent and this adds latency | AutoGen GroupChat, OpenAI Swarm, LangGraph supervisor |
Strong opinion: most production agents should be graph-based. ReAct is pedagogically clean but production-brittle: the moment you need to checkpoint, retry a single step, swap tools, or let a human approve a mutation, you are reinventing half of LangGraph in your own code. Start with a graph even when the graph has three nodes.
The interesting design work in an agentic system is state management. Token usage is not the hard problem; what to keep is.
Agents accumulate four state channels, and you need to treat them differently:
read_file that returned 30k tokens three steps ago should now be a one-line note.The single highest-leverage change most agent systems need is structured working memory. When the agent extracts a fact ("the ticket ID is INC-2291"), write it to a typed slot instead of leaving it in the chat transcript. Claude/GPT will drop it during summarisation otherwise, and you'll watch the agent re-query the same database for the fourth time in a 20-step run.
Observed failure frequencies from production agent deployments (loosely ordered — see AgentLoops for specific detection recipes):
{"user_id": "42"} when the schema expects an integer. Fix: validate every tool call with JSON Schema before dispatching, feed the validation error back to the model as the tool response. Do not crash the loop on validation failure — prompt it to correct. This alone eliminates ~40% of hang/retry noise.goal field the summariser can't drop.send_email because the first call timed out. User gets two emails. Fix: every mutating tool needs an idempotency key the agent generates once per "intended operation" and re-uses on retries.{"error": "unknown tool, available: [...]"} to nudge the next turn. Never throw.Below is the smallest agent architecture I'd deploy in production today. Every piece is there because I've seen its absence cause an outage.
┌──────────────────────────┐
initial goal ───────▶│ Orchestrator Node │◀──── checkpoint store
│ (state machine / graph) │ (SQL / Redis)
└────┬──────────────┬──────┘
│ │
LLM call │ │ tool dispatch
▼ ▼
┌─────────┐ ┌──────────┐
│ Model │ │ Tools │
│ (w/ │ │ (w/ JSON │
│ cache) │ │ Schema │
│ │ │ validator│
└────┬────┘ └────┬─────┘
│ │
▼ ▼
┌──────────────────────────┐
│ Observability sink │
│ (traces + evals) │
└──────────────────────────┘
What this buys you:
INSERT per node. Value: every "the agent died 18 steps in after $4 of tokens" becomes a resume, not a restart.Things that sound important but usually aren't until your v2:
Most teams build agents by vibes. Don't. The cheapest useful eval is a fixed set of 20–50 task-rollout pairs stored as JSON, replayed on every prompt or model change. Track:
When these metrics plateau, you graduate to harder benchmarks — SWE-bench for code agents, τ-bench or agentbench for generalists. See AgentTesting and LlmEvaluationMetrics for the full discipline.