Multi-Agent Orchestration
Complex tasks often require multiple specialized agents working together. AgentLIB provides theOrchestrator class to coordinate agent interactions, manage state, and create sophisticated multi-agent workflows.
Overview
The Orchestrator pattern uses:- Planner Agent: A meta-agent that coordinates the workflow
- Sub-Agents: Specialized agents for specific tasks (research, coding, review, etc.)
- Dynamic Routing: The planner decides which agents to invoke and when
- State Management: Centralized tracking of agent interactions and outputs
Basic Orchestrator Setup
Here’s a complete example from the AgentLIB source:import { createAgent } from '@agentlib/core'
import { openai } from '@agentlib/openai'
import { Orchestrator } from '@agentlib/orchestrator'
import '@agentlib/reasoning' // Load reasoning engines
const provider = openai({
apiKey: process.env.OPENAI_API_KEY!,
model: 'gpt-4o',
baseURL: process.env.OPENAI_BASE_URL
})
// 1. Define specialized sub-agents
const researcher = createAgent({
name: 'researcher',
description: 'Finds detailed information about specific topics.',
systemPrompt: 'You are a researcher. Provide concise summaries of facts.'
})
.provider(provider)
.reasoning('react')
const coder = createAgent({
name: 'coder',
description: 'Writes efficient code based on requirements.',
systemPrompt: 'You are an expert coder. Wrap your code in markdown blocks.'
})
.provider(provider)
.reasoning('react')
const critic = createAgent({
name: 'critic',
description: 'Reviews work and provides a score from 0 to 1.',
systemPrompt: 'You are a critical reviewer. Always end your message with "Score: X.X"'
})
.provider(provider)
.reasoning('react')
// 2. Define the planner (meta-agent)
const planner = createAgent({
name: 'planner',
description: 'Main coordinator that uses sub-agents to solve complex tasks.',
systemPrompt: 'You are a project manager. Use the available agents to research, code, and review.'
})
.provider(provider)
.reasoning('react')
// 3. Initialize the Orchestrator
const orchestrator = new Orchestrator(planner, {
agents: {
researcher,
coder,
critic
},
maxSteps: 10,
summarize: true,
exposeAgentsAsTools: true,
context: {
company: 'ACME Corp',
constraints: 'Follow ACME Corp coding standards'
},
onStep: (step) => {
console.log(`[Step] Agent: ${step.agent} | Type: ${step.type}`)
// Conditional abort based on step content
if (step.agent === 'critic' && step.content?.includes('Score: 0.3')) {
console.warn('Low quality work detected! Aborting...')
return 'abort'
}
}
})
// 4. Add event listeners
orchestrator.on('agent:invoke', ({ agent, prompt }) => {
console.log(`\n🚀 Invoking agent: ${agent}`)
console.log(`Prompt: ${prompt}`)
})
orchestrator.on('agent:completed', ({ agent, output }) => {
console.log(`\n✅ Agent ${agent} finished!`)
console.log(`Output: ${output.substring(0, 100)}...`)
})
// 5. Run the orchestrator
console.log('--- Starting Orchestration ---')
await orchestrator.run('Design and implement a scalable image processing system.')
console.log('\n--- Orchestration Finished ---')
// 6. Access final states
const states = orchestrator.getAllStates()
console.log('\nFinal States:', states.map(s => `${s.name}: ${s.status}`))
Orchestrator Configuration
Core Options
interface OrchestratorOptions {
// Map of available sub-agents
agents: Record<string, AgentInstance>
// Maximum steps before auto-termination
maxSteps?: number
// Auto-summarize sub-agent outputs for context efficiency
summarize?: boolean
// Expose sub-agents as tools to the planner
exposeAgentsAsTools?: boolean
// Shared context available to all agents
context?: Record<string, any>
// Step-by-step callback for monitoring/control
onStep?: (step: OrchestrationStep) => void | 'abort' | 'continue'
}
Example 1: Research Pipeline
A multi-stage research workflow:import { createAgent } from '@agentlib/core'
import { openai } from '@agentlib/openai'
import { Orchestrator } from '@agentlib/orchestrator'
import { defineTool } from '@agentlib/core'
const provider = openai({ apiKey: process.env.OPENAI_API_KEY! })
// Define specialized tools
const webSearchTool = defineTool({
schema: {
name: 'web_search',
description: 'Search the web for current information',
parameters: {
type: 'object',
properties: {
query: { type: 'string', description: 'Search query' }
},
required: ['query']
}
},
async execute({ query }) {
// Integration with search API
return { results: [`Information about ${query}`] }
}
})
const databaseQueryTool = defineTool({
schema: {
name: 'query_database',
description: 'Query internal company database',
parameters: {
type: 'object',
properties: {
sql: { type: 'string', description: 'SQL query' }
},
required: ['sql']
}
},
async execute({ sql }) {
// Execute query against DB
return { rows: [] }
}
})
// Create specialized agents
const webResearcher = createAgent({
name: 'web-researcher',
description: 'Searches the web for current information',
systemPrompt: 'You search the web and summarize findings.'
})
.provider(provider)
.tool(webSearchTool)
.reasoning('react')
const dataAnalyst = createAgent({
name: 'data-analyst',
description: 'Queries and analyzes internal data',
systemPrompt: 'You analyze company data and provide insights.'
})
.provider(provider)
.tool(databaseQueryTool)
.reasoning('react')
const synthesizer = createAgent({
name: 'synthesizer',
description: 'Combines research from multiple sources',
systemPrompt: 'You synthesize information from multiple sources into coherent reports.'
})
.provider(provider)
.reasoning('cot')
const planner = createAgent({
name: 'research-coordinator',
systemPrompt: `You coordinate research tasks. Available agents:
- web-researcher: For current web information
- data-analyst: For internal company data
- synthesizer: For combining findings into reports
Always gather information before synthesizing.`
})
.provider(provider)
.reasoning('planner')
const orchestrator = new Orchestrator(planner, {
agents: {
'web-researcher': webResearcher,
'data-analyst': dataAnalyst,
synthesizer
},
maxSteps: 15,
summarize: true,
exposeAgentsAsTools: true,
onStep: (step) => {
console.log(`[${step.agent}] ${step.type}:`, step.content?.substring(0, 80))
}
})
const result = await orchestrator.run(
'Research market trends for AI agents in 2025, including both public data and our internal sales figures.'
)
console.log('Research Report:', result.output)
Example 2: Code Generation Pipeline
A system that designs, implements, tests, and reviews code:import { createAgent } from '@agentlib/core'
import { openai } from '@agentlib/openai'
import { Orchestrator } from '@agentlib/orchestrator'
const provider = openai({ apiKey: process.env.OPENAI_API_KEY! })
// Architect designs the system
const architect = createAgent({
name: 'architect',
description: 'Designs system architecture and API contracts',
systemPrompt: 'You design software architecture. Output JSON specifications.'
})
.provider(provider)
.reasoning('cot')
// Developer implements the code
const developer = createAgent({
name: 'developer',
description: 'Implements code based on specifications',
systemPrompt: 'You write clean, well-documented code following specifications.'
})
.provider(provider)
.reasoning('react')
// Tester creates test cases
const tester = createAgent({
name: 'tester',
description: 'Creates comprehensive test cases',
systemPrompt: 'You write thorough test cases covering edge cases.'
})
.provider(provider)
.reasoning('react')
// Reviewer provides code review
const reviewer = createAgent({
name: 'reviewer',
description: 'Reviews code for quality, security, and best practices',
systemPrompt: 'You review code critically. Provide scores and actionable feedback.'
})
.provider(provider)
.reasoning('reflect')
// Engineering manager coordinates
const engineeringManager = createAgent({
name: 'engineering-manager',
systemPrompt: `You manage the development pipeline:
1. architect designs the system
2. developer implements
3. tester creates tests
4. reviewer provides feedback
5. If score < 0.7, iterate with developer
Only proceed when quality standards are met.`
})
.provider(provider)
.reasoning('planner')
const orchestrator = new Orchestrator(engineeringManager, {
agents: { architect, developer, tester, reviewer },
maxSteps: 20,
context: {
language: 'TypeScript',
framework: 'Node.js',
qualityThreshold: 0.7
},
onStep: (step) => {
// Track quality scores
if (step.agent === 'reviewer') {
const scoreMatch = step.content?.match(/Score: ([0-9.]+)/)
if (scoreMatch) {
const score = parseFloat(scoreMatch[1])
console.log(`📊 Quality Score: ${score}`)
if (score < 0.7) {
console.log('⚠️ Below quality threshold, revision needed')
}
}
}
}
})
const result = await orchestrator.run(
'Create a REST API for user authentication with JWT tokens'
)
Example 3: Dynamic Agent Routing
Route requests to appropriate specialists based on content:import { createAgent } from '@agentlib/core'
import { openai } from '@agentlib/openai'
import { Orchestrator } from '@agentlib/orchestrator'
const provider = openai({ apiKey: process.env.OPENAI_API_KEY! })
// Domain specialists
const specialists = {
finance: createAgent({
name: 'finance-expert',
description: 'Expert in financial analysis and accounting',
systemPrompt: 'You are a financial expert. Provide detailed financial analysis.'
}).provider(provider).reasoning('cot'),
legal: createAgent({
name: 'legal-expert',
description: 'Expert in legal compliance and contracts',
systemPrompt: 'You are a legal expert. Analyze from a legal compliance perspective.'
}).provider(provider).reasoning('cot'),
technical: createAgent({
name: 'technical-expert',
description: 'Expert in technical implementation',
systemPrompt: 'You are a technical expert. Focus on implementation details.'
}).provider(provider).reasoning('react'),
marketing: createAgent({
name: 'marketing-expert',
description: 'Expert in marketing strategy',
systemPrompt: 'You are a marketing expert. Analyze market positioning and strategy.'
}).provider(provider).reasoning('cot')
}
const router = createAgent({
name: 'router',
systemPrompt: `You route questions to the appropriate specialist:
- finance-expert: Financial, accounting, budgets
- legal-expert: Contracts, compliance, regulations
- technical-expert: Implementation, architecture, code
- marketing-expert: Marketing, branding, positioning
You may consult multiple specialists if needed.`
})
.provider(provider)
.reasoning('react')
const orchestrator = new Orchestrator(router, {
agents: specialists,
maxSteps: 10,
exposeAgentsAsTools: true,
summarize: true,
onStep: (step) => {
if (step.type === 'agent:invoke') {
console.log(`🎯 Routing to: ${step.agent}`)
}
}
})
// The router will automatically select the right specialist(s)
await orchestrator.run('What are the tax implications of our new SaaS pricing model?')
// Expected: Routes to finance-expert and legal-expert
await orchestrator.run('How should we architect our microservices deployment?')
// Expected: Routes to technical-expert
Monitoring and Control
Step Monitoring
const orchestrator = new Orchestrator(planner, {
agents: { agent1, agent2 },
onStep: (step) => {
// Log all steps
console.log(`[${step.timestamp}] ${step.agent}: ${step.type}`)
// Conditional abort
if (step.content?.includes('ERROR')) {
return 'abort'
}
// Collect metrics
metrics.recordStep(step)
return 'continue'
}
})
Event Listeners
// Agent lifecycle events
orchestrator.on('agent:invoke', ({ agent, prompt }) => {
console.log(`Invoking ${agent}`)
})
orchestrator.on('agent:completed', ({ agent, output }) => {
console.log(`${agent} completed`)
})
orchestrator.on('agent:failed', ({ agent, error }) => {
console.error(`${agent} failed:`, error)
})
// Orchestration events
orchestrator.on('orchestration:start', ({ input }) => {
console.log('Starting orchestration')
})
orchestrator.on('orchestration:complete', ({ output }) => {
console.log('Orchestration complete')
})
State Inspection
// Get all agent states
const states = orchestrator.getAllStates()
states.forEach(state => {
console.log(`${state.name}:`, {
status: state.status,
invocations: state.invocations,
lastOutput: state.lastOutput?.substring(0, 50)
})
})
// Get specific agent state
const researcherState = orchestrator.getAgentState('researcher')
console.log('Researcher invoked:', researcherState.invocations, 'times')
Best Practices
- Clear Agent Descriptions: The planner uses descriptions to decide which agent to invoke
- Appropriate Reasoning Engines: Match engines to agent roles (e.g.,
plannerfor coordinators,reactfor tool-users) - Set Reasonable Step Limits: Prevent infinite loops with
maxSteps - Use Summarization: Enable
summarize: truefor long-running workflows to manage context - Monitor Progress: Use
onStepcallback for visibility and control - Handle Failures Gracefully: Add error handling in event listeners
- Share Context: Use the
contextoption for shared state across agents
Advanced Patterns
Hierarchical Orchestration
// Sub-orchestrators for complex workflows
const researchOrchestrator = new Orchestrator(researchPlanner, {
agents: { webResearcher, dataAnalyst }
})
const developmentOrchestrator = new Orchestrator(devPlanner, {
agents: { architect, developer, tester }
})
// Main orchestrator coordinates sub-orchestrators
const mainOrchestrator = new Orchestrator(mainPlanner, {
agents: {
research: researchOrchestrator,
development: developmentOrchestrator
}
})
Conditional Workflows
const orchestrator = new Orchestrator(planner, {
agents: { agent1, agent2, agent3 },
onStep: (step) => {
// Route based on intermediate results
if (step.agent === 'agent1' && step.content?.includes('complex')) {
// Inject additional context for complex cases
return { continueWith: 'agent3', skipAgent2: true }
}
}
})
Next Steps
- Learn about Custom Reasoning Engines
- Explore Custom Middleware for orchestration hooks
- See Built-in Reasoning Strategies for engine options