UNPKG

claude-flow

Version:

Ruflo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration

865 lines (761 loc) 25.5 kB
/** * SpecializedWorker - Domain-Specialized Worker Implementation * * Extends WorkerBase with domain-specific capabilities and * intelligent task matching using embedding-based similarity. * * Features: * - Domain specialization with configurable focus areas * - Embedding-based task matching for intelligent routing * - Capability verification and scoring * - Domain-specific execution strategies * * Compatible with agentic-flow's SpecializedAgent pattern. * * @module v3/integration/specialized-worker * @version 3.0.0-alpha.1 */ import { WorkerBase, WorkerConfig, WorkerType, AgentOutput, WorkerArtifact, } from './worker-base.js'; import type { Task } from './agentic-flow-agent.js'; /** * Domain specialization types */ export type DomainSpecialization = | 'frontend' | 'backend' | 'database' | 'devops' | 'security' | 'performance' | 'testing' | 'documentation' | 'architecture' | 'machine-learning' | 'data-engineering' | 'mobile' | 'infrastructure' | 'api-design' | 'code-review' | 'custom'; /** * Specialized worker configuration */ export interface SpecializedWorkerConfig extends WorkerConfig { /** Primary domain specialization */ domain: DomainSpecialization; /** Secondary domains (ordered by proficiency) */ secondaryDomains?: DomainSpecialization[]; /** Domain-specific skills with proficiency levels (0.0-1.0) */ skills?: Map<string, number> | Record<string, number>; /** Preferred programming languages */ languages?: string[]; /** Preferred frameworks */ frameworks?: string[]; /** Preferred tools */ tools?: string[]; /** Domain expertise level (0.0-1.0) */ expertiseLevel?: number; /** Enable domain-specific preprocessing */ enablePreprocessing?: boolean; /** Enable domain-specific postprocessing */ enablePostprocessing?: boolean; /** Custom domain handlers */ handlers?: DomainHandlers; } /** * Domain-specific handlers for specialized processing */ export interface DomainHandlers { /** Preprocess task before execution */ preprocess?: (task: Task, worker: SpecializedWorker) => Promise<Task>; /** Postprocess output after execution */ postprocess?: (output: AgentOutput, task: Task, worker: SpecializedWorker) => Promise<AgentOutput>; /** Validate task for domain compatibility */ validate?: (task: Task, worker: SpecializedWorker) => Promise<boolean>; /** Generate domain-specific artifacts */ generateArtifacts?: (output: AgentOutput, task: Task, worker: SpecializedWorker) => Promise<WorkerArtifact[]>; } /** * Task matching result with detailed scoring */ export interface TaskMatchResult { /** Overall match score (0.0-1.0) */ score: number; /** Breakdown of scoring components */ breakdown: { /** Capability match score */ capabilityScore: number; /** Domain match score */ domainScore: number; /** Embedding similarity score */ embeddingScore: number; /** Skill match score */ skillScore: number; }; /** Whether worker meets minimum requirements */ meetsRequirements: boolean; /** Missing capabilities */ missingCapabilities: string[]; /** Matched capabilities */ matchedCapabilities: string[]; /** Recommendations for better matching */ recommendations?: string[]; } /** * Domain embedding configurations */ const DOMAIN_EMBEDDINGS: Record<DomainSpecialization, number[]> = { frontend: [1, 0, 0, 0, 0, 0.2, 0.3, 0.5, 0.2, 0, 0, 0.4, 0, 0.3, 0.2, 0], backend: [0, 1, 0.3, 0, 0, 0.3, 0.2, 0.3, 0.5, 0, 0.3, 0, 0.2, 0.5, 0.2, 0], database: [0, 0.3, 1, 0, 0, 0.4, 0.2, 0.2, 0.3, 0, 0.5, 0, 0.3, 0.4, 0.1, 0], devops: [0, 0.2, 0.2, 1, 0.3, 0.3, 0.2, 0.4, 0.3, 0, 0.2, 0, 0.8, 0.2, 0.1, 0], security: [0, 0.3, 0.3, 0.4, 1, 0.4, 0.5, 0.3, 0.3, 0, 0, 0, 0.3, 0.4, 0.6, 0], performance: [0.3, 0.4, 0.4, 0.3, 0.2, 1, 0.3, 0.2, 0.3, 0, 0, 0, 0.2, 0.2, 0.2, 0], testing: [0.3, 0.3, 0.2, 0.3, 0.4, 0.3, 1, 0.4, 0.2, 0, 0, 0.3, 0.2, 0.2, 0.3, 0], documentation: [0.4, 0.3, 0.2, 0.2, 0.2, 0.1, 0.3, 1, 0.3, 0, 0, 0.3, 0.1, 0.5, 0.2, 0], architecture: [0.3, 0.4, 0.4, 0.4, 0.4, 0.4, 0.3, 0.5, 1, 0.2, 0.3, 0.2, 0.4, 0.6, 0.4, 0], 'machine-learning': [0.2, 0.3, 0.3, 0.2, 0.2, 0.5, 0.3, 0.3, 0.3, 1, 0.6, 0.2, 0.3, 0.3, 0.2, 0], 'data-engineering': [0, 0.3, 0.6, 0.3, 0.2, 0.4, 0.2, 0.2, 0.4, 0.5, 1, 0, 0.4, 0.4, 0.2, 0], mobile: [0.5, 0.3, 0.2, 0.3, 0.3, 0.4, 0.4, 0.3, 0.3, 0.2, 0, 1, 0.2, 0.3, 0.2, 0], infrastructure: [0, 0.2, 0.3, 0.7, 0.4, 0.3, 0.2, 0.3, 0.5, 0, 0.3, 0, 1, 0.2, 0.3, 0], 'api-design': [0.2, 0.6, 0.3, 0.2, 0.3, 0.3, 0.2, 0.6, 0.5, 0, 0.2, 0.2, 0.2, 1, 0.3, 0], 'code-review': [0.4, 0.4, 0.3, 0.3, 0.5, 0.4, 0.5, 0.4, 0.4, 0.2, 0.2, 0.3, 0.2, 0.4, 1, 0], custom: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], }; /** * SpecializedWorker - Domain-focused worker with intelligent matching * * Usage: * ```typescript * const worker = new SpecializedWorker({ * id: 'frontend-1', * type: 'specialized', * domain: 'frontend', * capabilities: ['react', 'typescript', 'css'], * skills: { react: 0.9, typescript: 0.85, css: 0.8 }, * languages: ['typescript', 'javascript'], * frameworks: ['react', 'next.js'], * }); * * await worker.initialize(); * * // Match a task * const match = worker.matchTask(task); * if (match.score > 0.7) { * const result = await worker.execute(task); * } * ``` */ export class SpecializedWorker extends WorkerBase { /** Primary domain specialization */ readonly domain: DomainSpecialization; /** Secondary domains */ readonly secondaryDomains: DomainSpecialization[]; /** Domain-specific skills with proficiency levels */ protected skills: Map<string, number>; /** Preferred programming languages */ protected languages: string[]; /** Preferred frameworks */ protected frameworks: string[]; /** Preferred tools */ protected tools: string[]; /** Domain expertise level */ protected expertiseLevel: number; /** Domain-specific handlers */ protected handlers: DomainHandlers; /** Enable preprocessing */ protected enablePreprocessing: boolean; /** Enable postprocessing */ protected enablePostprocessing: boolean; /** * Create a new SpecializedWorker instance * * @param config - Specialized worker configuration */ constructor(config: SpecializedWorkerConfig) { // Extend type to specialized if not already set const baseConfig: WorkerConfig = { ...config, type: config.type || 'specialized', }; super(baseConfig); this.domain = config.domain; this.secondaryDomains = config.secondaryDomains || []; this.languages = config.languages || []; this.frameworks = config.frameworks || []; this.tools = config.tools || []; this.expertiseLevel = config.expertiseLevel ?? 0.8; this.enablePreprocessing = config.enablePreprocessing ?? true; this.enablePostprocessing = config.enablePostprocessing ?? true; this.handlers = config.handlers || {}; // Convert skills to Map if (config.skills instanceof Map) { this.skills = config.skills; } else if (config.skills) { this.skills = new Map(Object.entries(config.skills)); } else { this.skills = new Map(); } // Generate domain-specific embedding this.specialization = this.generateDomainEmbedding(); this.emit('specialized-worker-created', { workerId: this.id, domain: this.domain, expertiseLevel: this.expertiseLevel, }); } /** * Match a task to this worker * * Calculates a comprehensive match score based on: * - Capability overlap * - Domain compatibility * - Embedding similarity * - Skill proficiency * * @param task - Task to match * @returns Detailed match result with scores */ matchTask(task: Task): TaskMatchResult { const requiredCapabilities = this.extractRequiredCapabilities(task); const taskDomain = this.inferTaskDomain(task); const taskEmbedding = this.generateTaskEmbedding(task); // Calculate capability score const { matched, missing, score: capabilityScore } = this.calculateCapabilityMatch(requiredCapabilities); // Calculate domain score const domainScore = this.calculateDomainMatch(taskDomain); // Calculate embedding similarity const embeddingScore = this.calculateSimilarity(taskEmbedding); // Calculate skill match score const skillScore = this.calculateSkillMatch(task); // Weighted overall score const overallScore = capabilityScore * 0.3 + domainScore * 0.25 + embeddingScore * 0.25 + skillScore * 0.2; // Generate recommendations const recommendations = this.generateRecommendations( missing, taskDomain, overallScore ); return { score: overallScore, breakdown: { capabilityScore, domainScore, embeddingScore, skillScore, }, meetsRequirements: capabilityScore >= 0.5 && missing.length === 0, missingCapabilities: missing, matchedCapabilities: matched, recommendations, }; } /** * Execute a task with domain-specific processing * * @param task - Task to execute * @returns Agent output with results */ async execute(task: Task): Promise<AgentOutput> { const startTime = Date.now(); let processedTask = task; let artifacts: WorkerArtifact[] = []; try { // Validate task for domain compatibility if (this.handlers.validate) { const isValid = await this.handlers.validate(task, this); if (!isValid) { return { content: { error: 'Task validation failed for domain' }, success: false, error: new Error(`Task not compatible with ${this.domain} domain`), duration: Date.now() - startTime, }; } } // Preprocess task if (this.enablePreprocessing && this.handlers.preprocess) { processedTask = await this.handlers.preprocess(task, this); } // Execute the core task const output = await this.executeCore(processedTask); // Generate domain-specific artifacts if (this.handlers.generateArtifacts) { artifacts = await this.handlers.generateArtifacts(output, processedTask, this); } // Postprocess output let finalOutput = output; if (this.enablePostprocessing && this.handlers.postprocess) { finalOutput = await this.handlers.postprocess(output, processedTask, this); } // Add artifacts to output if (artifacts.length > 0) { finalOutput.artifacts = [...(finalOutput.artifacts || []), ...artifacts]; } return { ...finalOutput, duration: Date.now() - startTime, metadata: { ...finalOutput.metadata, domain: this.domain, expertiseLevel: this.expertiseLevel, workerId: this.id, }, }; } catch (error) { return { content: { error: (error as Error).message }, success: false, error: error as Error, duration: Date.now() - startTime, metadata: { domain: this.domain, workerId: this.id, }, }; } } /** * Core task execution logic * * Override this in subclasses for domain-specific implementations. * * @param task - Preprocessed task * @returns Execution output */ protected async executeCore(task: Task): Promise<AgentOutput> { // Default implementation with domain-aware processing const content = await this.processTaskForDomain(task); return { content, success: true, duration: 0, // Will be set by execute() metadata: { processedBy: this.id, domain: this.domain, }, }; } /** * Process task with domain-specific logic * * @param task - Task to process * @returns Processed content */ protected async processTaskForDomain(task: Task): Promise<Record<string, unknown>> { // Domain-specific processing logic const result: Record<string, unknown> = { taskId: task.id, domain: this.domain, processed: true, input: task.input, }; // Add domain-specific processing switch (this.domain) { case 'frontend': result.components = []; result.styles = {}; break; case 'backend': result.endpoints = []; result.services = []; break; case 'database': result.queries = []; result.migrations = []; break; case 'testing': result.testCases = []; result.coverage = 0; break; case 'security': result.vulnerabilities = []; result.recommendations = []; break; case 'architecture': result.diagrams = []; result.decisions = []; break; default: result.output = task.description; } return result; } /** * Get worker's domain expertise */ getDomainExpertise(): { primary: DomainSpecialization; secondary: DomainSpecialization[]; expertiseLevel: number; skills: Record<string, number>; } { return { primary: this.domain, secondary: this.secondaryDomains, expertiseLevel: this.expertiseLevel, skills: Object.fromEntries(this.skills), }; } /** * Update skill proficiency * * @param skill - Skill name * @param level - Proficiency level (0.0-1.0) */ updateSkill(skill: string, level: number): void { const clampedLevel = Math.max(0, Math.min(1, level)); this.skills.set(skill, clampedLevel); // Regenerate embedding with updated skills this.specialization = this.generateDomainEmbedding(); this.emit('skill-updated', { workerId: this.id, skill, level: clampedLevel, }); } // ===== Private Methods ===== /** * Generate domain-specific embedding */ private generateDomainEmbedding(): Float32Array { const baseDimension = 64; const embedding = new Float32Array(baseDimension); // Start with domain embedding const domainVec = DOMAIN_EMBEDDINGS[this.domain] || DOMAIN_EMBEDDINGS.custom; for (let i = 0; i < domainVec.length && i < baseDimension; i++) { embedding[i] = domainVec[i] * this.expertiseLevel; } // Add secondary domain influence for (let j = 0; j < this.secondaryDomains.length; j++) { const secondaryVec = DOMAIN_EMBEDDINGS[this.secondaryDomains[j]]; const weight = 0.5 / (j + 1); // Diminishing weight for (let i = 0; i < secondaryVec.length && i < baseDimension; i++) { embedding[i] += secondaryVec[i] * weight * this.expertiseLevel; } } // Add skill influence let skillIdx = 16; for (const [skill, level] of Array.from(this.skills.entries())) { if (skillIdx < baseDimension) { embedding[skillIdx] = level; skillIdx++; } } // Normalize const norm = Math.sqrt(embedding.reduce((sum, v) => sum + v * v, 0)); if (norm > 0) { for (let i = 0; i < baseDimension; i++) { embedding[i] /= norm; } } return embedding; } /** * Extract required capabilities from a task */ private extractRequiredCapabilities(task: Task): string[] { const capabilities: string[] = []; // From metadata if (task.metadata?.requiredCapabilities) { capabilities.push(...(task.metadata.requiredCapabilities as string[])); } // Infer from task type const typeCapabilities = this.inferCapabilitiesFromType(task.type); capabilities.push(...typeCapabilities); // Infer from description keywords const descriptionCapabilities = this.inferCapabilitiesFromDescription(task.description); capabilities.push(...descriptionCapabilities); // Deduplicate return Array.from(new Set(capabilities)); } /** * Infer capabilities from task type */ private inferCapabilitiesFromType(type: string): string[] { const typeMap: Record<string, string[]> = { code: ['code-generation'], review: ['code-review'], test: ['testing'], fix: ['debugging', 'code-generation'], refactor: ['refactoring', 'code-review'], document: ['documentation'], design: ['architecture'], security: ['security-audit'], performance: ['performance-analysis'], }; return typeMap[type.toLowerCase()] || []; } /** * Infer capabilities from task description */ private inferCapabilitiesFromDescription(description: string): string[] { const capabilities: string[] = []; const lowerDesc = description.toLowerCase(); const keywordMap: Record<string, string> = { react: 'react', vue: 'vue', angular: 'angular', typescript: 'typescript', javascript: 'javascript', python: 'python', api: 'api-design', database: 'database', sql: 'sql', test: 'testing', security: 'security', performance: 'performance', docker: 'docker', kubernetes: 'kubernetes', aws: 'aws', graphql: 'graphql', rest: 'rest-api', }; for (const [keyword, capability] of Object.entries(keywordMap)) { if (lowerDesc.includes(keyword)) { capabilities.push(capability); } } return capabilities; } /** * Infer domain from task */ private inferTaskDomain(task: Task): DomainSpecialization { const lowerDesc = task.description.toLowerCase(); const lowerType = task.type.toLowerCase(); const domainKeywords: Record<DomainSpecialization, string[]> = { frontend: ['ui', 'component', 'react', 'vue', 'angular', 'css', 'html', 'frontend'], backend: ['api', 'server', 'endpoint', 'service', 'backend', 'rest', 'graphql'], database: ['database', 'sql', 'query', 'migration', 'schema', 'postgres', 'mysql'], devops: ['deploy', 'ci', 'cd', 'pipeline', 'docker', 'kubernetes', 'terraform'], security: ['security', 'auth', 'vulnerability', 'penetration', 'encryption'], performance: ['performance', 'optimize', 'benchmark', 'profiling', 'speed'], testing: ['test', 'spec', 'coverage', 'unit', 'integration', 'e2e'], documentation: ['document', 'readme', 'api-doc', 'comment', 'guide'], architecture: ['architecture', 'design', 'pattern', 'structure', 'diagram'], 'machine-learning': ['ml', 'model', 'training', 'neural', 'ai', 'tensorflow', 'pytorch'], 'data-engineering': ['etl', 'pipeline', 'data', 'warehouse', 'spark', 'kafka'], mobile: ['mobile', 'ios', 'android', 'react-native', 'flutter', 'app'], infrastructure: ['infrastructure', 'cloud', 'aws', 'gcp', 'azure', 'network'], 'api-design': ['api', 'rest', 'graphql', 'endpoint', 'schema', 'openapi'], 'code-review': ['review', 'pr', 'pull-request', 'feedback', 'audit'], custom: [], }; for (const [domain, keywords] of Object.entries(domainKeywords)) { for (const keyword of keywords) { if (lowerDesc.includes(keyword) || lowerType.includes(keyword)) { return domain as DomainSpecialization; } } } return 'custom'; } /** * Generate embedding for a task */ private generateTaskEmbedding(task: Task): Float32Array { const dimension = 64; const embedding = new Float32Array(dimension); // Get domain embedding const taskDomain = this.inferTaskDomain(task); const domainVec = DOMAIN_EMBEDDINGS[taskDomain]; for (let i = 0; i < domainVec.length && i < dimension; i++) { embedding[i] = domainVec[i]; } // Add task type influence const typeHash = this.hashStringSpecialized(task.type); for (let i = 16; i < 32 && i < dimension; i++) { embedding[i] = ((typeHash >> (i % 32)) & 1) ? 0.5 : -0.5; } // Normalize const norm = Math.sqrt(embedding.reduce((sum, v) => sum + v * v, 0)); if (norm > 0) { for (let i = 0; i < dimension; i++) { embedding[i] /= norm; } } return embedding; } /** * Calculate capability match */ private calculateCapabilityMatch(required: string[]): { matched: string[]; missing: string[]; score: number; } { if (required.length === 0) { return { matched: [], missing: [], score: 1.0 }; } const matched = required.filter((cap) => this.capabilities.includes(cap)); const missing = required.filter((cap) => !this.capabilities.includes(cap)); const score = matched.length / required.length; return { matched, missing, score }; } /** * Calculate domain match score */ private calculateDomainMatch(taskDomain: DomainSpecialization): number { if (taskDomain === this.domain) { return 1.0; } if (this.secondaryDomains.includes(taskDomain)) { const index = this.secondaryDomains.indexOf(taskDomain); return 0.8 - index * 0.1; // Diminishing score for later secondary domains } // Check domain embedding similarity const taskEmbedding = DOMAIN_EMBEDDINGS[taskDomain]; const workerEmbedding = DOMAIN_EMBEDDINGS[this.domain]; let similarity = 0; for (let i = 0; i < taskEmbedding.length; i++) { similarity += taskEmbedding[i] * workerEmbedding[i]; } return Math.max(0, similarity * 0.5); } /** * Calculate skill match score */ private calculateSkillMatch(task: Task): number { const requiredSkills = this.extractSkillsFromTask(task); if (requiredSkills.length === 0) { return 1.0; } let totalScore = 0; let matchedCount = 0; for (const skill of requiredSkills) { if (this.skills.has(skill)) { totalScore += this.skills.get(skill)!; matchedCount++; } } if (matchedCount === 0) { return 0; } return totalScore / requiredSkills.length; } /** * Extract skills from task */ private extractSkillsFromTask(task: Task): string[] { const skills: string[] = []; const lowerDesc = task.description.toLowerCase(); // Check for language/framework mentions const allSkillsArray = [ ...this.languages, ...this.frameworks, ...this.tools, ...Array.from(this.skills.keys()), ]; for (const skill of allSkillsArray) { if (lowerDesc.includes(skill.toLowerCase())) { skills.push(skill); } } return Array.from(new Set(skills)); } /** * Generate recommendations for better matching */ private generateRecommendations( missing: string[], taskDomain: DomainSpecialization, score: number ): string[] { const recommendations: string[] = []; if (missing.length > 0) { recommendations.push(`Consider acquiring capabilities: ${missing.join(', ')}`); } if (taskDomain !== this.domain && !this.secondaryDomains.includes(taskDomain)) { recommendations.push(`Task domain '${taskDomain}' differs from specialization '${this.domain}'`); } if (score < 0.5) { recommendations.push('Low match score - consider routing to a more specialized worker'); } return recommendations; } /** * Simple string hash function for specialized worker */ protected hashStringSpecialized(str: string): number { let hash = 0; for (let i = 0; i < str.length; i++) { const char = str.charCodeAt(i); hash = ((hash << 5) - hash) + char; hash = hash & hash; } return hash; } } /** * Create a specialized worker factory * * @param domain - Primary domain specialization * @param config - Additional configuration * @returns Configured SpecializedWorker */ export function createSpecializedWorker( domain: DomainSpecialization, config: Partial<Omit<SpecializedWorkerConfig, 'domain'>> = {} ): SpecializedWorker { return new SpecializedWorker({ id: config.id || `${domain}-worker-${Date.now()}`, type: 'specialized', domain, capabilities: config.capabilities || [], ...config, }); } /** * Create a frontend specialized worker */ export function createFrontendWorker( config: Partial<Omit<SpecializedWorkerConfig, 'domain'>> = {} ): SpecializedWorker { return createSpecializedWorker('frontend', { capabilities: ['react', 'typescript', 'css', 'html', 'code-generation'], languages: ['typescript', 'javascript'], frameworks: ['react', 'next.js', 'vue'], skills: { react: 0.9, typescript: 0.85, css: 0.8, html: 0.9 }, ...config, }); } /** * Create a backend specialized worker */ export function createBackendWorker( config: Partial<Omit<SpecializedWorkerConfig, 'domain'>> = {} ): SpecializedWorker { return createSpecializedWorker('backend', { capabilities: ['api-design', 'database', 'typescript', 'code-generation'], languages: ['typescript', 'python', 'go'], frameworks: ['express', 'fastify', 'nest.js'], skills: { typescript: 0.9, 'api-design': 0.85, database: 0.8 }, ...config, }); } /** * Create a testing specialized worker */ export function createTestingWorker( config: Partial<Omit<SpecializedWorkerConfig, 'domain'>> = {} ): SpecializedWorker { return createSpecializedWorker('testing', { capabilities: ['testing', 'code-review', 'typescript'], languages: ['typescript', 'javascript'], frameworks: ['jest', 'vitest', 'playwright'], skills: { testing: 0.95, 'test-automation': 0.9, 'code-review': 0.8 }, ...config, }); }