import * as fs from 'fs/promises';
import * as path from 'path';
export class PersistentLearningSystem {
knowledgeBase = new Map();
sessionMemory = new Map();
currentSessionId;
learningRate = 0.1;
forgettingRate = 0.01;
storagePath;
constructor(storagePath = './data/learning') {
this.storagePath = storagePath;
this.currentSessionId = `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
this.initializeSession();
}
async initializeSession() {
await this.loadPersistedKnowledge();
this.sessionMemory.set(this.currentSessionId, {
sessionId: this.currentSessionId,
startTime: Date.now(),
interactions: [],
discoveries: [],
performanceMetrics: {}
});
}
async learnFromInteraction(interaction) {
const session = this.sessionMemory.get(this.currentSessionId);
if (session) {
session.interactions.push(interaction);
}
if (interaction.success) {
const newTriples = this.extractLearningTriples(interaction);
for (const triple of newTriples) {
await this.addKnowledge(triple);
}
const patterns = this.detectPatterns(session?.interactions || []);
for (const pattern of patterns) {
await this.recordDiscovery({
timestamp: Date.now(),
type: 'pattern',
content: pattern,
novelty: this.calculateNovelty(pattern),
utility: this.calculateUtility(pattern)
});
}
}
}
async addKnowledge(triple) {
const key = `${triple.subject}:${triple.predicate}:${triple.object}`;
const existing = this.knowledgeBase.get(key);
if (existing) {
existing.confidence = Math.min(1.0, existing.confidence + this.learningRate * (1 - existing.confidence));
existing.timestamp = Date.now();
existing.sources.push(triple.sessionId);
}
else {
this.knowledgeBase.set(key, triple);
}
await this.persistKnowledge();
}
queryKnowledge(subject, predicate, object) {
const results = [];
for (const [key, triple] of this.knowledgeBase) {
let matches = true;
if (subject && triple.subject !== subject)
matches = false;
if (predicate && triple.predicate !== predicate)
matches = false;
if (object && triple.object !== object)
matches = false;
if (matches) {
results.push(triple);
}
}
return results.sort((a, b) => (b.confidence * 0.7 + (b.timestamp / Date.now()) * 0.3) -
(a.confidence * 0.7 + (a.timestamp / Date.now()) * 0.3));
}
async analyzeHistoricalPatterns() {
const allSessions = Array.from(this.sessionMemory.values());
const discoveries = [];
const successPatterns = this.findSuccessPatterns(allSessions);
discoveries.push(...successPatterns.map(pattern => ({
timestamp: Date.now(),
type: 'pattern',
content: pattern,
novelty: this.calculateNovelty(pattern),
utility: this.calculateUtility(pattern)
})));
const toolEffectiveness = this.analyzeToolEffectiveness(allSessions);
discoveries.push({
timestamp: Date.now(),
type: 'optimization',
content: { toolRankings: toolEffectiveness },
novelty: 0.5,
utility: 0.8
});
for (const discovery of discoveries) {
await this.recordDiscovery(discovery);
}
return discoveries;
}
getLearningRecommendations() {
const recommendations = [];
const underutilized = this.findUnderutilizedCombinations();
recommendations.push({
type: 'exploration',
suggestion: 'Try under-utilized tool combinations',
combinations: underutilized,
priority: 0.7
});
const successfulPatterns = this.getSuccessfulPatterns();
recommendations.push({
type: 'reinforcement',
suggestion: 'Strengthen successful reasoning patterns',
patterns: successfulPatterns,
priority: 0.8
});
const weakAreas = this.identifyWeakAreas();
recommendations.push({
type: 'improvement',
suggestion: 'Focus learning on weak performance areas',
areas: weakAreas,
priority: 0.9
});
return recommendations.sort((a, b) => b.priority - a.priority);
}
async applyForgetting() {
const now = Date.now();
const oneDay = 24 * 60 * 60 * 1000;
for (const [key, triple] of this.knowledgeBase) {
const age = now - triple.timestamp;
const ageDays = age / oneDay;
const forgettingFactor = Math.exp(-this.forgettingRate * ageDays);
triple.confidence *= forgettingFactor;
if (triple.confidence < 0.01) {
this.knowledgeBase.delete(key);
}
}
await this.persistKnowledge();
}
extractLearningTriples(interaction) {
const triples = [];
if (interaction.success && interaction.tools.length > 0) {
triples.push({
subject: interaction.tools.join('+'),
predicate: 'effective_for',
object: interaction.type,
confidence: 0.5,
timestamp: Date.now(),
sessionId: this.currentSessionId,
sources: [this.currentSessionId]
});
}
if (interaction.input && interaction.output) {
const inputPattern = this.extractPattern(interaction.input);
const outputPattern = this.extractPattern(interaction.output);
if (inputPattern && outputPattern) {
triples.push({
subject: inputPattern,
predicate: 'transforms_to',
object: outputPattern,
confidence: 0.6,
timestamp: Date.now(),
sessionId: this.currentSessionId,
sources: [this.currentSessionId]
});
}
}
return triples;
}
extractPattern(data) {
if (typeof data === 'string')
return data.substring(0, 50);
if (typeof data === 'object')
return JSON.stringify(data).substring(0, 50);
return null;
}
detectPatterns(interactions) {
const patterns = [];
const temporalPatterns = this.findTemporalPatterns(interactions);
patterns.push(...temporalPatterns);
const toolPatterns = this.findToolPatterns(interactions);
patterns.push(...toolPatterns);
return patterns;
}
findTemporalPatterns(interactions) {
return [];
}
findToolPatterns(interactions) {
return [];
}
findSuccessPatterns(sessions) {
return [];
}
analyzeToolEffectiveness(sessions) {
return {};
}
findUnderutilizedCombinations() {
return [];
}
getSuccessfulPatterns() {
return [];
}
identifyWeakAreas() {
return [];
}
calculateNovelty(pattern) {
return Math.random() * 0.5 + 0.5; }
calculateUtility(pattern) {
return Math.random() * 0.5 + 0.5; }
async recordDiscovery(discovery) {
const session = this.sessionMemory.get(this.currentSessionId);
if (session) {
session.discoveries.push(discovery);
}
}
async persistKnowledge() {
try {
await fs.mkdir(this.storagePath, { recursive: true });
const knowledgeArray = Array.from(this.knowledgeBase.values());
await fs.writeFile(path.join(this.storagePath, 'knowledge_base.json'), JSON.stringify(knowledgeArray, null, 2));
const sessionArray = Array.from(this.sessionMemory.values());
await fs.writeFile(path.join(this.storagePath, 'session_memory.json'), JSON.stringify(sessionArray, null, 2));
}
catch (error) {
console.error('Failed to persist knowledge:', error);
}
}
async loadPersistedKnowledge() {
try {
const knowledgePath = path.join(this.storagePath, 'knowledge_base.json');
const sessionPath = path.join(this.storagePath, 'session_memory.json');
try {
const knowledgeData = await fs.readFile(knowledgePath, 'utf-8');
const knowledgeArray = JSON.parse(knowledgeData);
this.knowledgeBase.clear();
for (const triple of knowledgeArray) {
const key = `${triple.subject}:${triple.predicate}:${triple.object}`;
this.knowledgeBase.set(key, triple);
}
}
catch (error) {
}
try {
const sessionData = await fs.readFile(sessionPath, 'utf-8');
const sessionArray = JSON.parse(sessionData);
this.sessionMemory.clear();
for (const session of sessionArray) {
this.sessionMemory.set(session.sessionId, session);
}
}
catch (error) {
}
}
catch (error) {
console.error('Failed to load persisted knowledge:', error);
}
}
getLearningStats() {
return {
totalTriples: this.knowledgeBase.size,
currentSession: this.currentSessionId,
totalSessions: this.sessionMemory.size,
avgConfidence: this.calculateAverageConfidence(),
lastUpdate: this.getLastUpdateTime(),
learningRate: this.learningRate,
forgettingRate: this.forgettingRate
};
}
calculateAverageConfidence() {
const triples = Array.from(this.knowledgeBase.values());
if (triples.length === 0)
return 0;
const sum = triples.reduce((acc, triple) => acc + triple.confidence, 0);
return sum / triples.length;
}
getLastUpdateTime() {
const triples = Array.from(this.knowledgeBase.values());
if (triples.length === 0)
return 0;
return Math.max(...triples.map(triple => triple.timestamp));
}
}