Skip to content

Commit 91494bc

Browse files
authored
Merge pull request #79 from mvanhorn/osc/76-research-aggregation
feat: add multi-source research aggregation example
2 parents faf24aa + 54bfe2e commit 91494bc

1 file changed

Lines changed: 169 additions & 0 deletions

File tree

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
/**
2+
* Example 14 — Multi-Source Research Aggregation
3+
*
4+
* Demonstrates runTasks() with explicit dependency chains:
5+
* - Parallel execution: three analyst agents research the same topic independently
6+
* - Dependency chain via dependsOn: synthesizer waits for all analysts to finish
7+
* - Automatic shared memory: agent output flows to downstream agents via the framework
8+
*
9+
* Compare with example 07 (fan-out-aggregate) which uses AgentPool.runParallel()
10+
* for the same 3-analysts + synthesizer pattern. This example shows the runTasks()
11+
* API with explicit dependsOn declarations instead.
12+
*
13+
* Flow:
14+
* [technical-analyst, market-analyst, community-analyst] (parallel) → synthesizer
15+
*
16+
* Run:
17+
* npx tsx examples/14-research-aggregation.ts
18+
*
19+
* Prerequisites:
20+
* ANTHROPIC_API_KEY env var must be set.
21+
*/
22+
23+
import { OpenMultiAgent } from '../src/index.js'
24+
import type { AgentConfig, OrchestratorEvent } from '../src/types.js'
25+
26+
// ---------------------------------------------------------------------------
27+
// Topic
28+
// ---------------------------------------------------------------------------
29+
30+
const TOPIC = 'WebAssembly adoption in 2026'
31+
32+
// ---------------------------------------------------------------------------
33+
// Agents — three analysts + one synthesizer
34+
// ---------------------------------------------------------------------------
35+
36+
const technicalAnalyst: AgentConfig = {
37+
name: 'technical-analyst',
38+
model: 'claude-sonnet-4-6',
39+
systemPrompt: `You are a technical analyst. Given a topic, research its technical
40+
capabilities, limitations, performance characteristics, and architectural patterns.
41+
Write your findings as structured markdown. Keep it to 200-300 words.`,
42+
maxTurns: 2,
43+
}
44+
45+
const marketAnalyst: AgentConfig = {
46+
name: 'market-analyst',
47+
model: 'claude-sonnet-4-6',
48+
systemPrompt: `You are a market analyst. Given a topic, research industry adoption
49+
rates, key companies using the technology, market size estimates, and competitive
50+
landscape. Write your findings as structured markdown. Keep it to 200-300 words.`,
51+
maxTurns: 2,
52+
}
53+
54+
const communityAnalyst: AgentConfig = {
55+
name: 'community-analyst',
56+
model: 'claude-sonnet-4-6',
57+
systemPrompt: `You are a developer community analyst. Given a topic, research
58+
developer sentiment, ecosystem maturity, learning resources, community size,
59+
and conference/meetup activity. Write your findings as structured markdown.
60+
Keep it to 200-300 words.`,
61+
maxTurns: 2,
62+
}
63+
64+
const synthesizer: AgentConfig = {
65+
name: 'synthesizer',
66+
model: 'claude-sonnet-4-6',
67+
systemPrompt: `You are a research director who synthesizes multiple analyst reports
68+
into a single cohesive document. You will receive all prior analyst outputs
69+
automatically. Then:
70+
71+
1. Cross-reference claims across reports - flag agreements and contradictions
72+
2. Identify the 3 most important insights
73+
3. Produce a structured report with: Executive Summary, Key Findings,
74+
Areas of Agreement, Open Questions, and Recommendation
75+
76+
Keep the final report to 300-400 words.`,
77+
maxTurns: 2,
78+
}
79+
80+
// ---------------------------------------------------------------------------
81+
// Orchestrator + team
82+
// ---------------------------------------------------------------------------
83+
84+
function handleProgress(event: OrchestratorEvent): void {
85+
if (event.type === 'task_start') {
86+
console.log(` [START] ${event.task ?? ''}${event.agent ?? ''}`)
87+
}
88+
if (event.type === 'task_complete') {
89+
console.log(` [DONE] ${event.task ?? ''}`)
90+
}
91+
}
92+
93+
const orchestrator = new OpenMultiAgent({
94+
defaultModel: 'claude-sonnet-4-6',
95+
onProgress: handleProgress,
96+
})
97+
98+
const team = orchestrator.createTeam('research-team', {
99+
name: 'research-team',
100+
agents: [technicalAnalyst, marketAnalyst, communityAnalyst, synthesizer],
101+
sharedMemory: true,
102+
})
103+
104+
// ---------------------------------------------------------------------------
105+
// Tasks — three analysts run in parallel, synthesizer depends on all three
106+
// ---------------------------------------------------------------------------
107+
108+
const tasks = [
109+
{
110+
title: 'Technical analysis',
111+
description: `Research the technical aspects of ${TOPIC}. Focus on capabilities, limitations, performance, and architecture.`,
112+
assignee: 'technical-analyst',
113+
},
114+
{
115+
title: 'Market analysis',
116+
description: `Research the market landscape for ${TOPIC}. Focus on adoption rates, key players, market size, and competition.`,
117+
assignee: 'market-analyst',
118+
},
119+
{
120+
title: 'Community analysis',
121+
description: `Research the developer community around ${TOPIC}. Focus on sentiment, ecosystem maturity, learning resources, and community activity.`,
122+
assignee: 'community-analyst',
123+
},
124+
{
125+
title: 'Synthesize report',
126+
description: `Cross-reference all analyst findings, identify key insights, flag contradictions, and produce a unified research report.`,
127+
assignee: 'synthesizer',
128+
dependsOn: ['Technical analysis', 'Market analysis', 'Community analysis'],
129+
},
130+
]
131+
132+
// ---------------------------------------------------------------------------
133+
// Run
134+
// ---------------------------------------------------------------------------
135+
136+
console.log('Multi-Source Research Aggregation')
137+
console.log('='.repeat(60))
138+
console.log(`Topic: ${TOPIC}`)
139+
console.log('Pipeline: 3 analysts (parallel) → synthesizer')
140+
console.log('='.repeat(60))
141+
console.log()
142+
143+
const result = await orchestrator.runTasks(team, tasks)
144+
145+
// ---------------------------------------------------------------------------
146+
// Output
147+
// ---------------------------------------------------------------------------
148+
149+
console.log('\n' + '='.repeat(60))
150+
console.log(`Overall success: ${result.success}`)
151+
console.log(`Tokens — input: ${result.totalTokenUsage.input_tokens}, output: ${result.totalTokenUsage.output_tokens}`)
152+
console.log()
153+
154+
for (const [name, r] of result.agentResults) {
155+
const icon = r.success ? 'OK ' : 'FAIL'
156+
const tokens = `in:${r.tokenUsage.input_tokens} out:${r.tokenUsage.output_tokens}`
157+
console.log(` [${icon}] ${name.padEnd(20)} ${tokens}`)
158+
}
159+
160+
const synthResult = result.agentResults.get('synthesizer')
161+
if (synthResult?.success) {
162+
console.log('\n' + '='.repeat(60))
163+
console.log('SYNTHESIZED REPORT')
164+
console.log('='.repeat(60))
165+
console.log()
166+
console.log(synthResult.output)
167+
}
168+
169+
console.log('\nDone.')

0 commit comments

Comments
 (0)