Spaces:
Running
Running
| /** | |
| * AgentOS 2.0 - Base Agent Class | |
| * All department agents inherit from this class | |
| * Provides: memory, personality, voice, scoring, learning | |
| */ | |
| const fs = require('fs'); | |
| const path = require('path'); | |
| const apiCaller = require('../skills/api_caller'); | |
| const memory = require('../skills/memory'); | |
| const conversationMemory = require('../skills/conversation_memory'); | |
| const AGENTS_DB = path.join(__dirname, '../database/agents_db.json'); | |
| const METRICS_DB = path.join(__dirname, '../database/metrics_db.json'); | |
| class BaseAgent { | |
| constructor(config) { | |
| this.id = config.id; | |
| this.name = config.name; | |
| this.role = config.role; | |
| this.department = config.department; | |
| this.personality = config.personality; | |
| this.voice = config.voice; | |
| this.skills = config.skills || []; | |
| this.status = 'idle'; // idle, working, waiting, error | |
| this.lastActive = null; | |
| this.pulseInterval = config.pulseInterval || 15 * 60 * 1000; // 15 min default | |
| this.pulseTimer = null; | |
| // Load agent-specific memory | |
| this.agentMemory = this.loadAgentMemory(); | |
| // Metrics | |
| this.metrics = { | |
| tasksCompleted: 0, | |
| tasksFailed: 0, | |
| avgScore: 0, | |
| totalActions: 0, | |
| learningLevel: 1 | |
| }; | |
| this.loadMetrics(); | |
| } | |
| /** | |
| * Agent personality system - defines tone, style, behavior | |
| */ | |
| getSystemPrompt() { | |
| return `You are ${this.name}, the ${this.role} in AgentOS. | |
| PERSONALITY: | |
| ${this.personality.description} | |
| VOICE & STYLE: | |
| - Tone: ${this.personality.tone} | |
| - Speaking style: ${this.personality.style} | |
| - Emoji usage: ${this.personality.emojiUsage} | |
| - Greeting: ${this.personality.greeting} | |
| - Signoff: ${this.personality.signoff} | |
| RESPONSIBILITIES: | |
| ${this.personality.responsibilities.join('\n')} | |
| BEHAVIOR RULES: | |
| 1. Always be proactive - suggest next actions | |
| 2. Ask for feedback after completing tasks | |
| 3. Learn from user corrections | |
| 4. Score your own work (1-10) before submitting | |
| 5. If stuck, ask the CEO (APEX) for guidance | |
| 6. Maintain context across conversations | |
| 7. Document everything for legacy tracking | |
| CURRENT CONTEXT: | |
| - User: ${this.agentMemory.userProfile?.name || 'Unknown'} | |
| - Company: ${this.agentMemory.userProfile?.company || 'Unknown'} | |
| - Goals: ${JSON.stringify(this.agentMemory.userProfile?.goals || [])} | |
| - Preferences: ${JSON.stringify(this.agentMemory.userProfile?.preferences || {})}`; | |
| } | |
| /** | |
| * Load agent-specific memory from DB | |
| */ | |
| loadAgentMemory() { | |
| try { | |
| const db = JSON.parse(fs.readFileSync(AGENTS_DB, 'utf8')); | |
| return db.agents?.[this.id] || { | |
| userProfile: {}, | |
| conversationHistory: [], | |
| learnedPatterns: [], | |
| feedbackHistory: [], | |
| skillLevels: {} | |
| }; | |
| } catch (e) { | |
| return { userProfile: {}, conversationHistory: [], learnedPatterns: [], feedbackHistory: [], skillLevels: {} }; | |
| } | |
| } | |
| /** | |
| * Save agent memory | |
| */ | |
| saveAgentMemory() { | |
| try { | |
| const db = JSON.parse(fs.readFileSync(AGENTS_DB, 'utf8')) || { agents: {} }; | |
| db.agents[this.id] = this.agentMemory; | |
| fs.writeFileSync(AGENTS_DB, JSON.stringify(db, null, 2)); | |
| return true; | |
| } catch (e) { | |
| console.error(`[${this.name}] Failed to save memory:`, e.message); | |
| return false; | |
| } | |
| } | |
| /** | |
| * Load metrics from DB | |
| */ | |
| loadMetrics() { | |
| try { | |
| const db = JSON.parse(fs.readFileSync(METRICS_DB, 'utf8')); | |
| const agentMetrics = db.agents?.[this.id]; | |
| if (agentMetrics) { | |
| this.metrics = { ...this.metrics, ...agentMetrics }; | |
| } | |
| } catch (e) { | |
| // No metrics yet | |
| } | |
| } | |
| /** | |
| * Save metrics | |
| */ | |
| saveMetrics() { | |
| try { | |
| const db = JSON.parse(fs.readFileSync(METRICS_DB, 'utf8')) || { agents: {}, timestamp: new Date().toISOString() }; | |
| db.agents[this.id] = this.metrics; | |
| db.timestamp = new Date().toISOString(); | |
| fs.writeFileSync(METRICS_DB, JSON.stringify(db, null, 2)); | |
| return true; | |
| } catch (e) { | |
| console.error(`[${this.name}] Failed to save metrics:`, e.message); | |
| return false; | |
| } | |
| } | |
| /** | |
| * Score an action (1-10) | |
| */ | |
| scoreAction(action, score, feedback = '') { | |
| const record = { | |
| action, | |
| score, | |
| feedback, | |
| timestamp: new Date().toISOString(), | |
| agent: this.id | |
| }; | |
| // Update running average | |
| const totalActions = this.metrics.totalActions + 1; | |
| this.metrics.avgScore = ((this.metrics.avgScore * this.metrics.totalActions) + score) / totalActions; | |
| this.metrics.totalActions = totalActions; | |
| // Store in feedback history | |
| this.agentMemory.feedbackHistory.push(record); | |
| if (this.agentMemory.feedbackHistory.length > 100) { | |
| this.agentMemory.feedbackHistory = this.agentMemory.feedbackHistory.slice(-100); | |
| } | |
| this.saveMetrics(); | |
| this.saveAgentMemory(); | |
| console.log(`[${this.name}] Action scored: ${score}/10 - ${action}`); | |
| return record; | |
| } | |
| /** | |
| * Learn from feedback | |
| */ | |
| async learnFromFeedback(task, feedback, correction) { | |
| const learning = { | |
| task, | |
| feedback, | |
| correction, | |
| timestamp: new Date().toISOString(), | |
| lesson: await this.extractLesson(task, feedback, correction) | |
| }; | |
| this.agentMemory.learnedPatterns.push(learning); | |
| this.metrics.learningLevel = Math.min(10, Math.floor(this.agentMemory.learnedPatterns.length / 5) + 1); | |
| this.saveAgentMemory(); | |
| this.saveMetrics(); | |
| console.log(`[${this.name}] Learned: ${learning.lesson}`); | |
| return learning; | |
| } | |
| /** | |
| * Extract lesson from feedback using AI | |
| */ | |
| async extractLesson(task, feedback, correction) { | |
| const prompt = [ | |
| { role: 'system', content: 'Extract a concise lesson from this feedback. Max 1 sentence.' }, | |
| { role: 'user', content: `Task: ${task}\nFeedback: ${feedback}\nCorrection: ${correction}\n\nLesson:` } | |
| ]; | |
| const result = await apiCaller.callOpenRouter(prompt, 'meta-llama/llama-3.3-70b-instruct:free'); | |
| return result.success ? result.data : 'Learned from feedback'; | |
| } | |
| /** | |
| * Execute a task with scoring | |
| */ | |
| async execute(task, options = {}) { | |
| const startTime = Date.now(); | |
| this.status = 'working'; | |
| this.lastActive = new Date().toISOString(); | |
| try { | |
| // Pre-task: Check context | |
| const context = await this.gatherContext(task); | |
| // Execute task using appropriate skill | |
| const result = await this.performTask(task, context, options); | |
| // Self-score before user feedback | |
| const selfScore = await this.selfScore(result); | |
| // Post-task: Update metrics | |
| this.metrics.tasksCompleted++; | |
| const duration = ((Date.now() - startTime) / 1000).toFixed(2); | |
| this.status = 'idle'; | |
| return { | |
| success: true, | |
| agent: this.name, | |
| result, | |
| selfScore, | |
| duration, | |
| timestamp: new Date().toISOString() | |
| }; | |
| } catch (error) { | |
| this.metrics.tasksFailed++; | |
| this.status = 'error'; | |
| return { | |
| success: false, | |
| agent: this.name, | |
| error: error.message, | |
| timestamp: new Date().toISOString() | |
| }; | |
| } finally { | |
| this.saveMetrics(); | |
| } | |
| } | |
| /** | |
| * Gather context for task (RAG-style) | |
| */ | |
| async gatherContext(task) { | |
| const context = { | |
| userProfile: this.agentMemory.userProfile, | |
| relevantHistory: this.agentMemory.conversationHistory.slice(-10), | |
| learnedPatterns: this.agentMemory.learnedPatterns.slice(-5), | |
| similarTasks: await this.findSimilarTasks(task) | |
| }; | |
| return context; | |
| } | |
| /** | |
| * Find similar tasks from history | |
| */ | |
| async findSimilarTasks(task) { | |
| // Simple keyword matching - can be enhanced with vector search | |
| const history = this.agentMemory.conversationHistory; | |
| const taskKeywords = task.toLowerCase().split(' ').filter(w => w.length > 4); | |
| return history.filter(h => | |
| taskKeywords.some(k => h.content?.toLowerCase().includes(k)) | |
| ).slice(-3); | |
| } | |
| /** | |
| * Self-score result before user feedback | |
| */ | |
| async selfScore(result) { | |
| const prompt = [ | |
| { role: 'system', content: 'Score this task result from 1-10 based on completeness and quality.' }, | |
| { role: 'user', content: `Result: ${JSON.stringify(result).substring(0, 500)}\n\nScore (1-10):` } | |
| ]; | |
| const res = await apiCaller.callOpenRouter(prompt, 'meta-llama/llama-3.3-70b-instruct:free'); | |
| const score = parseInt(res.data.match(/\d+/)?.[0]) || 5; | |
| return Math.min(10, Math.max(1, score)); | |
| } | |
| /** | |
| * Abstract method - override in subclasses | |
| */ | |
| async performTask(task, context, options) { | |
| throw new Error('performTask must be implemented by subclass'); | |
| } | |
| /** | |
| * Start pulse/heartbeat | |
| */ | |
| startPulse() { | |
| if (this.pulseTimer) clearInterval(this.pulseTimer); | |
| this.pulseTimer = setInterval(() => { | |
| this.pulse(); | |
| }, this.pulseInterval); | |
| console.log(`[${this.name}] Pulse started (${this.pulseInterval/1000}s interval)`); | |
| } | |
| /** | |
| * Pulse heartbeat - check pending tasks | |
| */ | |
| async pulse() { | |
| console.log(`[${this.name}] Pulse check at ${new Date().toISOString()}`); | |
| // Override in subclasses for specific pulse behavior | |
| } | |
| /** | |
| * Stop pulse | |
| */ | |
| stopPulse() { | |
| if (this.pulseTimer) { | |
| clearInterval(this.pulseTimer); | |
| this.pulseTimer = null; | |
| console.log(`[${this.name}] Pulse stopped`); | |
| } | |
| } | |
| /** | |
| * Get agent status | |
| */ | |
| getStatus() { | |
| return { | |
| id: this.id, | |
| name: this.name, | |
| role: this.role, | |
| status: this.status, | |
| lastActive: this.lastActive, | |
| metrics: this.metrics, | |
| learningLevel: this.metrics.learningLevel | |
| }; | |
| } | |
| /** | |
| * Interview user to learn preferences | |
| */ | |
| async interviewUser(chatId, topic = 'general') { | |
| const questions = await this.generateInterviewQuestions(topic); | |
| return { | |
| agent: this.name, | |
| topic, | |
| questions, | |
| instructions: 'Answer these questions to help me understand your needs better.' | |
| }; | |
| } | |
| /** | |
| * Generate interview questions | |
| */ | |
| async generateInterviewQuestions(topic) { | |
| const prompt = [ | |
| { role: 'system', content: `You are ${this.name}. Generate 5 interview questions to understand user's ${topic} needs.` }, | |
| { role: 'user', content: `Generate 5 concise questions about: ${topic}` } | |
| ]; | |
| const result = await apiCaller.callOpenRouter(prompt, 'meta-llama/llama-3.3-70b-instruct:free'); | |
| return result.success ? result.data.split('\n').filter(q => q.trim()) : []; | |
| } | |
| } | |
| module.exports = BaseAgent; | |