UNPKG

claude-flow

Version:

Enterprise-grade AI agent orchestration with ruv-swarm integration (Alpha Release)

1,059 lines (918 loc) 33.2 kB
import { getErrorMessage } from '../../utils/error-handler.js'; /** * Optimized Research Strategy Implementation * Provides intelligent research capabilities with parallel processing, * semantic clustering, caching, and progressive refinement */ import { BaseStrategy } from './base.js'; import type { DecompositionResult, StrategyMetrics } from './base.js'; import { Logger } from '../../core/logger.js'; import { generateId } from '../../utils/helpers.js'; import { SwarmObjective, TaskDefinition, TaskId, TaskType, TaskPriority, SwarmConfig, SWARM_CONSTANTS } from '../types.js'; // Research-specific interfaces interface ResearchQuery { id: string; query: string; keywords: string[]; domains: string[]; priority: number; timestamp: Date; sources?: string[]; filters?: ResearchFilters; } interface ResearchFilters { dateRange?: { start: Date; end: Date }; sourceTypes?: ('academic' | 'news' | 'blog' | 'documentation' | 'forum')[]; languages?: string[]; credibilityThreshold?: number; maxResults?: number; } interface ResearchResult { id: string; queryId: string; url: string; title: string; content: string; summary: string; credibilityScore: number; relevanceScore: number; sourceType: string; publishedDate?: Date; extractedAt: Date; metadata: Record<string, any>; semanticVector?: number[]; } interface ResearchCluster { id: string; topic: string; results: ResearchResult[]; centroid: number[]; coherenceScore: number; keywords: string[]; summary: string; } interface CacheEntry { key: string; data: any; timestamp: Date; ttl: number; accessCount: number; lastAccessed: Date; } interface ConnectionPool { active: number; idle: number; max: number; timeout: number; connections: Map<string, any>; } interface RateLimiter { requests: number; windowStart: Date; windowSize: number; maxRequests: number; backoffMultiplier: number; } export class ResearchStrategy extends BaseStrategy { private logger: Logger; private researchCache: Map<string, CacheEntry> = new Map(); private connectionPool: ConnectionPool; private rateLimiters: Map<string, RateLimiter> = new Map(); private semanticModel: any; // Placeholder for semantic analysis private researchQueries: Map<string, ResearchQuery> = new Map(); private researchResults: Map<string, ResearchResult> = new Map(); private researchClusters: Map<string, ResearchCluster> = new Map(); // Research-specific metrics extending base metrics private researchMetrics = { queriesExecuted: 0, resultsCollected: 0, cacheHits: 0, cacheMisses: 0, averageResponseTime: 0, credibilityScores: [] as number[], clusteringAccuracy: 0, parallelEfficiency: 0 }; constructor(config: Partial<SwarmConfig> = {}) { const defaultConfig: SwarmConfig = { name: 'research-strategy', description: 'Research-focused strategy', version: '1.0.0', mode: 'mesh', strategy: 'research', coordinationStrategy: { name: 'research-coordination', description: 'Research-optimized coordination', agentSelection: 'capability-based', taskScheduling: 'priority', loadBalancing: 'work-sharing', faultTolerance: 'retry', communication: 'direct' }, maxAgents: 8, maxTasks: 50, maxDuration: 3600000, resourceLimits: {}, qualityThreshold: 0.8, reviewRequired: true, testingRequired: false, monitoring: { metricsEnabled: true, loggingEnabled: true, tracingEnabled: false, metricsInterval: 5000, heartbeatInterval: 10000, healthCheckInterval: 30000, retentionPeriod: 86400000, maxLogSize: 1048576, maxMetricPoints: 1000, alertingEnabled: false, alertThresholds: {}, exportEnabled: false, exportFormat: 'json', exportDestination: 'file' }, memory: { namespace: 'research', partitions: [], permissions: { read: 'swarm', write: 'swarm', delete: 'team', share: 'swarm' }, persistent: true, backupEnabled: false, distributed: false, consistency: 'eventual', cacheEnabled: true, compressionEnabled: false }, security: { authenticationRequired: false, authorizationRequired: false, encryptionEnabled: false, defaultPermissions: ['read', 'write'], adminRoles: ['admin'], auditEnabled: false, auditLevel: 'info', inputValidation: true, outputSanitization: true }, performance: { maxConcurrency: 10, defaultTimeout: 300000, cacheEnabled: true, cacheSize: 100, cacheTtl: 3600000, optimizationEnabled: true, adaptiveScheduling: true, predictiveLoading: false, resourcePooling: true, connectionPooling: true, memoryPooling: false } }; const mergedConfig = { ...defaultConfig, ...config }; super(mergedConfig); this.logger = new Logger( { level: 'info', format: 'text', destination: 'console' }, { component: 'ResearchStrategy' } ); // Initialize connection pool this.connectionPool = { active: 0, idle: 0, max: config.performance?.maxConcurrency || 10, timeout: 30000, connections: new Map() }; this.logger.info('ResearchStrategy initialized with optimizations', { maxConcurrency: this.connectionPool.max, cacheEnabled: config.performance?.cacheEnabled !== false }); } async decomposeObjective(objective: SwarmObjective): Promise<DecompositionResult> { this.logger.info('Decomposing research objective', { objectiveId: objective.id, description: objective.description }); const tasks: TaskDefinition[] = []; const dependencies = new Map<string, string[]>(); // Extract research parameters from objective const researchParams = this.extractResearchParameters(objective.description); // Create research query planning task const queryPlanningTask = this.createResearchTask( 'query-planning', 'research', 'Research Query Planning', `Analyze the research objective and create optimized search queries: ${objective.description} Create a comprehensive research plan that includes: 1. Primary and secondary research questions 2. Key search terms and synonyms 3. Relevant domains and sources to explore 4. Research methodology and approach 5. Quality criteria for evaluating sources Focus on creating queries that will yield high-quality, credible results.`, { priority: 'high' as TaskPriority, estimatedDuration: 5 * 60 * 1000, // 5 minutes requiredCapabilities: ['research', 'analysis'], researchParams } ); tasks.push(queryPlanningTask); // Create parallel web search tasks const webSearchTask = this.createResearchTask( 'web-search', 'research', 'Parallel Web Search Execution', `Execute parallel web searches based on the research plan: ${objective.description} Perform comprehensive web searches using: 1. Multiple search engines and sources 2. Parallel query execution for efficiency 3. Intelligent source ranking and filtering 4. Real-time credibility assessment 5. Deduplication of results Collect diverse, high-quality sources relevant to the research objective.`, { priority: 'high' as TaskPriority, estimatedDuration: 10 * 60 * 1000, // 10 minutes requiredCapabilities: ['web-search', 'research'], dependencies: [queryPlanningTask.id.id], researchParams } ); tasks.push(webSearchTask); dependencies.set(webSearchTask.id.id, [queryPlanningTask.id.id]); // Create data extraction and processing task const dataExtractionTask = this.createResearchTask( 'data-extraction', 'analysis', 'Parallel Data Extraction', `Extract and process data from collected sources: ${objective.description} Process the collected sources by: 1. Extracting key information and insights 2. Performing semantic analysis and clustering 3. Identifying patterns and relationships 4. Assessing information quality and reliability 5. Creating structured summaries Use parallel processing for efficient data extraction.`, { priority: 'high' as TaskPriority, estimatedDuration: 8 * 60 * 1000, // 8 minutes requiredCapabilities: ['analysis', 'research'], dependencies: [webSearchTask.id.id], researchParams } ); tasks.push(dataExtractionTask); dependencies.set(dataExtractionTask.id.id, [webSearchTask.id.id]); // Create semantic clustering task const clusteringTask = this.createResearchTask( 'semantic-clustering', 'analysis', 'Semantic Clustering and Analysis', `Perform semantic clustering of research findings: ${objective.description} Analyze the extracted data by: 1. Grouping related information using semantic similarity 2. Identifying key themes and topics 3. Creating coherent clusters of information 4. Generating cluster summaries and insights 5. Mapping relationships between clusters Provide a structured analysis of the research findings.`, { priority: 'medium' as TaskPriority, estimatedDuration: 6 * 60 * 1000, // 6 minutes requiredCapabilities: ['analysis', 'research'], dependencies: [dataExtractionTask.id.id], researchParams } ); tasks.push(clusteringTask); dependencies.set(clusteringTask.id.id, [dataExtractionTask.id.id]); // Create synthesis and reporting task const synthesisTask = this.createResearchTask( 'synthesis-reporting', 'documentation', 'Research Synthesis and Reporting', `Synthesize research findings into comprehensive report: ${objective.description} Create a comprehensive research report that includes: 1. Executive summary of key findings 2. Detailed analysis of each research cluster 3. Insights and recommendations 4. Source credibility assessment 5. Methodology and limitations 6. References and citations Ensure the report is well-structured and actionable.`, { priority: 'medium' as TaskPriority, estimatedDuration: 7 * 60 * 1000, // 7 minutes requiredCapabilities: ['documentation', 'analysis'], dependencies: [clusteringTask.id.id], researchParams } ); tasks.push(synthesisTask); dependencies.set(synthesisTask.id.id, [clusteringTask.id.id]); const totalDuration = tasks.reduce((sum, task) => sum + (task.constraints.timeoutAfter || 0), 0 ); this.logger.info('Research objective decomposed', { objectiveId: objective.id, taskCount: tasks.length, estimatedDuration: totalDuration, parallelTasks: tasks.filter(t => !dependencies.has(t.id.id)).length }); return { tasks, dependencies, estimatedDuration: totalDuration, recommendedStrategy: 'research', complexity: this.estimateComplexity(objective.description), batchGroups: this.createTaskBatches(tasks, dependencies), timestamp: new Date(), ttl: 3600000, // 1 hour accessCount: 0, lastAccessed: new Date(), data: { objectiveId: objective.id, description: objective.description }, resourceRequirements: { memory: SWARM_CONSTANTS.DEFAULT_MEMORY_LIMIT * 1.5, cpu: SWARM_CONSTANTS.DEFAULT_CPU_LIMIT * 1.2, network: 'high', storage: 'medium' } }; } // Research-specific optimizations for task execution async optimizeTaskExecution(task: TaskDefinition, agent: any): Promise<any> { const startTime = Date.now(); try { // Apply research-specific optimizations based on task type switch (task.type) { case 'research': return await this.executeOptimizedWebSearch(task, agent); case 'analysis': return await this.executeOptimizedDataExtraction(task, agent); default: return await this.executeGenericResearchTask(task, agent); } } finally { const duration = Date.now() - startTime; this.updateResearchMetrics(task.type, duration); } } private async executeOptimizedWebSearch(task: TaskDefinition, agent: any): Promise<any> { this.logger.info('Executing optimized web search', { taskId: task.id.id }); // Check cache first const cacheKey = this.generateCacheKey('web-search', task.description); const cached = this.getFromCache(cacheKey); if (cached) { this.researchMetrics.cacheHits++; return cached; } // Execute parallel web searches with rate limiting const queries = this.generateSearchQueries(task.description); const searchPromises = queries.map(query => this.executeRateLimitedSearch(query, agent) ); const results = await Promise.allSettled(searchPromises); const successfulResults = results .filter(r => r.status === 'fulfilled') .map(r => (r as PromiseFulfilledResult<any>).value) .flat(); // Rank and filter results by credibility const rankedResults = await this.rankResultsByCredibility(successfulResults); // Cache results this.setCache(cacheKey, rankedResults, 3600000); // 1 hour TTL this.researchMetrics.cacheMisses++; return { results: rankedResults, totalFound: successfulResults.length, queriesExecuted: queries.length, credibilityScores: rankedResults.map(r => r.credibilityScore) }; } private async executeOptimizedDataExtraction(task: TaskDefinition, agent: any): Promise<any> { this.logger.info('Executing optimized data extraction', { taskId: task.id.id }); // Get connection from pool const connection = await this.getPooledConnection(); try { // Parallel data extraction with deduplication const extractionPromises = this.createParallelExtractionTasks(task, agent); const extractedData = await Promise.all(extractionPromises); // Deduplicate results const deduplicatedData = this.deduplicateResults(extractedData.flat()); return { extractedData: deduplicatedData, totalExtracted: extractedData.flat().length, uniqueResults: deduplicatedData.length, deduplicationRate: 1 - (deduplicatedData.length / extractedData.flat().length) }; } finally { this.releasePooledConnection(connection); } } private async executeOptimizedClustering(task: TaskDefinition, agent: any): Promise<any> { this.logger.info('Executing optimized semantic clustering', { taskId: task.id.id }); // Implement semantic clustering with caching const data = task.input?.extractedData || []; const cacheKey = this.generateCacheKey('clustering', JSON.stringify(data)); const cached = this.getFromCache(cacheKey); if (cached) { return cached; } // Perform semantic clustering const clusters = await this.performSemanticClustering(data); // Cache clustering results this.setCache(cacheKey, clusters, 7200000); // 2 hours TTL return { clusters, clusterCount: clusters.length, averageClusterSize: clusters.reduce((sum, c) => sum + c.results.length, 0) / clusters.length, coherenceScore: clusters.reduce((sum, c) => sum + c.coherenceScore, 0) / clusters.length }; } private async executeGenericResearchTask(task: TaskDefinition, agent: any): Promise<any> { this.logger.info('Executing generic research task', { taskId: task.id.id }); // Apply general research optimizations return { status: 'completed', optimizations: ['caching', 'rate-limiting', 'connection-pooling'], executionTime: Date.now() }; } // Helper methods for research optimizations private extractResearchParameters(description: string): any { return { domains: this.extractDomains(description), keywords: this.extractKeywords(description), timeframe: this.extractTimeframe(description), sourceTypes: this.extractSourceTypes(description) }; } private extractDomains(description: string): string[] { // Extract relevant domains from description const domains = []; if (description.includes('academic') || description.includes('research')) domains.push('academic'); if (description.includes('news') || description.includes('current')) domains.push('news'); if (description.includes('technical') || description.includes('documentation')) domains.push('technical'); return domains.length > 0 ? domains : ['general']; } private extractKeywords(description: string): string[] { // Simple keyword extraction - in production, use NLP return description .toLowerCase() .split(/\s+/) .filter(word => word.length > 3) .slice(0, 10); } private extractTimeframe(description: string): any { // Extract time-related constraints const now = new Date(); return { start: new Date(now.getTime() - 365 * 24 * 60 * 60 * 1000), // 1 year ago end: now }; } private extractSourceTypes(description: string): string[] { return ['academic', 'news', 'documentation', 'blog']; } private generateSearchQueries(description: string): ResearchQuery[] { const baseQuery = description.substring(0, 100); const keywords = this.extractKeywords(description); return [ { id: generateId('query'), query: baseQuery, keywords: keywords.slice(0, 5), domains: ['general'], priority: 1, timestamp: new Date() }, { id: generateId('query'), query: `${baseQuery} research study`, keywords: [...keywords.slice(0, 3), 'research', 'study'], domains: ['academic'], priority: 2, timestamp: new Date() }, { id: generateId('query'), query: `${baseQuery} best practices`, keywords: [...keywords.slice(0, 3), 'best', 'practices'], domains: ['technical'], priority: 2, timestamp: new Date() } ]; } private async executeRateLimitedSearch(query: ResearchQuery, agent: any): Promise<ResearchResult[]> { const domain = query.domains[0] || 'general'; // Check rate limits if (!this.checkRateLimit(domain)) { await this.waitForRateLimit(domain); } // Simulate web search with retry logic let attempts = 0; const maxAttempts = 3; while (attempts < maxAttempts) { try { // Simulate search execution const results = await this.simulateWebSearch(query); this.updateRateLimit(domain); return results; } catch (error) { attempts++; if (attempts >= maxAttempts) throw error; await this.exponentialBackoff(attempts); } } return []; } private async simulateWebSearch(query: ResearchQuery): Promise<ResearchResult[]> { // Simulate web search results const resultCount = Math.floor(Math.random() * 10) + 5; const results: ResearchResult[] = []; for (let i = 0; i < resultCount; i++) { results.push({ id: generateId('result'), queryId: query.id, url: `https://example.com/result-${i}`, title: `Research Result ${i} for ${query.query}`, content: `Content for ${query.query} - result ${i}`, summary: `Summary of result ${i}`, credibilityScore: Math.random() * 0.4 + 0.6, // 0.6-1.0 relevanceScore: Math.random() * 0.3 + 0.7, // 0.7-1.0 sourceType: query.domains[0] || 'general', extractedAt: new Date(), metadata: { queryKeywords: query.keywords } }); } return results; } private async rankResultsByCredibility(results: ResearchResult[]): Promise<ResearchResult[]> { // Sort by combined credibility and relevance score return results.sort((a, b) => { const scoreA = (a.credibilityScore * 0.6) + (a.relevanceScore * 0.4); const scoreB = (b.credibilityScore * 0.6) + (b.relevanceScore * 0.4); return scoreB - scoreA; }); } private createParallelExtractionTasks(task: TaskDefinition, agent: any): Promise<any>[] { // Create parallel extraction tasks const results = task.input?.results || []; const batchSize = Math.ceil(results.length / this.connectionPool.max); const batches = []; for (let i = 0; i < results.length; i += batchSize) { const batch = results.slice(i, i + batchSize); batches.push(this.extractDataFromBatch(batch)); } return batches; } private async extractDataFromBatch(batch: ResearchResult[]): Promise<any[]> { // Simulate parallel data extraction return batch.map(result => ({ id: result.id, extractedData: `Extracted data from ${result.title}`, insights: [`Insight 1 from ${result.title}`, `Insight 2 from ${result.title}`], metadata: result.metadata })); } private deduplicateResults(results: any[]): any[] { const seen = new Set(); return results.filter(result => { const key = result.extractedData || result.id; if (seen.has(key)) return false; seen.add(key); return true; }); } private async performSemanticClustering(data: any[]): Promise<ResearchCluster[]> { // Simulate semantic clustering const clusterCount = Math.min(Math.ceil(data.length / 5), 10); const clusters: ResearchCluster[] = []; for (let i = 0; i < clusterCount; i++) { const clusterData = data.slice(i * 5, (i + 1) * 5); clusters.push({ id: generateId('cluster'), topic: `Research Topic ${i + 1}`, results: clusterData, centroid: Array(10).fill(0).map(() => Math.random()), coherenceScore: Math.random() * 0.3 + 0.7, keywords: [`keyword${i}1`, `keyword${i}2`], summary: `Summary of cluster ${i + 1}` }); } return clusters; } // Connection pooling methods private async getPooledConnection(): Promise<any> { if (this.connectionPool.active >= this.connectionPool.max) { await this.waitForConnection(); } this.connectionPool.active++; return { id: generateId('connection'), timestamp: new Date() }; } private releasePooledConnection(connection: any): void { this.connectionPool.active--; this.connectionPool.idle++; } private async waitForConnection(): Promise<void> { return new Promise(resolve => { const checkConnection = () => { if (this.connectionPool.active < this.connectionPool.max) { resolve(); } else { setTimeout(checkConnection, 100); } }; checkConnection(); }); } // Rate limiting methods private checkRateLimit(domain: string): boolean { const limiter = this.rateLimiters.get(domain); if (!limiter) { this.rateLimiters.set(domain, { requests: 0, windowStart: new Date(), windowSize: 60000, // 1 minute maxRequests: 10, backoffMultiplier: 1 }); return true; } const now = new Date(); if (now.getTime() - limiter.windowStart.getTime() > limiter.windowSize) { limiter.requests = 0; limiter.windowStart = now; } return limiter.requests < limiter.maxRequests; } private updateRateLimit(domain: string): void { const limiter = this.rateLimiters.get(domain); if (limiter) { limiter.requests++; } } private async waitForRateLimit(domain: string): Promise<void> { const limiter = this.rateLimiters.get(domain); if (!limiter) return; const waitTime = limiter.windowSize * limiter.backoffMultiplier; await new Promise(resolve => setTimeout(resolve, waitTime)); } private async exponentialBackoff(attempt: number): Promise<void> { const delay = Math.pow(2, attempt) * 1000; await new Promise(resolve => setTimeout(resolve, delay)); } // Caching methods private generateCacheKey(type: string, data: string): string { return `${type}:${Buffer.from(data).toString('base64').substring(0, 32)}`; } private getFromCache(key: string): any | null { const entry = this.researchCache.get(key); if (!entry) return null; const now = new Date(); if (now.getTime() - entry.timestamp.getTime() > entry.ttl) { this.researchCache.delete(key); return null; } entry.accessCount++; entry.lastAccessed = now; return entry.data; } private setCache(key: string, data: any, ttl: number): void { this.researchCache.set(key, { key, data, timestamp: new Date(), ttl, accessCount: 0, lastAccessed: new Date() }); // Cleanup old entries if cache is too large if (this.researchCache.size > 1000) { this.cleanupCache(); } } private cleanupCache(): void { const entries = Array.from(this.researchCache.entries()); entries.sort((a, b) => a[1].lastAccessed.getTime() - b[1].lastAccessed.getTime()); // Remove oldest 20% of entries const toRemove = Math.floor(entries.length * 0.2); for (let i = 0; i < toRemove; i++) { this.researchCache.delete(entries[i][0]); } } private createResearchTask( id: string, type: TaskType, name: string, instructions: string, options: any = {} ): TaskDefinition { const taskId: TaskId = { id: generateId('task'), swarmId: 'research-swarm', sequence: 1, priority: 1 }; return { id: taskId, type, name, description: instructions, instructions, requirements: { capabilities: options.requiredCapabilities || ['research'], tools: ['WebFetchTool', 'WebSearch'], permissions: ['read', 'write'] }, constraints: { dependencies: options.dependencies || [], dependents: [], conflicts: [], maxRetries: 3, timeoutAfter: options.estimatedDuration || 300000 }, priority: options.priority || 'medium', input: options.researchParams || {}, context: {}, examples: [], status: 'created', createdAt: new Date(), updatedAt: new Date(), attempts: [], statusHistory: [{ timestamp: new Date(), from: 'created', to: 'created', reason: 'Task created', triggeredBy: 'system' }] }; } private updateResearchMetrics(taskType: string, duration: number): void { this.researchMetrics.queriesExecuted++; this.researchMetrics.averageResponseTime = (this.researchMetrics.averageResponseTime + duration) / 2; } private createTaskBatches(tasks: TaskDefinition[], dependencies: Map<string, string[]>): any[] { const batches: any[] = []; const processed = new Set<string>(); let batchIndex = 0; while (processed.size < tasks.length) { const batchTasks = tasks.filter(task => !processed.has(task.id.id) && task.constraints.dependencies.every(dep => processed.has(dep.id)) ); if (batchTasks.length === 0) break; // Prevent infinite loop const batch = { id: `research-batch-${batchIndex++}`, tasks: batchTasks, canRunInParallel: batchTasks.length > 1, estimatedDuration: Math.max(...batchTasks.map(t => t.constraints.timeoutAfter || 0)), requiredResources: { agents: batchTasks.length, memory: batchTasks.length * 512, // MB cpu: batchTasks.length * 0.5 // CPU cores } }; batches.push(batch); batchTasks.forEach(task => processed.add(task.id.id)); } return batches; } // Public API for metrics override getMetrics() { const credibilityScoresRecord: Record<string, number> = {}; this.researchMetrics.credibilityScores.forEach((score, index) => { credibilityScoresRecord[`result_${index}`] = score; }); return { ...this.metrics, queriesExecuted: this.researchMetrics.queriesExecuted, averageResponseTime: this.researchMetrics.averageResponseTime, cacheHits: this.researchMetrics.cacheHits, cacheMisses: this.researchMetrics.cacheMisses, credibilityScores: credibilityScoresRecord, cacheHitRate: this.researchMetrics.cacheHits / (this.researchMetrics.cacheHits + this.researchMetrics.cacheMisses || 1), averageCredibilityScore: this.researchMetrics.credibilityScores.length > 0 ? this.researchMetrics.credibilityScores.reduce((a, b) => a + b, 0) / this.researchMetrics.credibilityScores.length : 0, connectionPoolUtilization: this.connectionPool.active / this.connectionPool.max, cacheSize: this.researchCache.size }; } // Progressive refinement methods async refineResearchScope(objective: SwarmObjective, intermediateResults: any[]): Promise<SwarmObjective> { this.logger.info('Refining research scope based on intermediate results', { objectiveId: objective.id, resultsCount: intermediateResults.length }); // Analyze intermediate results to refine scope const refinedObjective = { ...objective }; // Update requirements based on findings if (intermediateResults.length > 0) { const avgCredibility = intermediateResults .map(r => r.credibilityScore || 0.5) .reduce((a, b) => a + b, 0) / intermediateResults.length; if (avgCredibility < 0.7) { refinedObjective.requirements.qualityThreshold = Math.max( refinedObjective.requirements.qualityThreshold, 0.8 ); } } return refinedObjective; } // Implementation of abstract methods from BaseStrategy async selectAgentForTask(task: TaskDefinition, availableAgents: any[]): Promise<string | null> { if (availableAgents.length === 0) return null; // Research-specific agent selection logic let bestAgent = null; let bestScore = 0; for (const agent of availableAgents) { let score = 0; // Check for research capabilities if (agent.capabilities?.research) score += 0.4; if (agent.capabilities?.webSearch) score += 0.3; if (agent.capabilities?.analysis) score += 0.2; // Check for specific research task types if (task.type === 'research' && agent.type === 'researcher') score += 0.3; if (task.type === 'analysis' && agent.type === 'analyst') score += 0.3; if (task.type === 'research' && agent.capabilities?.webSearch) score += 0.4; // Consider current workload score *= (1 - (agent.workload || 0)); if (score > bestScore) { bestScore = score; bestAgent = agent; } } return bestAgent?.id?.id || null; } async optimizeTaskSchedule(tasks: TaskDefinition[], agents: any[]): Promise<any[]> { const allocations: any[] = []; // Group tasks by type for optimal allocation const researchTasks = tasks.filter(t => t.type === 'research'); const analysisTasks = tasks.filter(t => t.type === 'analysis'); const otherTasks = tasks.filter(t => !['research', 'analysis'].includes(t.type as string)); for (const agent of agents) { const allocation = { agentId: agent.id?.id || agent.id, tasks: [] as string[], estimatedWorkload: 0, capabilities: this.getAgentCapabilitiesList(agent) }; // Allocate tasks based on agent capabilities if (agent.type === 'researcher' && researchTasks.length > 0) { const task = researchTasks.shift(); if (task) { allocation.tasks.push(task.id.id); allocation.estimatedWorkload += 0.3; } } if (agent.type === 'analyst' && analysisTasks.length > 0) { const task = analysisTasks.shift(); if (task) { allocation.tasks.push(task.id.id); allocation.estimatedWorkload += 0.3; } } // Web search tasks are handled as research tasks // Allocate remaining tasks if (allocation.tasks.length === 0 && otherTasks.length > 0) { const task = otherTasks.shift(); if (task) { allocation.tasks.push(task.id.id); allocation.estimatedWorkload += 0.2; } } if (allocation.tasks.length > 0) { allocations.push(allocation); } } return allocations; } private getAgentCapabilitiesList(agent: any): string[] { const caps: string[] = []; if (agent.capabilities) { if (agent.capabilities.research) caps.push('research'); if (agent.capabilities.webSearch) caps.push('web-search'); if (agent.capabilities.analysis) caps.push('analysis'); if (agent.capabilities.codeGeneration) caps.push('code-generation'); if (agent.capabilities.documentation) caps.push('documentation'); } return caps; } }