Four Agent Orchestration Patterns That Actually Ship
Four Advanced Orchestration Patterns
Part 5 of the series · Previous: Part 1 → Part 2 → Part 3 → Part 4
Once you've mastered basic agent chaining, parallel, and pipeline, the patterns that recur in real work boil down to just a few. This article covers the four most-used patterns, each with core code and design rationale.
Pattern 1: Quality Gate — Multi-Dimensional Review Then Revise
After writing an article or a piece of code, a single agent checking end-to-end tends to miss things. Let multiple agents each check one dimension, then have one final agent synthesize the feedback and revise.
// Phase 1: Write the first draft
const draft = await agent(`Write an article about ${TOPIC}`, { phase: 'write draft' })
// Phase 2: Three simultaneous reviews (haiku is enough, cheap)
const [tech, style, readability] = await parallel([
() => agent(`Check for technical errors:\n${draft}`, { label: 'tech review', model: 'haiku' }),
() => agent(`Check for AI-flavor and clichés:\n${draft}`, { label: 'style review', model: 'haiku' }),
() => agent(`Check readability from a beginner's perspective:\n${draft}`, { label: 'readability', model: 'haiku' }),
])
// Phase 3: One agent synthesizes all three opinions and revises
const final = await agent(
`Original:\n${draft}\n\nTech feedback: ${tech}\nStyle feedback: ${style}\nReadability feedback: ${readability}\n
Technical errors must be fixed, style issues must be fixed, readability suggestions adopted selectively.`,
{ phase: 'synthesize and revise' }
)
Design points:
- Reviews use parallel; the three dimensions don't depend on each other
- Reviews use haiku; finding problems doesn't require strong reasoning
- The final synthesis must use a single agent, because the three opinions may conflict and need trade-offs
- Return the draft and review comments alongside the final, so you can see what changed
Pattern 2: Batch Production — Pipeline for Multiple Audiences, Each on Their Own Track
Same topic, different audiences, multiple versions produced simultaneously without waiting for each other.
const audiences = [
{ name: 'beginner', prompt: 'start from zero, use lots of analogies' },
{ name: 'experienced developer', prompt: 'direct comparisons, give code' },
{ name: 'tech manager', prompt: 'focus on efficiency and ROI, no code snippets' },
]
const results = await pipeline(
audiences,
// Stage 1: Search for materials (managers search for different content)
(a) => agent(
a.name === 'tech manager'
? `Search for efficiency improvement cases and data on ${TOPIC}`
: `Search for tutorials and hands-on examples on ${TOPIC}`,
{ model: 'haiku' }
).then(materials => ({ a, materials })),
// Stage 2: Outline
({ a, materials }) => agent(
`Create an outline for ${a.name}. ${a.prompt}\nMaterials: ${materials}`
).then(outline => ({ a, outline })),
// Stage 3: Write first draft
({ a, outline }) => agent(
`Write a first draft for ${a.name}. ${a.prompt}\nOutline: ${outline}`
).then(draft => ({ audience: a.name, draft })),
)
Design points:
- Use pipeline, not parallel, because each audience goes through three stages
.then()passes audience info forward; otherwise, by the draft stage you won't know who you're writing for- The more audiences, the bigger pipeline's advantage; any one getting stuck doesn't block the others
Pattern 3: Review and Verify — Multi-Dimensional Check → Deduplicate → Verify Each Finding Individually
False positives are the biggest fear in code review. Four dimensions each check once, deduplicate, then assign an independent agent to each finding to try to refute it; only confirmed ones are kept.
// Phase 1: Four-dimensional parallel review (use schema to get structured results directly)
const DIMENSIONS = [
{ key: 'bugs', prompt: 'check for logic errors, missing await' },
{ key: 'security', prompt: 'check for injection, sensitive info leaks' },
{ key: 'performance', prompt: 'check for unnecessary serialization, redundant computation' },
{ key: 'style', prompt: 'check naming, simplifiable code' },
]
const FINDINGS_SCHEMA = {
type: 'object',
properties: {
findings: {
type: 'array',
items: {
type: 'object',
properties: { description: { type: 'string' } },
required: ['description'],
},
},
},
required: ['findings'],
}
const allFindings = await parallel(
DIMENSIONS.map(d => () =>
agent(`Review ${TARGET}. ${d.prompt}`, {
model: 'haiku',
schema: FINDINGS_SCHEMA,
}).then(result => result.findings.map(f => ({ ...f, dimension: d.key })))
)
)
// Deduplication: pure JS
const seen = new Set()
const unique = allFindings.flat().filter(f => {
const key = f.description.slice(0, 40)
if (seen.has(key)) return false
seen.add(key)
return true
})
// Phase 2: Assign one agent per finding to try to refute it (pipeline, slow ones don't block fast ones)
const verified = await pipeline(
unique,
(finding) => agent(
`Read ${TARGET}. Someone says there's a problem here: "${finding.description}"
Please verify. If confirmed output CONFIRMED, if false positive output FALSE, if uncertain treat as FALSE.`
).then(verdict => verdict.startsWith('CONFIRMED') ? finding : null)
)
const realIssues = verified.filter(Boolean)
Design points:
- Deduplication uses JS, not an agent
- Verification phase uses pipeline; one item reading a large file slowly doesn't block others
- "Uncertain also counts as FALSE" is key to reducing false positives
- Batch verification tends to get lazy; must verify one by one
- Full production version (auto-fix, run tests)
Pattern 4: Loop-Until-Dry — When You Don't Know the Total, Loop Until No New Findings
Tasks like finding hardcoded paths or security vulnerabilities — you don't know how many there are in total. One agent searching once will definitely miss some, so run multiple rounds, changing the angle each round, until two consecutive rounds produce no new findings.
const STRATEGIES = [
'Use grep to search for absolute path patterns',
'Check config files for hardcoded paths',
'Check Shell and JS scripts for paths',
'Check docs for real user paths',
]
const allFound = new Set()
let dryRounds = 0
let round = 0
while (dryRounds < 2) {
round++
const strategy = STRATEGIES[(round - 1) % STRATEGIES.length]
const found = await agent(
`${strategy}. Known findings (don't repeat):\n${[...allFound].join('\n') || 'none'}`,
{ model: 'haiku' }
)
const lines = found.split('\n').filter(l => l.match(/\/Users\//))
const newOnes = lines.filter(l => !allFound.has(l))
if (newOnes.length === 0) {
dryRounds++
} else {
dryRounds = 0
newOnes.forEach(l => allFound.add(l))
}
}
return { total: allFound.size, items: [...allFound] }
Design points:
while (dryRounds < 2)stops only after two consecutive rounds with no new findings, preventing a single round from missing something- Change strategy each round, searching from different angles to reduce blind spots
- Feed known findings back to prevent duplicates
- Deduplication
Setis pure JS - Suitable for: investigative tasks, tasks with unknown totals
- Not suitable for: tasks with a known exact count, tasks where each round is very expensive
How to Choose
Multiple subtasks independent, results need to be combined → parallel
Multiple items going through the same multi-step process → pipeline
After writing, need multi-dimensional check then revise → Quality Gate (parallel + synthesize)
Findings need to be verified for truth → Review and Verify (parallel + deduplicate + pipeline)
Unknown total, need repeated investigation → loop-until-dry (while)
These patterns can be combined. For example, the Review and Verify pattern uses both parallel and pipeline simultaneously.
Summary
Quality Gate: write → parallel multi-dimensional check → one person synthesizes and revises
Batch Production: pipeline for multiple audiences each on their own track, .then() passes params
Review and Verify: parallel check → JS deduplicate → pipeline refute one by one
loop-until-dry: while loop + change strategy + stop after N consecutive rounds with no new findings