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
448 lines (377 loc) • 18.3 kB
text/typescript
/**
* RuVector PostgreSQL Bridge - Self-Learning Example
*
* This example demonstrates:
* - Enabling the self-optimization learning loop
* - Monitoring query patterns and performance
* - Auto-tuning HNSW index parameters
* - Pattern recognition and anomaly detection
*
* Run with: npx ts-node examples/ruvector/self-learning.ts
*
* @module @claude-flow/plugins/examples/ruvector/self-learning
*/
import {
createRuVectorBridge,
type RuVectorBridge,
} from '../../src/integrations/ruvector/index.js';
import {
createSelfLearningSystem,
LearningLoop,
QueryOptimizer,
IndexTuner,
PatternRecognizer,
DEFAULT_LEARNING_CONFIG,
HIGH_ACCURACY_LEARNING_CONFIG,
type LearningConfig,
type QueryHistory,
type Pattern,
type WorkloadAnalysis,
} from '../../src/integrations/ruvector/self-learning.js';
// ============================================================================
// Configuration
// ============================================================================
const config = {
connection: {
host: process.env.POSTGRES_HOST || 'localhost',
port: parseInt(process.env.POSTGRES_PORT || '5432', 10),
database: process.env.POSTGRES_DB || 'vectors',
user: process.env.POSTGRES_USER || 'postgres',
password: process.env.POSTGRES_PASSWORD || 'postgres',
},
dimensions: 384,
learningEnabled: true,
};
// ============================================================================
// Simulated Query Workload
// ============================================================================
/**
* Simulated queries representing a realistic workload.
*/
const workloadQueries = [
// Vector similarity searches (frequent)
"SELECT id, embedding <-> $1 as distance FROM documents ORDER BY distance LIMIT 10",
"SELECT id, embedding <=> $1 as distance FROM documents WHERE category = 'tech' ORDER BY distance LIMIT 5",
"SELECT id, metadata FROM documents WHERE embedding <-> $1 < 0.5 LIMIT 20",
// Analytical queries
"SELECT category, COUNT(*) FROM documents GROUP BY category",
"SELECT DATE(created_at), COUNT(*) FROM documents GROUP BY DATE(created_at)",
// CRUD operations
"INSERT INTO documents (id, embedding, metadata) VALUES ($1, $2, $3)",
"UPDATE documents SET metadata = $1 WHERE id = $2",
"DELETE FROM documents WHERE created_at < $1",
// Complex joins
"SELECT d.id, d.metadata, c.name FROM documents d JOIN categories c ON d.category_id = c.id WHERE d.embedding <-> $1 < 0.3",
];
/**
* Generate realistic query history.
*/
function generateQueryHistory(queries: string[], count: number): QueryHistory[] {
const history: QueryHistory[] = [];
const now = Date.now();
for (let i = 0; i < count; i++) {
const query = queries[Math.floor(Math.random() * queries.length)];
const isVectorSearch = query.includes('<->') || query.includes('<=>');
// Simulate realistic durations
let baseDuration = isVectorSearch ? 50 : 10;
if (query.includes('GROUP BY')) baseDuration = 100;
if (query.includes('JOIN')) baseDuration = 150;
// Add some variance
const duration = baseDuration * (0.5 + Math.random());
// Simulate periodic pattern (business hours)
const hour = Math.floor((i / count) * 24);
const isPeakHour = hour >= 9 && hour <= 17;
const timestamp = new Date(now - (count - i) * 60000 + (isPeakHour ? 0 : Math.random() * 300000));
history.push({
fingerprint: `qf_${Math.abs(hashCode(query)).toString(16)}`,
sql: query,
timestamp,
durationMs: duration,
rowCount: Math.floor(Math.random() * 100) + 1,
success: Math.random() > 0.02, // 2% error rate
sessionId: `session_${i % 10}`,
});
}
return history;
}
/**
* Simple hash function for query fingerprinting.
*/
function hashCode(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;
}
/**
* Print workload analysis summary.
*/
function printWorkloadAnalysis(analysis: WorkloadAnalysis): void {
console.log('\n Workload Analysis Summary');
console.log(' ' + '-'.repeat(50));
console.log(` Total queries analyzed: ${analysis.totalQueries}`);
console.log(` Analysis duration: ${analysis.durationMs.toFixed(2)}ms`);
console.log('\n Query type distribution:');
analysis.queryDistribution.forEach((count, type) => {
const pct = ((count / analysis.totalQueries) * 100).toFixed(1);
console.log(` ${type}: ${count} (${pct}%)`);
});
console.log('\n Workload characteristics:');
console.log(` Read/Write ratio: ${analysis.characteristics.readWriteRatio.toFixed(2)}`);
console.log(` Vector search %: ${analysis.characteristics.vectorSearchPercentage.toFixed(1)}%`);
console.log(` Avg complexity: ${analysis.characteristics.avgComplexity.toFixed(3)}`);
console.log(` Type: ${analysis.characteristics.isOLTP ? 'OLTP' : analysis.characteristics.isOLAP ? 'OLAP' : 'Hybrid'}`);
console.log('\n Top query patterns:');
analysis.topPatterns.slice(0, 5).forEach((pattern, i) => {
console.log(` ${i + 1}. Frequency: ${pattern.frequency}, Avg: ${pattern.avgDurationMs.toFixed(2)}ms`);
console.log(` ${pattern.example.slice(0, 60)}...`);
});
console.log('\n Hot tables:');
analysis.hotTables.slice(0, 3).forEach(table => {
console.log(` ${table.tableName}: reads=${table.reads}, writes=${table.writes}, vector=${table.vectorSearches}`);
});
console.log('\n Recommendations:');
analysis.recommendations.forEach(rec => {
console.log(` [P${rec.priority}] ${rec.description}`);
console.log(` Impact: ${rec.estimatedImpact}`);
});
}
// ============================================================================
// Main Example
// ============================================================================
async function main(): Promise<void> {
console.log('RuVector PostgreSQL Bridge - Self-Learning Example');
console.log('====================================================\n');
const bridge: RuVectorBridge = createRuVectorBridge({
connectionString: `postgresql://${config.connection.user}:${config.connection.password}@${config.connection.host}:${config.connection.port}/${config.connection.database}`,
});
// Create self-learning system with custom configuration
const learningConfig: Partial<LearningConfig> = {
enableMicroLearning: true,
microLearningThresholdMs: 0.1,
enableBackgroundLearning: true,
backgroundLearningIntervalMs: 5000, // 5 seconds for demo
enableEWC: true,
ewcLambda: 0.5,
maxPatterns: 1000,
patternExpiryMs: 3600000, // 1 hour
};
const { learningLoop, queryOptimizer, indexTuner, patternRecognizer } =
createSelfLearningSystem(learningConfig);
try {
await bridge.connect();
console.log('Connected to PostgreSQL');
// ========================================================================
// 1. Enable Learning Loop
// ========================================================================
console.log('\n1. Starting Self-Learning Loop');
console.log(' ' + '-'.repeat(50));
learningLoop.start();
console.log(' Learning loop started');
console.log(` Configuration:`);
console.log(` - Micro-learning: ${learningConfig.enableMicroLearning ? 'enabled' : 'disabled'}`);
console.log(` - Background learning interval: ${learningConfig.backgroundLearningIntervalMs}ms`);
console.log(` - EWC protection: ${learningConfig.enableEWC ? 'enabled' : 'disabled'}`);
console.log(` - Max patterns: ${learningConfig.maxPatterns}`);
// ========================================================================
// 2. Simulate Query Workload
// ========================================================================
console.log('\n2. Simulating Query Workload');
console.log(' ' + '-'.repeat(50));
const queryHistory = generateQueryHistory(workloadQueries, 500);
console.log(` Generated ${queryHistory.length} simulated queries`);
// Feed queries to the learning system
console.log(' Processing queries through micro-learning...');
const startProcess = performance.now();
for (const history of queryHistory) {
// Micro-learning: process each query
learningLoop.microLearn(history.sql, history.durationMs, history.rowCount);
// Record in index tuner
indexTuner.recordQuery(history);
}
const processTime = performance.now() - startProcess;
console.log(` Processed ${queryHistory.length} queries in ${processTime.toFixed(2)}ms`);
console.log(` Throughput: ${(queryHistory.length / (processTime / 1000)).toFixed(0)} queries/second`);
// ========================================================================
// 3. Analyze Workload Patterns
// ========================================================================
console.log('\n3. Analyzing Workload Patterns');
console.log(' ' + '-'.repeat(50));
const workloadAnalysis = indexTuner.analyzeWorkload();
printWorkloadAnalysis(workloadAnalysis);
// ========================================================================
// 4. Query Optimization Suggestions
// ========================================================================
console.log('\n4. Query Optimization Suggestions');
console.log(' ' + '-'.repeat(50));
// Analyze a slow query
const slowQuery = "SELECT * FROM documents WHERE embedding <-> $1 < 0.5";
console.log(`\n Analyzing: "${slowQuery}"`);
const analysis = queryOptimizer.analyzeQuery(slowQuery);
console.log(`\n Analysis results:`);
console.log(` Query type: ${analysis.queryType}`);
console.log(` Complexity: ${(analysis.complexity * 100).toFixed(1)}%`);
console.log(` Parse time: ${analysis.parseTimeMs.toFixed(3)}ms`);
console.log('\n Vector operations:');
analysis.vectorOperations.forEach(op => {
console.log(` - ${op.type} on ${op.table}.${op.column} (metric: ${op.metric})`);
});
console.log('\n Bottlenecks:');
analysis.bottlenecks.forEach(b => {
console.log(` - [${b.type}] ${b.description} (severity: ${b.severity})`);
console.log(` Suggestion: ${b.suggestion}`);
});
const optimizations = queryOptimizer.suggestOptimizations(analysis);
console.log('\n Suggested optimizations:');
optimizations.forEach(opt => {
console.log(` - [${opt.type}] ${opt.description}`);
console.log(` Expected improvement: ${opt.expectedImprovement}%`);
console.log(` Confidence: ${(opt.confidence * 100).toFixed(0)}%, Risk: ${opt.risk}`);
});
// ========================================================================
// 5. Auto-Tune HNSW Parameters
// ========================================================================
console.log('\n5. Auto-Tuning HNSW Index Parameters');
console.log(' ' + '-'.repeat(50));
const hnswParams = indexTuner.tuneHNSW('documents');
console.log(' Recommended HNSW parameters for "documents" table:');
console.log(` M: ${hnswParams.m}`);
console.log(` ef_construction: ${hnswParams.efConstruction}`);
console.log(` ef_search: ${hnswParams.efSearch}`);
console.log(` Optimized for: ${hnswParams.optimizedFor}`);
console.log(` Confidence: ${(hnswParams.confidence * 100).toFixed(1)}%`);
console.log(` Estimated recall: ${(hnswParams.estimatedRecall * 100).toFixed(1)}%`);
console.log(` Estimated QPS: ${hnswParams.estimatedQps}`);
// Get index suggestions
const indexSuggestions = indexTuner.suggestIndexes();
console.log('\n Index suggestions:');
indexSuggestions.slice(0, 3).forEach(suggestion => {
console.log(` - ${suggestion.tableName}.${suggestion.columnName}`);
console.log(` Type: ${suggestion.indexType}, Metric: ${suggestion.metric}`);
console.log(` Confidence: ${(suggestion.confidence * 100).toFixed(1)}%`);
console.log(` SQL: ${suggestion.createSql}`);
});
// ========================================================================
// 6. Pattern Recognition
// ========================================================================
console.log('\n6. Pattern Recognition');
console.log(' ' + '-'.repeat(50));
// Learn patterns from history
patternRecognizer.learnFromHistory(queryHistory);
const patterns = patternRecognizer.detectPatterns();
console.log(` Detected ${patterns.length} patterns`);
console.log('\n Top patterns by occurrence:');
patterns.slice(0, 5).forEach((pattern, i) => {
console.log(` ${i + 1}. ${pattern.type} (${pattern.occurrences} occurrences)`);
console.log(` Confidence: ${(pattern.confidence * 100).toFixed(1)}%`);
if (pattern.temporal) {
console.log(` Periodic: ${pattern.temporal.isPeriodic}`);
console.log(` Peak hours: ${pattern.temporal.peakHours.join(', ')}`);
}
console.log(` Performance trend: ${pattern.performance.responseTrend}`);
});
// ========================================================================
// 7. Anomaly Detection
// ========================================================================
console.log('\n7. Anomaly Detection');
console.log(' ' + '-'.repeat(50));
// Simulate some anomalous queries
const anomalousQueries = [
"SELECT * FROM documents", // No limit - potential full scan
"SELECT * FROM documents d1, documents d2", // Cartesian product
];
const anomalies = patternRecognizer.detectAnomalies(anomalousQueries);
if (anomalies.length > 0) {
console.log(` Detected ${anomalies.length} anomalies:`);
anomalies.forEach((anomaly, i) => {
console.log(`\n ${i + 1}. ${anomaly.type}`);
console.log(` Query: ${anomaly.query.slice(0, 50)}...`);
console.log(` Severity: ${anomaly.severity}/10`);
console.log(` Description: ${anomaly.description}`);
console.log(` Possible causes:`);
anomaly.possibleCauses.forEach(cause => console.log(` - ${cause}`));
console.log(` Recommendations:`);
anomaly.recommendations.forEach(rec => console.log(` - ${rec}`));
});
} else {
console.log(' No anomalies detected in the analyzed queries');
}
// ========================================================================
// 8. Learning Statistics
// ========================================================================
console.log('\n8. Learning Statistics');
console.log(' ' + '-'.repeat(50));
const stats = learningLoop.getStats();
console.log(' Current learning state:');
console.log(` Total patterns: ${stats.totalPatterns}`);
console.log(` Active patterns: ${stats.activePatterns}`);
console.log(` Micro-learning events: ${stats.microLearningEvents}`);
console.log(` Background learning cycles: ${stats.backgroundLearningCycles}`);
console.log(` EWC consolidations: ${stats.ewcConsolidations}`);
console.log(` Avg learning time: ${stats.avgLearningTimeMs.toFixed(3)}ms`);
console.log(` Memory usage: ${(stats.memoryUsageBytes / 1024).toFixed(2)} KB`);
console.log(` Last learning: ${stats.lastLearningTimestamp.toISOString()}`);
// EWC state
const ewcState = learningLoop.getEWCState();
console.log('\n EWC++ Protection State:');
console.log(` Protected patterns: ${ewcState.protectedPatterns.size}`);
console.log(` Consolidation count: ${ewcState.consolidationCount}`);
console.log(` Fisher matrix entries: ${ewcState.fisherDiagonal.size}`);
// ========================================================================
// 9. Query Statistics
// ========================================================================
console.log('\n9. Query Statistics');
console.log(' ' + '-'.repeat(50));
const queryStats = queryOptimizer.getQueryStats() as Array<{
fingerprint: string;
sql: string;
executionCount: number;
avgDurationMs: number;
p95DurationMs: number;
}>;
if (Array.isArray(queryStats) && queryStats.length > 0) {
console.log(` Tracked ${queryStats.length} unique query patterns`);
// Sort by execution count
const topQueries = queryStats
.sort((a, b) => b.executionCount - a.executionCount)
.slice(0, 5);
console.log('\n Top queries by frequency:');
topQueries.forEach((stat, i) => {
console.log(` ${i + 1}. ${stat.sql.slice(0, 50)}...`);
console.log(` Executions: ${stat.executionCount}`);
console.log(` Avg duration: ${stat.avgDurationMs.toFixed(2)}ms`);
console.log(` P95 duration: ${stat.p95DurationMs.toFixed(2)}ms`);
});
}
// ========================================================================
// 10. Stop Learning Loop
// ========================================================================
console.log('\n10. Stopping Learning Loop');
console.log(' ' + '-'.repeat(50));
learningLoop.stop();
console.log(' Learning loop stopped');
console.log(` Final stats:`);
console.log(` - Total patterns learned: ${learningLoop.getStats().totalPatterns}`);
console.log(` - Total micro-learning events: ${learningLoop.getStats().microLearningEvents}`);
// ========================================================================
// Done
// ========================================================================
console.log('\n' + '='.repeat(55));
console.log('Self-learning example completed!');
console.log('='.repeat(55));
} catch (error) {
console.error('Error:', error);
throw error;
} finally {
// Ensure learning loop is stopped
if (learningLoop.isActive()) {
learningLoop.stop();
}
await bridge.disconnect();
console.log('\nDisconnected from PostgreSQL.');
}
}
main().catch(console.error);