← All examplesEnterprise support
// Orchestration
Fan-Out / Aggregate (MapReduce) Pattern
Demonstrates: - Fan-out: send the same question to N "analyst" agents in parallel - Aggregate: a "synthesizer" agent reads all analyst outputs and produces a balanced final report - AgentPool with runParallel() for concurrent fan-out - No tools needed — pure LLM reasoning to keep the focus on the pattern
01 Run it
OMA APIs
AgentAgentPoolToolRegistryToolExecutorregisterBuiltInToolsFrom a clone of the repo — this exact file:
npx tsx packages/core/examples/patterns/fan-out-aggregate.ts
Prerequisites
- ANTHROPIC_API_KEY env var must be set.
OMA is provider-agnostic — this example is written for the key above, but you can run it on OpenAI, Gemini, Groq and others. All providers →
Open the complete, synchronized source · 209 lines
The complete example, synchronized from the pinned Framework commit.
/*** Fan-Out / Aggregate (MapReduce) Pattern** Demonstrates:* - Fan-out: send the same question to N "analyst" agents in parallel* - Aggregate: a "synthesizer" agent reads all analyst outputs and produces* a balanced final report* - AgentPool with runParallel() for concurrent fan-out* - No tools needed — pure LLM reasoning to keep the focus on the pattern** Run:* npx tsx packages/core/examples/patterns/fan-out-aggregate.ts** Prerequisites:* ANTHROPIC_API_KEY env var must be set.*/import { Agent, AgentPool, ToolRegistry, ToolExecutor, registerBuiltInTools } from '../../src/index.js'import type { AgentConfig, AgentRunResult } from '../../src/types.js'// ---------------------------------------------------------------------------// Analysis topic// ---------------------------------------------------------------------------const TOPIC = `Should a solo developer build a SaaS product that uses AI agentsfor automated customer support? Consider the current state of AI technology,market demand, competition, costs, and the unique constraints of being a solofounder with limited time (~6 hours/day of productive work).`// ---------------------------------------------------------------------------// Analyst agent configs — three perspectives on the same question// ---------------------------------------------------------------------------const optimistConfig: AgentConfig = {name: 'optimist',model: 'claude-sonnet-4-6',systemPrompt: `You are an optimistic technology analyst who focuses onopportunities, upside potential, and emerging trends. You see possibilitieswhere others see obstacles. Back your optimism with concrete reasoning —cite market trends, cost curves, and real capabilities. Keep your analysisto 200-300 words.`,maxTurns: 1,temperature: 0.4,}const skepticConfig: AgentConfig = {name: 'skeptic',model: 'claude-sonnet-4-6',systemPrompt: `You are a skeptical technology analyst who focuses on risks,challenges, failure modes, and hidden costs. You stress-test assumptions andask "what could go wrong?" Back your skepticism with concrete reasoning —cite failure rates, technical limitations, and market realities. Keep youranalysis to 200-300 words.`,maxTurns: 1,temperature: 0.4,}const pragmatistConfig: AgentConfig = {name: 'pragmatist',model: 'claude-sonnet-4-6',systemPrompt: `You are a pragmatic technology analyst who focuses on practicalfeasibility, execution complexity, and resource requirements. You care aboutwhat works today, not what might work someday. You think in terms of MVPs,timelines, and concrete tradeoffs. Keep your analysis to 200-300 words.`,maxTurns: 1,temperature: 0.4,}const synthesizerConfig: AgentConfig = {name: 'synthesizer',model: 'claude-sonnet-4-6',systemPrompt: `You are a senior strategy advisor who synthesizes multipleperspectives into a balanced, actionable recommendation. You do not simplysummarise — you weigh the arguments, identify where they agree and disagree,and produce a clear verdict with next steps. Structure your output as:1. Key agreements across perspectives2. Key disagreements and how you weigh them3. Verdict (go / no-go / conditional go)4. Recommended next steps (3-5 bullet points)Keep the final report to 300-400 words.`,maxTurns: 1,temperature: 0.3,}// ---------------------------------------------------------------------------// Build agents — no tools needed for pure reasoning// ---------------------------------------------------------------------------function buildAgent(config: AgentConfig): Agent {const registry = new ToolRegistry()registerBuiltInTools(registry) // available to grant, but these agents grant none (tools are opt-in) — pure reasoningconst executor = new ToolExecutor(registry)return new Agent(config, registry, executor)}const optimist = buildAgent(optimistConfig)const skeptic = buildAgent(skepticConfig)const pragmatist = buildAgent(pragmatistConfig)const synthesizer = buildAgent(synthesizerConfig)// ---------------------------------------------------------------------------// Set up the pool// ---------------------------------------------------------------------------const pool = new AgentPool(3) // 3 analysts can run simultaneouslypool.add(optimist)pool.add(skeptic)pool.add(pragmatist)pool.add(synthesizer)console.log('Fan-Out / Aggregate (MapReduce) Pattern')console.log('='.repeat(60))console.log(`\nTopic: ${TOPIC.replace(/\n/g, ' ').trim()}\n`)// ---------------------------------------------------------------------------// Step 1: Fan-out — run all 3 analysts in parallel// ---------------------------------------------------------------------------console.log('[Step 1] Fan-out: 3 analysts running in parallel...\n')const analystResults: Map<string, AgentRunResult> = await pool.runParallel([{ agent: 'optimist', prompt: TOPIC },{ agent: 'skeptic', prompt: TOPIC },{ agent: 'pragmatist', prompt: TOPIC },])// Print each analyst's output (truncated)const analysts = ['optimist', 'skeptic', 'pragmatist'] as constfor (const name of analysts) {const result = analystResults.get(name)!const status = result.success ? 'OK' : 'FAILED'console.log(` ${name} [${status}] — ${result.tokenUsage.output_tokens} output tokens`)console.log(` ${result.output.slice(0, 150).replace(/\n/g, ' ')}...`)console.log()}// Check all analysts succeededfor (const name of analysts) {if (!analystResults.get(name)!.success) {console.error(`Analyst '${name}' failed: ${analystResults.get(name)!.output}`)process.exit(1)}}// ---------------------------------------------------------------------------// Step 2: Aggregate — synthesizer reads all 3 analyses// ---------------------------------------------------------------------------console.log('[Step 2] Aggregate: synthesizer producing final report...\n')const synthesizerPrompt = `Three analysts have independently evaluated the same question.Read their analyses below and produce your synthesis report.--- OPTIMIST ---${analystResults.get('optimist')!.output}--- SKEPTIC ---${analystResults.get('skeptic')!.output}--- PRAGMATIST ---${analystResults.get('pragmatist')!.output}Now synthesize these three perspectives into a balanced recommendation.`const synthResult = await pool.run('synthesizer', synthesizerPrompt)if (!synthResult.success) {console.error('Synthesizer failed:', synthResult.output)process.exit(1)}// ---------------------------------------------------------------------------// Final output// ---------------------------------------------------------------------------console.log('='.repeat(60))console.log('SYNTHESIZED REPORT')console.log('='.repeat(60))console.log()console.log(synthResult.output)console.log()console.log('-'.repeat(60))// ---------------------------------------------------------------------------// Token usage comparison// ---------------------------------------------------------------------------console.log('\nToken Usage Summary:')console.log('-'.repeat(60))let totalInput = 0let totalOutput = 0for (const name of analysts) {const r = analystResults.get(name)!totalInput += r.tokenUsage.input_tokenstotalOutput += r.tokenUsage.output_tokensconsole.log(` ${name.padEnd(12)} — input: ${r.tokenUsage.input_tokens}, output: ${r.tokenUsage.output_tokens}`)}totalInput += synthResult.tokenUsage.input_tokenstotalOutput += synthResult.tokenUsage.output_tokensconsole.log(` ${'synthesizer'.padEnd(12)} — input: ${synthResult.tokenUsage.input_tokens}, output: ${synthResult.tokenUsage.output_tokens}`)console.log('-'.repeat(60))console.log(` ${'TOTAL'.padEnd(12)} — input: ${totalInput}, output: ${totalOutput}`)console.log('\nDone.')
// Learn the concepts
// Enterprise
Taking this to production?
open-multi-agent is MIT-licensed and free to run yourself. When you need it delivered, integrated, or supported on a deadline, 元定义科技 (YuanASI) offers commercial delivery and support.