If you're building AI agents, you've probably hit a frustrating pattern: your agent works great 90% of the time, but randomly skips critical steps or makes baffling decisions. The fix isn't better prompts—it's architecture.
The Single Agent Trap
Consider a conversational data collection agent—something like a survey bot or an intake form assistant. The agent has one job: collect information through natural conversation. Simple enough, right?
Except "collect information" actually means:
- Parse natural language answers
- Detect conflicts with previous responses
- Validate answer formats
- Evaluate question dependencies
- Select the next question
- Generate natural responses
- Decide when the conversation is complete
One agent. Seven responsibilities. And it keeps failing in subtle ways:
Skipped conflict detection. User says "I work remotely" then later mentions "I commute 2 hours daily." The agent silently overwrites the first answer instead of asking for clarification.
Premature endings. The agent decides the conversation is complete without checking if all required questions were answered. It just... decides it's done.
Unpredictable behavior. The same conversation flow produces different results depending on how the model interpreted its 47 instructions that turn.
The problem isn't the model. The problem is asking one agent to juggle too many responsibilities and hoping it remembers all of them every single time.
The Multi-Agent Solution
The fix: decompose into specialized agents that act as mandatory safety gates.
Instead of one agent doing everything, you have multiple agents with focused jobs. Critical checks become architecturally enforced—not just instructions the model might skip.
1User message
2 ↓
3┌─────────────────┐
4│ Orchestrator │ ← Coordinates flow
5└────────┬────────┘
6 ↓
7┌─────────────────┐
8│ Answer │ ← Extracts structured data
9│ Interpreter │
10└────────┬────────┘
11 ↓
12┌─────────────────┐
13│ Conflict │ ← MANDATORY GATE
14│ Detector │ Must pass before any writes
15└────────┬────────┘
16 ↓ (if conflict → ask user, no writes)
17┌─────────────────┐
18│ Answer │ ← Validates format/constraints
19│ Validator │
20└────────┬────────┘
21 ↓
22 [save answers]
23 ↓
24┌─────────────────┐
25│ Dependency │ ← Evaluates "show if" conditions
26│ Evaluator │
27└────────┬────────┘
28 ↓
29┌─────────────────┐
30│ Completion │ ← HARD GATE
31│ Checker │ Must approve before ending
32└────────┬────────┘
33 ↓
34┌─────────────────┐
35│ Question │ ← Picks next question
36│ Selector │
37└────────┬────────┘
38 ↓
39┌─────────────────┐
40│ Response │ ← Generates natural language
41│ Composer │
42└─────────────────┘The Eight Agents
Each agent has a single, clear responsibility:
| Agent | Job | Why Separate? |
|---|---|---|
| Orchestrator | Routes between agents | Keeps flow logic in code, not prompts |
| Answer Interpreter | Extracts structured answers from text | Focused parsing = better accuracy |
| Conflict Detector | Checks for contradictions | Critical safety check that can't be skipped |
| Answer Validator | Validates format and constraints | Mix of code rules + LLM judgment |
| Dependency Evaluator | Evaluates "show if" conditions | Natural language conditions need LLM |
| Question Selector | Picks the next question | Can optimize for conversation flow |
| Response Composer | Generates user-facing text | Separates content from presentation |
| Completion Checker | Verifies conversation is truly complete | Hard gate before ending |
The key insight: a focused agent with a simple prompt is more reliable than a complex agent with many responsibilities.
"Your ONLY job is to detect if this answer conflicts with previous answers" is easier for a model to follow than instruction #14 in a 50-line system prompt.
Mandatory Safety Gates
The architecture's real power is making critical checks non-skippable through code.
In the monolithic design, conflict detection was an instruction:
1IMPORTANT: Before updating any answers, check if the user's
2response conflicts with previously recorded answers...The model might follow this. Or it might not. You're hoping.
In the multi-agent design, the orchestrator code enforces it:
1async def process_message(message):
2 answer = await answer_interpreter.parse(message)
3
4 # This MUST run - it's code, not a suggestion
5 conflict = await conflict_detector.check(answer, previous_answers)
6
7 if conflict:
8 return await response_composer.ask_clarification(conflict)
9
10 # Only reaches here if no conflict
11 await database.save_answer(answer)The model can't skip the conflict check because it doesn't control the flow—the orchestrator does.
Same pattern for completion checking. The conversation only ends after the Completion Checker agent explicitly approves. No approval, no ending.
Hybrid Code + LLM
Not everything needs model inference. Use the right tool:
| Task | Approach | Why |
|---|---|---|
| "Is this a valid email?" | Code | Regex is faster, cheaper, deterministic |
| "Does this answer make sense given the question?" | LLM | Requires judgment |
| "Are all required questions answered?" | Code | Just counting |
| "Should we show this conditional question?" | LLM | Natural language conditions |
| "Is there a conflict with previous answers?" | LLM | Semantic comparison |
The Completion Checker is mostly code—count unanswered required questions, check for blocking dependencies. Only edge cases need LLM judgment.
This hybrid approach reduces latency and cost while keeping the reliability benefits of separation.
Trade-offs
This architecture isn't free:
Latency. Multiple sequential LLM calls take longer than one. Mitigate by:
- Running independent agents in parallel (Conflict Detector + Answer Validator)
- Using faster models for simple classification tasks
- Making some agents pure code
Cost. More LLM calls = higher API bills. Mitigate by:
- Hybrid code/LLM implementation
- Conditional invocation (skip Conflict Detector if no previous answers)
- Using cheaper models where possible
Complexity. More moving parts to maintain. Mitigate by:
- Typed input/output contracts for each agent
- Unit tests for agents in isolation
- Structured logging for observability
The trade-off is worth it when reliability matters more than raw speed—which is most production systems.
When to Use This Pattern
Multi-agent architecture makes sense when:
- Your single agent is doing 5+ distinct tasks
- Critical checks are being skipped randomly
- Debugging failures is difficult (which step went wrong?)
- Reliability is more important than latency
- You need observability into AI decision-making
It's overkill for simple, single-purpose agents. But if you're building anything that modifies state based on LLM reasoning, mandatory safety gates are worth the complexity.
The core lesson: don't trust prompts for critical behavior. Make it architecturally impossible to skip important steps. Your future self—debugging a production incident at 2am—will thank you.
