ai-functions
Version:
Core AI primitives for building intelligent applications
291 lines (239 loc) • 8.2 kB
text/typescript
/**
* Multi-Agent Research Workflow Example
*
* This example demonstrates a multi-agent research workflow where different
* specialized agents collaborate to research a topic. It shows how to:
* - Define specialized agents with different roles
* - Coordinate agent interactions
* - Aggregate and synthesize results
* - Use tool orchestration for complex workflows
*
* @example
* ```bash
* ANTHROPIC_API_KEY=sk-... npx tsx examples/02-multi-agent-research.ts
* ```
*/
import {
ai,
write,
list,
is,
configure,
AgenticLoop,
createTool,
createToolset,
type Tool,
} from '../src/index.js'
import { z } from 'zod'
// ============================================================================
// Agent Definitions
// ============================================================================
interface AgentResult {
agent: string
findings: string[]
confidence: number
}
/**
* Research Planner Agent - Creates a research plan
*/
async function plannerAgent(topic: string): Promise<string[]> {
console.log('\n[Planner Agent] Creating research plan...')
const questions = await list`5 key research questions to thoroughly investigate: "${topic}"
Consider:
- Background and context
- Current state and trends
- Key players and stakeholders
- Challenges and opportunities
- Future implications`
console.log('[Planner Agent] Research questions:')
questions.forEach((q, i) => console.log(` ${i + 1}. ${q}`))
return questions
}
/**
* Fact Finder Agent - Gathers factual information
*/
async function factFinderAgent(question: string): Promise<AgentResult> {
console.log(`\n[Fact Finder] Researching: ${question.substring(0, 50)}...`)
const { facts, sources, confidence } =
await ai`Research this question and provide factual information:
"${question}"
Provide your response with:
- facts: array of factual findings (3-5 key facts)
- sources: where this information typically comes from
- confidence: your confidence level 0-1 in these facts`
const result: AgentResult = {
agent: 'FactFinder',
findings: facts as string[],
confidence: (confidence as number) || 0.7,
}
console.log(
`[Fact Finder] Found ${result.findings.length} facts (confidence: ${result.confidence})`
)
return result
}
/**
* Critical Analyst Agent - Analyzes and critiques
*/
async function analystAgent(findings: string[]): Promise<AgentResult> {
console.log('\n[Analyst Agent] Analyzing findings...')
const { analysis, gaps, confidence } = await ai`Critically analyze these research findings:
${findings.map((f, i) => `${i + 1}. ${f}`).join('\n')}
Provide:
- analysis: array of analytical insights (3-4 insights)
- gaps: any gaps or areas needing more research
- confidence: confidence in the analysis 0-1`
const result: AgentResult = {
agent: 'Analyst',
findings: analysis as string[],
confidence: (confidence as number) || 0.6,
}
console.log(`[Analyst Agent] Generated ${result.findings.length} insights`)
return result
}
/**
* Synthesizer Agent - Creates final summary
*/
async function synthesizerAgent(allResults: AgentResult[]): Promise<string> {
console.log('\n[Synthesizer Agent] Creating final synthesis...')
const allFindings = allResults.flatMap((r) => r.findings)
const synthesis = await write`Create a comprehensive research summary from these findings:
${allFindings.map((f, i) => `- ${f}`).join('\n')}
Structure the summary with:
1. Executive Summary (2-3 sentences)
2. Key Findings (bullet points)
3. Analysis & Insights
4. Recommendations
5. Areas for Further Research`
return synthesis
}
// ============================================================================
// Coordinator - Orchestrates the Multi-Agent Workflow
// ============================================================================
class ResearchCoordinator {
private topic: string
private results: AgentResult[] = []
constructor(topic: string) {
this.topic = topic
}
async run(): Promise<string> {
console.log(`\n${'='.repeat(60)}`)
console.log(`Research Topic: ${this.topic}`)
console.log('='.repeat(60))
// Phase 1: Planning
const questions = await plannerAgent(this.topic)
// Phase 2: Parallel Research
console.log('\n[Coordinator] Starting parallel research phase...')
const researchPromises = questions.slice(0, 3).map((q) => factFinderAgent(q))
const researchResults = await Promise.all(researchPromises)
this.results.push(...researchResults)
// Phase 3: Analysis
const allFacts = researchResults.flatMap((r) => r.findings)
const analysisResult = await analystAgent(allFacts)
this.results.push(analysisResult)
// Phase 4: Synthesis
const finalReport = await synthesizerAgent(this.results)
// Phase 5: Quality Check
const isQualityOk =
await is`This research summary is well-structured and comprehensive: "${finalReport.substring(
0,
200
)}..."`
if (!isQualityOk) {
console.log('[Coordinator] Quality check failed, requesting revision...')
// In production, you might iterate on the summary
}
return finalReport
}
getResults(): AgentResult[] {
return this.results
}
}
// ============================================================================
// Tool-Based Research Agent (Alternative Approach)
// ============================================================================
const searchTool = createTool({
name: 'search',
description: 'Search for information on a topic',
parameters: {
query: z.string().describe('Search query'),
},
execute: async ({ query }) => {
// Simulate search results
console.log(` [Tool] Searching: ${query}`)
return {
results: [
`Result 1 for "${query}": Found relevant information...`,
`Result 2 for "${query}": Additional context...`,
],
}
},
})
const analyzeTool = createTool({
name: 'analyze',
description: 'Analyze and synthesize information',
parameters: {
data: z.string().describe('Data to analyze'),
perspective: z.string().describe('Analysis perspective'),
},
execute: async ({ data, perspective }) => {
console.log(` [Tool] Analyzing from ${perspective} perspective...`)
return {
analysis: `Analysis of "${data.substring(
0,
30
)}..." from ${perspective} perspective: Key insights identified.`,
}
},
})
async function toolBasedResearch(topic: string): Promise<void> {
console.log('\n--- Tool-Based Research Agent ---')
const loop = new AgenticLoop({
tools: createToolset(searchTool, analyzeTool),
maxSteps: 5,
onStep: (step) => {
console.log(`[Step ${step.stepNumber}] Tool calls: ${step.toolCalls.length}`)
},
})
// This requires a model that supports tool calling
// For demonstration, we'll skip the actual execution
console.log('Tool-based agent configured with:', loop.getToolsForSDK())
}
// ============================================================================
// Main Example
// ============================================================================
async function main() {
console.log('\n=== Multi-Agent Research Workflow ===\n')
// Configure the AI provider
configure({
model: 'sonnet',
provider: 'anthropic',
})
// Run the research workflow
const coordinator = new ResearchCoordinator(
'The impact of Large Language Models on software development practices'
)
const report = await coordinator.run()
console.log('\n' + '='.repeat(60))
console.log('FINAL RESEARCH REPORT')
console.log('='.repeat(60))
console.log(report)
// Show agent statistics
console.log('\n--- Agent Statistics ---')
const results = coordinator.getResults()
for (const result of results) {
console.log(
`${result.agent}: ${result.findings.length} findings (confidence: ${result.confidence})`
)
}
// Demonstrate tool-based approach
await toolBasedResearch('AI in healthcare')
}
main()
.then(() => {
console.log('\n=== Example Complete ===\n')
process.exit(0)
})
.catch((error) => {
console.error('\nError:', error.message)
process.exit(1)
})