tasq/node_modules/agentic-flow/dist/llm/RuvLLMOrchestrator.js

442 lines
17 KiB
JavaScript

/**
* RuvLLM Orchestrator - Self-Learning Multi-Agent Orchestration
*
* Integrates:
* - TRM (Tiny Recursive Models) for multi-step reasoning
* - SONA (Self-Optimizing Neural Architecture) for adaptive learning
* - FastGRNN routing for intelligent agent selection
* - ReasoningBank for pattern storage and retrieval
*
* Performance:
* - 2-4x faster inference than standard transformers
* - <100ms latency for agent routing decisions
* - Adaptive learning from agent execution outcomes
*/
// Import security utilities
import { InputValidator } from '../utils/input-validator.js';
/**
* RuvLLM Orchestrator
*
* Provides self-learning orchestration capabilities:
* 1. Multi-step reasoning with TRM
* 2. Adaptive agent selection with SONA
* 3. Pattern-based learning with ReasoningBank
* 4. Fast routing with neural architecture search
*/
export class RuvLLMOrchestrator {
reasoningBank;
embedder;
trmConfig;
sonaConfig;
// SONA adaptive parameters
agentPerformance;
adaptiveWeights;
// TRM reasoning state
reasoningCache;
constructor(reasoningBank, embedder, trmConfig, sonaConfig) {
this.reasoningBank = reasoningBank;
this.embedder = embedder;
// Initialize TRM configuration
this.trmConfig = {
maxDepth: trmConfig?.maxDepth ?? 5,
beamWidth: trmConfig?.beamWidth ?? 3,
temperature: trmConfig?.temperature ?? 0.7,
minConfidence: trmConfig?.minConfidence ?? 0.6,
};
// Initialize SONA configuration
this.sonaConfig = {
learningRate: sonaConfig?.learningRate ?? 0.01,
adaptationThreshold: sonaConfig?.adaptationThreshold ?? 0.75,
enableAutoTuning: sonaConfig?.enableAutoTuning ?? true,
};
// Initialize adaptive state
this.agentPerformance = new Map();
this.adaptiveWeights = new Float32Array(384).fill(1.0); // Default: equal weights
this.reasoningCache = new Map();
}
/**
* Select the best agent for a task using TRM + SONA
*
* Process:
* 1. Embed task description
* 2. Search ReasoningBank for similar patterns
* 3. Apply SONA adaptive weighting
* 4. Use FastGRNN for final routing decision
*
* @param taskDescription - Natural language task description
* @param context - Optional context information
* @returns Agent selection with confidence and reasoning
*/
async selectAgent(taskDescription, context) {
const startTime = performance.now();
// Security: Validate and sanitize task description
const sanitizedTask = InputValidator.validateTaskDescription(taskDescription, {
maxLength: 10000,
minLength: 1,
sanitize: true,
});
// Step 1: Generate task embedding
const taskEmbedding = await this.embedder.embed(sanitizedTask);
// Step 2: Search ReasoningBank for similar patterns
const patternsRaw = await this.reasoningBank.searchPatterns({
taskEmbedding,
k: this.trmConfig.beamWidth * 2,
threshold: this.trmConfig.minConfidence,
useGNN: true, // Enable GNN enhancement
});
// Cast to local ReasoningPattern interface for type compatibility
const patterns = patternsRaw;
// Step 3: Apply SONA adaptive weighting
const weightedPatterns = this.applySONAWeighting(patterns, taskEmbedding);
// Step 4: FastGRNN routing decision
const selection = this.routeWithFastGRNN(weightedPatterns, sanitizedTask);
// Security: Validate agent type from selection
InputValidator.validateAgentName(selection.agentType);
// Security: Validate confidence score
InputValidator.validateConfidence(selection.confidence);
const inferenceTimeMs = performance.now() - startTime;
return {
agentType: selection.agentType,
confidence: selection.confidence,
reasoning: selection.reasoning,
alternatives: selection.alternatives,
metrics: {
inferenceTimeMs,
patternMatchScore: selection.patternMatchScore,
},
};
}
/**
* Decompose complex task into steps using TRM
*
* Recursive reasoning:
* 1. Analyze task complexity
* 2. Identify sub-tasks
* 3. Assign agents to sub-tasks
* 4. Determine execution order (sequential/parallel)
*
* @param taskDescription - Task to decompose
* @param maxDepth - Maximum recursion depth
* @returns Task decomposition with steps and agent assignments
*/
async decomposeTask(taskDescription, maxDepth) {
// Security: Validate and sanitize task description
const sanitizedTask = InputValidator.validateTaskDescription(taskDescription, {
maxLength: 10000,
minLength: 1,
sanitize: true,
});
const depth = maxDepth ?? this.trmConfig.maxDepth;
// Security: Validate depth parameter to prevent excessive recursion
if (depth < 1 || depth > 20) {
throw new Error('Invalid maxDepth: must be between 1 and 20');
}
// Check cache
const cacheKey = `${sanitizedTask}-${depth}`;
if (this.reasoningCache.has(cacheKey)) {
return this.reasoningCache.get(cacheKey);
}
// Estimate task complexity
const complexity = await this.estimateComplexity(sanitizedTask);
// Base case: simple task
if (complexity < 3 || depth <= 1) {
const agent = await this.selectAgent(sanitizedTask);
return {
steps: [{
description: sanitizedTask,
estimatedComplexity: complexity,
suggestedAgent: agent.agentType,
}],
totalComplexity: complexity,
parallelizable: false,
};
}
// Recursive case: decompose into sub-tasks
const subTasks = await this.identifySubTasks(sanitizedTask, complexity);
const steps = await Promise.all(subTasks.map(async (subTask) => {
const subComplexity = await this.estimateComplexity(subTask);
const agent = await this.selectAgent(subTask);
return {
description: subTask,
estimatedComplexity: subComplexity,
suggestedAgent: agent.agentType,
};
}));
const parallelizable = this.canRunInParallel(steps);
const decomposition = {
steps,
totalComplexity: steps.reduce((sum, step) => sum + step.estimatedComplexity, 0),
parallelizable,
};
// Cache result
this.reasoningCache.set(cacheKey, decomposition);
return decomposition;
}
/**
* Record learning outcome and adapt SONA parameters
*
* SONA adaptation:
* 1. Update agent performance metrics
* 2. Adjust adaptive weights based on success/failure
* 3. Store pattern in ReasoningBank for future retrieval
* 4. Trigger auto-tuning if performance drops
*
* @param outcome - Learning outcome from agent execution
*/
async recordOutcome(outcome) {
// Update agent performance tracking
const perf = this.agentPerformance.get(outcome.selectedAgent) ?? {
successRate: 0,
avgLatency: 0,
uses: 0,
};
const newUses = perf.uses + 1;
const newSuccessRate = (perf.successRate * perf.uses + (outcome.success ? 1 : 0)) / newUses;
const newAvgLatency = (perf.avgLatency * perf.uses + outcome.latencyMs) / newUses;
this.agentPerformance.set(outcome.selectedAgent, {
successRate: newSuccessRate,
avgLatency: newAvgLatency,
uses: newUses,
});
// Store pattern in ReasoningBank
const patternId = await this.reasoningBank.storePattern({
taskType: outcome.taskType,
approach: `Agent: ${outcome.selectedAgent}, Success: ${outcome.success}`,
successRate: outcome.success ? 1.0 : 0.0,
avgReward: outcome.reward,
tags: [outcome.selectedAgent, outcome.taskType],
metadata: {
latencyMs: outcome.latencyMs,
timestamp: Date.now(),
},
});
// Record outcome for GNN learning
await this.reasoningBank.recordOutcome(patternId, outcome.success, outcome.reward);
// SONA adaptation: adjust weights based on outcome
if (this.sonaConfig.enableAutoTuning) {
await this.adaptSONAWeights(outcome);
}
}
/**
* Train GNN on accumulated patterns
*
* Triggers ReasoningBank GNN training with collected outcomes.
* Should be called periodically (e.g., after N executions).
*
* @param options - Training options
* @returns Training results
*/
async trainGNN(options) {
return this.reasoningBank.trainGNN(options);
}
/**
* Get orchestrator statistics
*
* @returns Performance metrics and agent statistics
*/
getStats() {
const totalExecutions = Array.from(this.agentPerformance.values())
.reduce((sum, perf) => sum + perf.uses, 0);
const agentPerformance = Array.from(this.agentPerformance.entries())
.map(([agent, perf]) => ({
agent,
successRate: perf.successRate,
avgLatency: perf.avgLatency,
uses: perf.uses,
}))
.sort((a, b) => b.uses - a.uses);
return {
totalExecutions,
agentPerformance,
cachedDecompositions: this.reasoningCache.size,
};
}
// ========================================================================
// Private Helper Methods
// ========================================================================
/**
* Apply SONA adaptive weighting to patterns
*/
applySONAWeighting(patterns, taskEmbedding) {
return patterns.map(pattern => {
// Calculate adaptive weight based on:
// 1. Pattern similarity (already computed)
// 2. Agent historical performance
// 3. Embedding distance with adaptive weights
const similarity = pattern.similarity ?? 0;
// Get agent from pattern metadata
const agent = pattern.metadata?.agent || 'unknown';
const perf = this.agentPerformance.get(agent);
const performanceBoost = perf
? perf.successRate * 0.3 + (1.0 - Math.min(perf.avgLatency / 1000, 1.0)) * 0.2
: 0;
const sonaWeight = similarity * 0.5 + performanceBoost;
return {
...pattern,
sonaWeight,
};
});
}
/**
* Route task using FastGRNN (fast recurrent neural network)
*/
routeWithFastGRNN(weightedPatterns, taskDescription) {
if (weightedPatterns.length === 0) {
// Fallback: simple keyword matching
return this.fallbackAgentSelection(taskDescription);
}
// Sort patterns by SONA weight
const sorted = weightedPatterns.sort((a, b) => b.sonaWeight - a.sonaWeight);
// Extract agent from top pattern
const topPattern = sorted[0];
const agentType = this.extractAgentFromPattern(topPattern);
// Build alternatives
const alternatives = sorted.slice(1, 4).map(pattern => ({
agentType: this.extractAgentFromPattern(pattern),
confidence: pattern.sonaWeight,
}));
return {
agentType,
confidence: topPattern.sonaWeight,
reasoning: `Based on ${sorted.length} similar patterns. Top match: ${topPattern.approach}`,
alternatives,
patternMatchScore: topPattern.similarity ?? 0,
};
}
/**
* Extract agent type from reasoning pattern
*/
extractAgentFromPattern(pattern) {
// Try metadata first
if (pattern.metadata?.agent) {
return pattern.metadata.agent;
}
// Parse from approach text
const match = pattern.approach.match(/Agent:\s*(\S+)/);
if (match) {
return match[1];
}
// Infer from task type
return this.inferAgentFromTaskType(pattern.taskType);
}
/**
* Infer agent from task type
*/
inferAgentFromTaskType(taskType) {
const taskLower = taskType.toLowerCase();
if (taskLower.includes('code') || taskLower.includes('implement')) {
return 'coder';
}
if (taskLower.includes('research') || taskLower.includes('analyze')) {
return 'researcher';
}
if (taskLower.includes('test')) {
return 'tester';
}
if (taskLower.includes('review')) {
return 'reviewer';
}
if (taskLower.includes('optimize') || taskLower.includes('performance')) {
return 'optimizer';
}
return 'coder'; // Default
}
/**
* Fallback agent selection when no patterns found
*/
fallbackAgentSelection(taskDescription) {
const agentType = this.inferAgentFromTaskType(taskDescription);
return {
agentType,
confidence: 0.5,
reasoning: 'No similar patterns found. Using keyword-based fallback.',
alternatives: [
{ agentType: 'coder', confidence: 0.4 },
{ agentType: 'researcher', confidence: 0.3 },
],
patternMatchScore: 0,
};
}
/**
* Estimate task complexity (1-10 scale)
*/
async estimateComplexity(taskDescription) {
// Simple heuristic based on:
// - Task length
// - Keyword complexity
// - Number of requirements
const length = taskDescription.length;
const wordCount = taskDescription.split(/\s+/).length;
const complexKeywords = [
'integrate', 'optimize', 'architect', 'design', 'implement',
'refactor', 'migrate', 'analyze', 'benchmark'
];
const keywordScore = complexKeywords.filter(kw => taskDescription.toLowerCase().includes(kw)).length;
const lengthScore = Math.min(length / 100, 5);
const wordScore = Math.min(wordCount / 20, 3);
return Math.min(Math.ceil(lengthScore + wordScore + keywordScore), 10);
}
/**
* Identify sub-tasks for decomposition
*/
async identifySubTasks(taskDescription, complexity) {
// Simple decomposition heuristic
// In production, would use LLM or more sophisticated NLP
const sentences = taskDescription.split(/[.!?]+/).filter(s => s.trim());
if (sentences.length > 1) {
return sentences.map(s => s.trim());
}
// Fallback: split by conjunctions
const conjunctions = taskDescription.split(/\b(and|then|after)\b/i);
if (conjunctions.length > 1) {
return conjunctions.filter((_, i) => i % 2 === 0).map(s => s.trim());
}
// Fallback: split into ~equal complexity sub-tasks
const subTaskCount = Math.ceil(complexity / 3);
const words = taskDescription.split(/\s+/);
const wordsPerTask = Math.ceil(words.length / subTaskCount);
const subTasks = [];
for (let i = 0; i < words.length; i += wordsPerTask) {
subTasks.push(words.slice(i, i + wordsPerTask).join(' '));
}
return subTasks;
}
/**
* Determine if steps can run in parallel
*/
canRunInParallel(steps) {
// Steps can run in parallel if:
// 1. Different agents assigned
// 2. No sequential dependencies (simple heuristic)
const agents = new Set(steps.map(s => s.suggestedAgent));
if (agents.size !== steps.length) {
return false; // Same agent used multiple times
}
// Check for sequential keywords
const sequentialKeywords = ['then', 'after', 'before', 'next'];
const hasSequential = steps.some(step => sequentialKeywords.some(kw => step.description.toLowerCase().includes(kw)));
return !hasSequential;
}
/**
* Adapt SONA weights based on outcome
*/
async adaptSONAWeights(outcome) {
const perf = this.agentPerformance.get(outcome.selectedAgent);
if (!perf) {
return;
}
// If performance drops below threshold, increase learning rate
if (perf.successRate < this.sonaConfig.adaptationThreshold) {
const adaptedLearningRate = this.sonaConfig.learningRate * 1.5;
// Simple weight adaptation: boost successful patterns, penalize failures
const adjustment = outcome.success
? this.sonaConfig.learningRate
: -this.sonaConfig.learningRate;
// Update adaptive weights (element-wise adjustment)
for (let i = 0; i < this.adaptiveWeights.length; i++) {
this.adaptiveWeights[i] = Math.max(0.1, this.adaptiveWeights[i] + adjustment);
}
}
}
}
//# sourceMappingURL=RuvLLMOrchestrator.js.map