← All patterns

Fan-out / Fan-in

Deep Research

The Problem

Running a single LLM call for complex research tasks produces shallow, biased results. The model anchors on whatever comes to mind first. You need breadth — multiple angles pursued simultaneously — then synthesis. Doing this sequentially is slow and expensive. A naive loop adds latency proportional to the number of subtasks.

The Pattern

Input Task
    │
    ▼
┌─────────────┐
│  Decompose  │  (planner agent: split into N subtasks)
└──┬──┬──┬───┘
   │  │  │
   ▼  ▼  ▼
[A1][A2][A3]   (parallel worker agents)
   │  │  │
   └──┴──┘
      │
      ▼
┌──────────┐
│Synthesize│  (aggregator agent: merge results)
└──────────┘
      │
      ▼
   Output

When to Use

When NOT to Use

Code Example

import { QueueAI } from "@queueai/sdk"

const queue = new QueueAI({ apiKey: process.env.QUEUEAI_API_KEY })

const task = await queue.submit({
  name: "deep-research",
  input: {
    question: "What are the best async job queue libraries in 2026?",
    depth: "comprehensive",
  },
  model: {
    primary: "anthropic/claude-opus-4-8",
    fallback: "anthropic/claude-sonnet-4-6",
  },
  budget: { maxCostUsd: 2.00 },
  retries: 3,
})

for await (const event of task.stream()) {
  console.log(event.step, event.message)
}
from queueai import QueueAI

queue = QueueAI(api_key=os.environ["QUEUEAI_API_KEY"])

task = queue.submit(
    name="deep-research",
    input={"question": "What are the best async job queue libraries in 2026?", "depth": "comprehensive"},
    model={"primary": "anthropic/claude-opus-4-8", "fallback": "anthropic/claude-sonnet-4-6"},
    budget={"max_cost_usd": 2.00},
    retries=3,
)

for event in task.stream():
    print(event.step, event.message)

See it in action: This pattern powers the Deep Research template in QueueAI.