The Sequentiality Problem
Most pipeline systems run agents sequentially: Step 1 completes, then Step 2 begins. This is fine for pipelines where each step depends on the previous step's output.
It is catastrophic for pipelines with independent branches.
Consider a due diligence pipeline:
Input: Company name + filing documents
Step 1: Document Parser (30s) — must run first
Step 2: Financial Analyser (45s) — needs parsed docs
Step 3: Legal Risk Scanner (40s) — needs parsed docs (independent of Step 2)
Step 4: Market Research Agent (35s) — needs only company name (independent of Steps 2+3)
Step 5: Summary Generator (20s) — needs Steps 2, 3, 4
Sequential total: 30 + 45 + 40 + 35 + 20 = 170 seconds
Parallel total: 30 + max(45, 40, 35) + 20 = 95 seconds (44% faster)
The DAG Engine
AgentDyne Pipelines are modelled as a Directed Acyclic Graph (DAG). Each node is an agent execution. Each edge is a data dependency.
The parallel execution algorithm:
1. Topological sort — determine valid execution order
2. Compute in-degree for every node
3. Find all nodes with in-degree = 0 after previous wave completes
→ these are the ready-to-run parallel candidates
4. Launch all candidates simultaneously with Promise.allSettled()
5. When wave completes, decrement in-degree for downstream nodes
6. Repeat from step 3
// Simplified parallel wave executor
async function executeDAG(nodes: PipelineNode[], edges: Edge[]) {
const inDegree = computeInDegree(nodes, edges)
const results = new Map<string, NodeResult>()
while (results.size < nodes.length) {
// Find all nodes whose dependencies are satisfied
const ready = nodes.filter(n =>
!results.has(n.id) &&
inDegree.get(n.id) === 0
)
if (ready.length === 0) throw new Error('DAG cycle detected')
// Execute this wave in parallel
const wave = await Promise.allSettled(
ready.map(node => executeNode(node, results))
)
// Record results and decrement downstream in-degrees
wave.forEach((outcome, i) => {
const node = ready[i]
results.set(node.id, outcome.status === 'fulfilled'
? { status: 'success', output: outcome.value }
: { status: 'failed', error: outcome.reason, node }
)
getDownstream(node.id, edges).forEach(d =>
inDegree.set(d, (inDegree.get(d) ?? 1) - 1)
)
})
}
return results
}
Why Promise.allSettled, Not Promise.all
This distinction is critical.
Promise.all rejects immediately if any promise fails. In an agent pipeline, this means a single flaky node cancels the entire wave — including nodes that completed successfully and whose work is lost.
Promise.allSettled waits for all promises to settle (fulfilled or rejected). This enables:
Real Performance Data
Across the first 30 days after enabling parallel execution on AgentDyne Pipelines:
| Pipeline type | Before (sequential) | After (parallel) | Improvement |
|---|---|---|---|
| Due diligence (5 nodes) | 174s P50 | 97s P50 | 44% faster |
| Content pipeline (4 nodes) | 112s P50 | 68s P50 | 39% faster |
| Data enrichment (6 nodes) | 231s P50 | 118s P50 | 49% faster |
| Linear pipeline (3 nodes) | 89s P50 | 91s P50 | ~flat (expected) |
Linear pipelines (where every node depends on the previous) show no improvement — as expected. The gains are entirely from parallelising independent branches.
Designing for Parallelism
To get the most from parallel execution, structure your pipeline to minimise artificial dependencies:
continue_on_failure: true on research/enrichment nodes that would block otherwiseThe parallel agent swarm is not a futuristic concept. It is a DAG with a good scheduler. Build pipelines that way from the start.