@polybiouslabs/polybious
Version:
Polybius is a next-generation intelligent agent framework built for adaptability across diverse domains. It merges contextual awareness, multi-agent collaboration, and predictive reasoning to deliver dynamic, self-optimizing performance.
208 lines (207 loc) • 8.49 kB
JavaScript
import { logger } from '../config/logger';
import * as fs from 'fs/promises';
import * as path from 'path';
export class MemorySystem {
memories = new Map();
patterns = new Map();
memoryPath;
maxMemories;
constructor(agentName, maxMemories = 1000) {
this.memoryPath = path.join('data', 'memory', `${agentName}.json`);
this.maxMemories = maxMemories;
this.loadMemories();
}
async store(type, data, importance = 0.5, tags = []) {
const id = `mem_${Date.now()}_${Math.random().toString(36).substring(7)}`;
const entry = {
id,
timestamp: new Date(),
type,
data,
importance,
tags
};
this.memories.set(id, entry);
// Extract patterns from the data
if (type === 'content' && data.performance) {
await this.extractPatterns(data);
}
// Manage memory size
await this.consolidateMemories();
await this.saveMemories();
logger.debug('Memory stored', { id, type, importance, tags });
return id;
}
async recall(query, limit = 10) {
const queryTerms = query.toLowerCase().split(' ');
const scored = [];
for (const entry of this.memories.values()) {
let score = 0;
const entryText = JSON.stringify(entry.data).toLowerCase();
// Keyword matching
for (const term of queryTerms) {
if (entryText.includes(term))
score += 1;
if (entry.tags.some(tag => tag.toLowerCase().includes(term)))
score += 2;
}
// Recency bonus
const hoursSince = (Date.now() - entry.timestamp.getTime()) / (1000 * 60 * 60);
score += Math.max(0, 1 - hoursSince / 168); // Decay over a week
// Importance weighting
score *= entry.importance;
if (score > 0) {
scored.push({ entry, score });
}
}
return scored
.sort((a, b) => b.score - a.score)
.slice(0, limit)
.map(item => item.entry);
}
async getPatterns() {
return Array.from(this.patterns.values())
.sort((a, b) => b.confidence - a.confidence);
}
async getInsights() {
const insights = [];
const patterns = await this.getPatterns();
// High-performing content patterns
const contentPatterns = patterns.filter(p => p.pattern.includes('high_performance'));
if (contentPatterns.length > 0) {
insights.push(`${contentPatterns[0].pattern.replace('high_performance_', '').replace('_', ' ')} content performs ${Math.round(contentPatterns[0].confidence * 100)}% better`);
}
// Engagement timing patterns
const timingPatterns = patterns.filter(p => p.pattern.includes('timing'));
if (timingPatterns.length > 0) {
insights.push(`Best posting times: ${timingPatterns.slice(0, 3).map(p => p.pattern.split('_')[1]).join(', ')}`);
}
// Content length patterns
const lengthPatterns = patterns.filter(p => p.pattern.includes('length'));
if (lengthPatterns.length > 0) {
const avgLength = lengthPatterns.reduce((sum, p) => sum + parseInt(p.pattern.split('_')[1] || '0'), 0) / lengthPatterns.length;
insights.push(`Optimal content length: ~${Math.round(avgLength)} characters`);
}
return insights;
}
async extractPatterns(contentData) {
const { content, performance } = contentData;
if (!performance)
return;
const engagementScore = this.calculateEngagementScore(performance);
// Pattern: High-performing content length
if (engagementScore > 0.7) {
const lengthPattern = `high_performance_length_${Math.round(content.length / 50) * 50}`;
this.updatePattern(lengthPattern, engagementScore, content);
}
// Pattern: Time-based performance
const hour = new Date().getHours();
const timePattern = `timing_${hour}h`;
this.updatePattern(timePattern, engagementScore, `Posted at ${hour}:00`);
// Pattern: Content type performance
const contentType = this.inferContentType(content);
if (contentType) {
const typePattern = `high_performance_${contentType}`;
this.updatePattern(typePattern, engagementScore, content);
}
// Pattern: Hashtag effectiveness
const hashtags = content.match(/#\w+/g) || [];
if (hashtags.length > 0 && engagementScore > 0.6) {
hashtags.forEach((hashtag) => {
const hashtagPattern = `effective_hashtag_${hashtag.toLowerCase()}`;
this.updatePattern(hashtagPattern, engagementScore, content);
});
}
}
updatePattern(patternName, confidence, example) {
const existing = this.patterns.get(patternName);
if (existing) {
existing.confidence = (existing.confidence + confidence) / 2;
existing.frequency++;
existing.lastSeen = new Date();
if (existing.examples.length < 5) {
existing.examples.push(example);
}
}
else {
this.patterns.set(patternName, {
pattern: patternName,
confidence,
examples: [example],
lastSeen: new Date(),
frequency: 1
});
}
}
calculateEngagementScore(performance) {
const { likes, shares, comments, views } = performance;
const total = likes + shares * 2 + comments * 3; // Weight interactions
return Math.min(1, total / Math.max(1, views) * 100);
}
inferContentType(content) {
if (content.includes('?'))
return 'question';
if (content.match(/https?:\/\/\S+/))
return 'link_share';
if ((content.match(/#\w+/g)?.length || 0) > 2)
return 'hashtag_heavy';
if (content.length < 50)
return 'short_form';
if (content.length > 200)
return 'long_form';
return 'standard';
}
async consolidateMemories() {
if (this.memories.size <= this.maxMemories)
return;
// Sort by importance and recency
const sorted = Array.from(this.memories.entries())
.sort(([, a], [, b]) => {
const scoreA = a.importance + (Date.now() - a.timestamp.getTime()) / (1000 * 60 * 60 * 24 * 7);
const scoreB = b.importance + (Date.now() - b.timestamp.getTime()) / (1000 * 60 * 60 * 24 * 7);
return scoreB - scoreA;
});
// Keep top memories
this.memories.clear();
sorted.slice(0, this.maxMemories).forEach(([id, entry]) => {
this.memories.set(id, entry);
});
logger.info(`Consolidated memories: kept ${this.memories.size} out of ${sorted.length}`);
}
async loadMemories() {
try {
await fs.mkdir(path.dirname(this.memoryPath), { recursive: true });
const data = await fs.readFile(this.memoryPath, 'utf8');
const parsed = JSON.parse(data);
if (parsed.memories) {
parsed.memories.forEach((entry) => {
entry.timestamp = new Date(entry.timestamp);
this.memories.set(entry.id, entry);
});
}
if (parsed.patterns) {
parsed.patterns.forEach((pattern) => {
pattern.lastSeen = new Date(pattern.lastSeen);
this.patterns.set(pattern.pattern, pattern);
});
}
logger.info(`Loaded ${this.memories.size} memories and ${this.patterns.size} patterns`);
}
catch (error) {
logger.debug('No existing memory file found, starting fresh');
}
}
async saveMemories() {
try {
const data = {
memories: Array.from(this.memories.values()),
patterns: Array.from(this.patterns.values()),
savedAt: new Date().toISOString()
};
await fs.writeFile(this.memoryPath, JSON.stringify(data, null, 2));
}
catch (error) {
logger.error('Failed to save memories', { error });
}
}
}