Thousands of teams have built LLM-powered agents that work flawlessly in notebooks and demos. Fewer teams have shipped them to production. The difference isn't talent or effort — it's design patterns. A pattern that feels like a minor optimization in isolation becomes the difference between an agent that handles edge cases gracefully and one that crashes silently under load.
This article distills five architectural patterns from production deployments and research: ReAct (the thinking loop), planning agents (task decomposition), tool-augmented reasoning, multi-agent orchestration, and human-in-the-loop feedback. For each pattern, I'll walk through the failure modes that motivate it, the implementation tradeoffs, and when to apply it. These patterns form the foundation of Building Agentic AI Systems, my Packt book on production agentic architecture.
Why Agent Demos Fail in Production
The typical agent demo follows a happy path: (1) user asks a question, (2) agent reasons about the question, (3) agent calls a tool, (4) tool returns a clean result, (5) agent formulates a confident answer. The failure modes don't appear until production:
- Hallucinated tool calls. The agent invents a tool that doesn't exist or calls a tool with parameters it doesn't accept. In the demo, the tool always exists and always succeeds.
- Infinite loops. The agent calls the same tool repeatedly without making progress, or cycles between two states. The demo never runs long enough to exhibit this.
- Noisy tool output. Real APIs return errors, timeouts, inconsistent formats, and partial failures. The demo tool returns perfect JSON.
- Context explosion. As the agent's reasoning and tool-call history grow, the context window fills and token costs explode. The demo runs a single 5-step loop; production runs 50-step loops.
- Unrecoverable failures. When a tool fails, the agent has no recovery strategy. It either hallucinates a workaround or repeats the failed call forever.
- Lack of interpretability. When the agent produces a wrong answer, there's no audit trail to understand why. The reasoning is embedded in LLM tokens, not in code you can inspect.
The five patterns address these failure modes head-on. They're not theoretical — they're battle-tested solutions to problems you will encounter at scale.
Production agents don't think harder; they think differently. They decompose problems into smaller, bounded tasks. They enforce tool-use contracts. They hand off work to specialized agents. And critically, they give humans a way to intervene before damage is done.
Pattern 1: ReAct — The Foundation
ReAct stands for Reasoning + Acting. It's the simplest production pattern and the foundation that all others build on. The idea: instead of asking an LLM to produce a single response, ask it to interleave reasoning steps with tool calls, with observations from those calls informing the next reasoning step.
The loop looks like this:
Thought: I need to find the user's account balance.
Action: get_account_balance(user_id=12345)
Observation: Balance is $4,230.50
Thought: The user has sufficient funds. I should process the withdrawal.
Action: process_withdrawal(user_id=12345, amount=1000)
Observation: Withdrawal successful. New balance: $3,230.50
Thought: The withdrawal is complete. I can now report to the user.
Why does this work better than a single end-to-end LLM call? Three reasons:
- Observability. You can see exactly what the agent thought and what it did at each step. When something goes wrong, the trace is legible.
- Grounding. Each thought is immediately validated against real data from tool observations. The agent can't hallucinate — it must reason about data it actually received.
- Recoverable errors. If a tool call fails, the agent sees the failure as an observation and can reason about how to retry or work around it.
Implementation: Structured Tool Use
The naive ReAct implementation asks the LLM to output free text like Action: get_balance(12345) and then parses it with regex. This is brittle — the model might output variants like I'll use the get_balance function with 12345, and your parser breaks.
Modern LLM APIs solve this with structured tool declarations. You define your tools as JSON schemas:
{
"name": "get_account_balance",
"description": "Retrieve the current account balance",
"parameters": {
"type": "object",
"properties": {
"user_id": { "type": "integer" }
},
"required": ["user_id"]
}
}
You pass this schema to the API along with your prompt. The model's response is guaranteed to include a structured tool-call object, not text you have to parse:
{
"type": "tool_use",
"id": "call_abc123",
"name": "get_account_balance",
"input": { "user_id": 12345 }
}
This eliminates parsing errors entirely. Your code is also simpler: no regex, no ambiguity, no silent failures. (I cover the gory details of function calling APIs in Chapter 3 of Building Agentic AI Systems.)
When ReAct Is Enough
ReAct handles single-goal queries: "What's the balance on account 12345?", "Send a message to alice@example.com", "Get the current stock price of AAPL". One observation loop gets the job done. ReAct fails when the problem requires multiple steps, prioritization, or backtracking — which brings us to the next pattern.
Pattern 2: Planning Agents — Task Decomposition
ReAct assumes a linear sequence of thoughts and actions. But real-world problems are branching. "Migrate all my emails to a new folder and notify my team" requires: (1) list all emails matching a filter, (2) move each email, (3) post to Slack, (4) send a follow-up email. Some of these steps are independent and could run in parallel. Others depend on earlier steps.
A planning agent explicitly decomposes the goal into a task graph, tracks dependencies, and executes in the right order.
The Plan-Then-Execute Model
The architecture has two phases:
- Planning. The agent receives the user's goal and generates a structured plan: a list of tasks, their dependencies, and success criteria. This plan is visible to the user and can be modified before execution begins.
- Execution. The agent runs each task in dependency order, updating the plan with results. If a task fails, the agent can replan or escalate.
Here's a concrete example: User asks "Prepare my weekly report". The planning phase produces:
Task 1: Retrieve last week's sales data (from CRM)
- Depends on: nothing
- Success criteria: JSON array with at least 5 records
Task 2: Summarize the data into 5 key insights
- Depends on: Task 1
- Success criteria: 5 bullet points, each < 100 words
Task 3: Create a document with the summary
- Depends on: Task 2
- Success criteria: Document is saved and has title + summary section
Task 4: Email the document to manager@company.com
- Depends on: Task 3
- Success criteria: Email delivery confirmed
The user sees this plan before anything runs. They can delete Task 4 if they want to review first, or reorder tasks. Then execution begins.
Advantages Over Flat ReAct
- Parallelization. Independent tasks (like fetching data from multiple sources) can run concurrently.
- User control. The plan is legible and editable before execution. No surprises.
- Replanability. If Task 2 fails, the agent can replan the downstream tasks without redoing Task 1.
- Bounded execution. You can set a max number of tasks and timeout per task, preventing infinite loops.
The downside: planning adds latency. You're making at least two LLM calls (one to plan, one per task). But for high-stakes operations (financial transfers, report generation), the extra round-trip is worth the visibility and auditability.
Pattern 3: Tool-Augmented Agents — The Contract Approach
Even with structured tool calling, agents hallucinate. They invoke tools with the wrong parameters, call tools in the wrong order, or misinterpret tool output. This pattern enforces a contract between the agent and the tool layer: each tool declares what it requires, what it guarantees, and how it fails.
The contract is encoded in the tool schema and in how you handle tool errors.
Example: The Database Query Tool
Naive tool definition: "I have a tool called query_database(sql_string). The agent can pass any SQL." The agent will write DELETE FROM users, and you've just wiped your database.
Contract-based approach:
{
"name": "query_database",
"description": "Execute a read-only SQL SELECT query",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"pattern": "^SELECT\\s+.*$",
"description": "Must start with SELECT (read-only)"
},
"timeout_seconds": {
"type": "integer",
"minimum": 1,
"maximum": 30,
"description": "Query timeout, max 30 seconds"
}
},
"required": ["query"]
},
"error_codes": {
"SYNTAX_ERROR": "The query has invalid SQL syntax",
"TIMEOUT": "Query exceeded timeout",
"COLUMN_NOT_FOUND": "Requested column does not exist",
"PERMISSION_DENIED": "You don't have access to this table"
}
}
Now when the agent tries to call this tool:
- It knows the query must be SELECT-only (schema documentation).
- If it submits a non-SELECT query, the schema validator rejects it before execution, and the agent gets structured feedback about why.
- If the query times out, it gets error code
TIMEOUT, not a raw exception. It can reason about retry strategy. - If it tries to access a table it shouldn't, it gets
PERMISSION_DENIED, which it can acknowledge instead of hallucinating a workaround.
This pattern is critical for safety. By constraining what tools can do and how they fail, you prevent whole categories of bugs.
Error Recovery as a First-Class Pattern
Most agent frameworks treat tool errors as exceptions and crash or retry blindly. In production, errors are data. When a tool returns an error, the agent should reason about it:
"The query timed out. The user asked for the top 10,000 transactions. Maybe I should limit it to the top 1,000 instead and try again."
This requires wrapping tool calls in error-handling logic that passes structured error information back to the LLM's reasoning loop, not just to an exception handler.
Pattern 4: Multi-Agent Orchestration
At a certain scale of complexity, a single agent isn't enough. Consider a customer support system that needs to: (1) classify the incoming ticket, (2) fetch customer history and product info, (3) draft a response, and (4) route to a human if escalation is needed. One agent trying to do all four will be generalist and slow. Four specialized agents, each expert at their task, will be faster and more reliable.
Multi-agent orchestration splits work across specialized agents and coordinates their execution. There are three main orchestration patterns:
Pattern 4a: Supervisor Pattern
A supervisor agent receives the task and decides which worker agents to invoke and in what order. The supervisor doesn't execute work; it orchestrates.
| Supervisor Responsibility | Worker Responsibility |
|---|---|
| Classify incoming request | Execute specialized task |
| Determine which workers to call | Call domain-specific tools |
| Sequence and coordinate work | Handle errors in their domain |
| Aggregate results for user | Return structured results |
Example flow: User asks "What's my balance and recent transactions?" Supervisor recognizes this needs two workers. It invokes AccountWorker (get_balance) and TransactionWorker (get_recent_transactions) in parallel, then aggregates their results.
Pattern 4b: Pipeline Pattern
Agents pass work sequentially, each transforming the input for the next agent. There's no supervisor; instead, each agent knows its successor.
Example: Email arrives. RetrievalAgent fetches customer history. ClassificationAgent analyzes the email and history to determine category. DraftAgent writes a response. ReviewAgent checks the response for tone and accuracy. The output is the final response ready to send.
Pipeline is simpler to implement (each agent has one responsibility) but less flexible than supervisor (you need to know the order in advance).
Pattern 4c: Debate Pattern
For high-stakes decisions, multiple agents propose different solutions and debate the merits. A judge agent or human picks the best.
Example: Should this transaction be flagged as fraud? ProAgent argues yes (unusual amount, new merchant). ConAgent argues no (customer is traveling, they texted about this purchase). JudgeAgent or human reviews both arguments and makes the call.
This pattern is slower (you're running multiple agents) but higher quality for decisions where error is costly.
Coordination Trade-offs
Supervisor is flexible (dynamic task selection) but adds latency (supervisor must decide before workers start). Pipeline is efficient (agents work in parallel) but rigid. Debate is most reliable but slowest. Choose based on your error tolerance and latency budget.
The best orchestration pattern is the simplest one that meets your latency and reliability constraints. Don't add agents unless you have a specific failure mode they solve.
Pattern 5: Human-in-the-Loop — Approval Gates and Escalation
No matter how well you design your agent, it will eventually need to ask for permission. Transfer $50,000? Delete a document? Post to public social media? These are moments where the agent should pause and get human approval before proceeding.
Human-in-the-loop isn't a weakness; it's a feature that makes autonomous systems trustworthy.
Approval Gates
An approval gate is a decision point where the agent generates a proposed action and waits for human confirmation before executing it. The architecture looks like:
Agent planning: I should transfer $50,000 to account 123456.
System: PAUSE - High-value transfer detected
Human review: [sees: amount, source, destination, reason]
Human decision: Approve / Reject / Modify
System: Resume or abort
The key is that the human sees structured information, not raw LLM output. They see the exact action that will be taken, the context, and alternatives.
Escalation Paths
Not every human interaction is an approval gate. Sometimes the agent gets stuck and needs help. Escalation paths let the agent ask a human for guidance without halting execution of other tasks.
Example escalation flow:
- Agent tries to resolve a customer complaint but can't find a solution within 3 attempts.
- Agent escalates to human: "Customer says invoice is missing. I checked three systems and found no invoice record. What should I do?"
- Human responds: "Check the archived invoices system. If it's there, restore it. If not, ask the customer for their email."
- Agent continues with human guidance.
This requires a human queue system and latency tolerance (the agent can't proceed instantly), but it's far more capable than hard-coded fallbacks.
Feedback Loops
The deepest level of human-in-the-loop is feedback: the agent produces a result, the human corrects it, and the agent learns. This isn't about pausing execution; it's about improving the agent over time.
Example: Agent drafts an email. Human sends it and later gets a reply that says "You didn't address my main concern." The human marks this as negative feedback. The agent's next draft for similar issues incorporates this lesson.
Implementing feedback requires capturing what the agent did, what the human did, and storing the pair for fine-tuning or in-context learning.
Pattern Selection Guide
You now have five patterns. When should you use each?
| Pattern | Best For | Cost (Latency) | Cost (Tokens) | Risk If Deployed Alone |
|---|---|---|---|---|
| ReAct | Single-goal queries with <5 steps | Low (1-3 rounds) | Low | Infinite loops, hallucinated tools |
| Planning | Multi-step tasks, high stakes | Medium (plan + execute) | Medium | Over-planning, brittle to changes |
| Tool Contract | Safety-critical tool calls | Low | Low (better validation) | Misuse of unconstrained tools |
| Multi-Agent | Complex domains, parallelizable work | Medium-High (coordination overhead) | High (multiple agents) | Agents work at cross-purposes |
| Human-in-Loop | High-stakes decisions, learning | High (wait for human) | Medium | Decisions made without oversight |
Recommended Combinations
You rarely use a single pattern in isolation. Here are proven combinations:
- Simple automation: ReAct + Tool Contract (fast, safe)
- Complex automation: Planning + Multi-Agent + Tool Contract (slower but more reliable)
- High-stakes operations: Planning + Human-in-Loop (explicit approval)
- Learning systems: ReAct + Human-in-Loop feedback loop (improves over time)
- Risk mitigation: Multi-Agent Debate + Human-in-Loop (multiple perspectives before decision)
Key Takeaways
Production agentic systems don't emerge from better prompting. They emerge from deliberate architectural choices:
- Start with ReAct. It's the simplest pattern and works for 70% of use cases. Don't over-engineer.
- Enforce contracts on tools. Schema validation, error codes, and structured output eliminate silent failures.
- Make work visible before execution. Planning agents let humans see and modify the task graph. This is non-negotiable for high-stakes work.
- Specialize when complexity demands it. Multi-agent orchestration adds overhead. Use it only when single agents are too slow or unreliable.
- Build escalation paths. Agents will get stuck. Make it easy and fast for them to ask for help without crashing.
- Instrument everything. You can't improve what you can't measure. Log reasoning traces, tool calls, errors, and human decisions. Use these to debug and fine-tune.
These patterns are the content of Building Agentic AI Systems (Packt, 2026), with production code examples for each. The book goes deeper into implementation details: how to build resilient multi-agent orchestration, how to design approval workflows, and how to handle the real-world failures that textbook examples skip.
For comprehensive coverage of these patterns with production code, read Building Agentic AI Systems (Packt Publishing, 2026). The book includes full implementations of ReAct loops, planning agents, tool contracts, multi-agent orchestration frameworks, and human-in-the-loop systems. Available now.