tasq/node_modules/@claude-flow/neural/dist/reasoning-bank.js

993 lines
37 KiB
JavaScript

/**
* ReasoningBank Integration with AgentDB
*
* Implements the 4-step learning pipeline with real vector storage:
* 1. RETRIEVE - Top-k memory injection with MMR diversity (using AgentDB HNSW)
* 2. JUDGE - LLM-as-judge trajectory evaluation
* 3. DISTILL - Extract strategy memories from trajectories
* 4. CONSOLIDATE - Dedup, detect contradictions, prune old patterns
*
* Performance Targets:
* - Retrieval: <10ms with AgentDB HNSW (150x faster than brute-force)
* - Learning step: <10ms
* - Consolidation: <100ms
*
* @module reasoning-bank
*/
// ============================================================================
// AgentDB Integration
// ============================================================================
let AgentDB;
let agentdbImportPromise;
async function ensureAgentDBImport() {
if (!agentdbImportPromise) {
agentdbImportPromise = (async () => {
try {
const agentdbModule = await import('agentdb');
AgentDB = agentdbModule.AgentDB || agentdbModule.default;
}
catch {
// AgentDB not available - will use fallback
AgentDB = undefined;
}
})();
}
return agentdbImportPromise;
}
/**
* Default ReasoningBank configuration
*/
const DEFAULT_CONFIG = {
maxTrajectories: 5000,
distillationThreshold: 0.6,
retrievalK: 3,
mmrLambda: 0.7,
maxPatternAgeDays: 30,
dedupThreshold: 0.95,
enableContradictionDetection: true,
dbPath: undefined,
vectorDimension: 768,
namespace: 'reasoning-bank',
enableAgentDB: true,
};
// ============================================================================
// ReasoningBank Class
// ============================================================================
/**
* ReasoningBank - Trajectory storage and learning pipeline with AgentDB
*
* This class implements a complete learning pipeline for AI agents:
* - Store and retrieve trajectories using vector similarity
* - Judge trajectory quality using rule-based evaluation
* - Distill successful trajectories into reusable patterns
* - Consolidate patterns to remove duplicates and contradictions
*/
export class ReasoningBank {
config;
trajectories = new Map();
memories = new Map();
patterns = new Map();
eventListeners = new Set();
// AgentDB instance for vector storage
agentdb = null;
agentdbAvailable = false;
initialized = false;
// Performance tracking
retrievalCount = 0;
totalRetrievalTime = 0;
distillationCount = 0;
totalDistillationTime = 0;
judgeCount = 0;
totalJudgeTime = 0;
consolidationCount = 0;
totalConsolidationTime = 0;
constructor(config = {}) {
this.config = { ...DEFAULT_CONFIG, ...config };
}
// ==========================================================================
// Initialization
// ==========================================================================
/**
* Initialize ReasoningBank with AgentDB
*/
async initialize() {
if (this.initialized)
return;
if (this.config.enableAgentDB) {
await ensureAgentDBImport();
this.agentdbAvailable = AgentDB !== undefined;
if (this.agentdbAvailable) {
try {
this.agentdb = new AgentDB({
dbPath: this.config.dbPath || ':memory:',
namespace: this.config.namespace,
vectorDimension: this.config.vectorDimension,
vectorBackend: 'auto',
});
await this.agentdb.initialize();
this.emitEvent({ type: 'memory_consolidated', memoriesCount: 0 });
}
catch (error) {
console.warn('AgentDB initialization failed, using fallback:', error);
this.agentdbAvailable = false;
}
}
}
this.initialized = true;
}
/**
* Shutdown and cleanup resources
*/
async shutdown() {
if (this.agentdb) {
await this.agentdb.close?.();
}
this.initialized = false;
}
// ==========================================================================
// STEP 1: RETRIEVE - Top-k memory injection with MMR diversity
// ==========================================================================
/**
* Retrieve relevant memories using Maximal Marginal Relevance (MMR)
*
* Uses AgentDB HNSW index for 150x faster retrieval when available.
*
* @param queryEmbedding - Query vector for similarity search
* @param k - Number of results to return (default: config.retrievalK)
* @returns Retrieval results with relevance and diversity scores
*/
async retrieve(queryEmbedding, k) {
const startTime = performance.now();
const retrieveK = k ?? this.config.retrievalK;
if (this.memories.size === 0) {
return [];
}
let candidates = [];
// Try AgentDB HNSW search first
if (this.agentdb && this.agentdbAvailable) {
try {
const results = await this.searchWithAgentDB(queryEmbedding, retrieveK * 3);
candidates = results
.map(r => {
const entry = this.memories.get(r.id);
return entry ? { entry, relevance: r.similarity } : null;
})
.filter((c) => c !== null);
}
catch {
// Fall through to brute-force
}
}
// Fallback: brute-force search
if (candidates.length === 0) {
for (const entry of this.memories.values()) {
const relevance = this.cosineSimilarity(queryEmbedding, entry.memory.embedding);
candidates.push({ entry, relevance });
}
candidates.sort((a, b) => b.relevance - a.relevance);
}
// Apply MMR for diversity
const results = [];
const selected = [];
while (results.length < retrieveK && candidates.length > 0) {
let bestIdx = 0;
let bestScore = -Infinity;
for (let i = 0; i < candidates.length; i++) {
const candidate = candidates[i];
// Compute MMR score: lambda * relevance - (1 - lambda) * max_similarity_to_selected
const relevance = candidate.relevance;
let maxSimilarity = 0;
for (const sel of selected) {
const sim = this.cosineSimilarity(candidate.entry.memory.embedding, sel.memory.embedding);
maxSimilarity = Math.max(maxSimilarity, sim);
}
const diversityScore = 1 - maxSimilarity;
const mmrScore = this.config.mmrLambda * relevance +
(1 - this.config.mmrLambda) * diversityScore;
if (mmrScore > bestScore) {
bestScore = mmrScore;
bestIdx = i;
}
}
// Add best candidate
const best = candidates[bestIdx];
selected.push(best.entry);
results.push({
memory: best.entry.memory,
relevanceScore: best.relevance,
diversityScore: 1 - this.computeMaxSimilarity(best.entry, selected.slice(0, -1)),
combinedScore: bestScore,
});
// Remove from candidates
candidates.splice(bestIdx, 1);
}
// Update stats
this.retrievalCount++;
this.totalRetrievalTime += performance.now() - startTime;
return results;
}
/**
* Search for similar memories by content string
*
* @param content - Text content to search for
* @param k - Number of results
* @returns Retrieval results
*/
async retrieveByContent(content, k) {
// Simple content-based retrieval using memory strategies
const retrieveK = k ?? this.config.retrievalK;
const results = [];
const contentLower = content.toLowerCase();
const entries = Array.from(this.memories.values());
// Score by content similarity
const scored = entries.map(entry => ({
entry,
score: this.computeContentSimilarity(contentLower, entry.memory.strategy),
}));
scored.sort((a, b) => b.score - a.score);
for (let i = 0; i < Math.min(retrieveK, scored.length); i++) {
const { entry, score } = scored[i];
if (score > 0) {
results.push({
memory: entry.memory,
relevanceScore: score,
diversityScore: 1,
combinedScore: score,
});
}
}
return results;
}
// ==========================================================================
// STEP 2: JUDGE - LLM-as-judge trajectory evaluation
// ==========================================================================
/**
* Judge a trajectory and produce a verdict
*
* Uses rule-based evaluation to assess trajectory quality.
* In production, this could be enhanced with LLM-as-judge.
*
* @param trajectory - Completed trajectory to judge
* @returns Verdict with success status and analysis
*/
async judge(trajectory) {
const startTime = performance.now();
if (!trajectory.isComplete) {
throw new Error('Cannot judge incomplete trajectory');
}
// Analyze trajectory steps
const stepAnalysis = this.analyzeSteps(trajectory.steps);
// Compute success based on quality and step analysis
const success = trajectory.qualityScore >= this.config.distillationThreshold &&
stepAnalysis.positiveRatio > 0.6;
// Identify strengths and weaknesses
const strengths = this.identifyStrengths(trajectory, stepAnalysis);
const weaknesses = this.identifyWeaknesses(trajectory, stepAnalysis);
// Generate improvement suggestions
const improvements = this.generateImprovements(weaknesses);
// Compute relevance for similar future tasks
const relevanceScore = this.computeRelevanceScore(trajectory);
const verdict = {
success,
confidence: this.computeConfidence(trajectory, stepAnalysis),
strengths,
weaknesses,
improvements,
relevanceScore,
};
// Store verdict with trajectory
trajectory.verdict = verdict;
// Update stats
this.judgeCount++;
this.totalJudgeTime += performance.now() - startTime;
return verdict;
}
// ==========================================================================
// STEP 3: DISTILL - Extract strategy memories from trajectories
// ==========================================================================
/**
* Distill a trajectory into a reusable memory
*
* @param trajectory - Trajectory to distill
* @returns Distilled memory or null if quality too low
*/
async distill(trajectory) {
const startTime = performance.now();
// Must be judged first
if (!trajectory.verdict) {
await this.judge(trajectory);
}
// Only distill successful trajectories
if (!trajectory.verdict.success ||
trajectory.qualityScore < this.config.distillationThreshold) {
return null;
}
// Extract strategy from trajectory
const strategy = this.extractStrategy(trajectory);
// Extract key learnings
const keyLearnings = this.extractKeyLearnings(trajectory);
// Compute aggregated embedding
const embedding = this.computeAggregateEmbedding(trajectory);
const memory = {
memoryId: `mem_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`,
trajectoryId: trajectory.trajectoryId,
strategy,
keyLearnings,
embedding,
quality: trajectory.qualityScore,
usageCount: 0,
lastUsed: Date.now(),
};
// Store the memory
const entry = {
memory,
trajectory,
verdict: trajectory.verdict,
consolidated: false,
};
this.memories.set(memory.memoryId, entry);
// Store in AgentDB for vector search
if (this.agentdb && this.agentdbAvailable) {
await this.storeInAgentDB(memory);
}
// Also store trajectory reference
trajectory.distilledMemory = memory;
// Update stats
this.distillationCount++;
this.totalDistillationTime += performance.now() - startTime;
this.emitEvent({
type: 'trajectory_completed',
trajectoryId: trajectory.trajectoryId,
qualityScore: trajectory.qualityScore,
});
return memory;
}
/**
* Batch distill multiple trajectories
*
* @param trajectories - Array of trajectories to distill
* @returns Array of distilled memories (excludes nulls)
*/
async distillBatch(trajectories) {
const memories = [];
for (const trajectory of trajectories) {
const memory = await this.distill(trajectory);
if (memory) {
memories.push(memory);
}
}
return memories;
}
// ==========================================================================
// STEP 4: CONSOLIDATE - Dedup, detect contradictions, prune old patterns
// ==========================================================================
/**
* Consolidate memories: deduplicate, detect contradictions, prune old
*
* @returns Consolidation statistics
*/
async consolidate() {
const startTime = performance.now();
const result = {
removedDuplicates: 0,
contradictionsDetected: 0,
prunedPatterns: 0,
mergedPatterns: 0,
};
// 1. Deduplicate similar memories
result.removedDuplicates = await this.deduplicateMemories();
// 2. Detect contradictions
if (this.config.enableContradictionDetection) {
result.contradictionsDetected = await this.detectContradictions();
}
// 3. Prune old patterns
result.prunedPatterns = await this.pruneOldPatterns();
// 4. Merge similar patterns
result.mergedPatterns = await this.mergePatterns();
// Update stats
this.consolidationCount++;
this.totalConsolidationTime += performance.now() - startTime;
// Emit consolidation event
this.emitEvent({
type: 'memory_consolidated',
memoriesCount: this.memories.size,
});
return result;
}
// ==========================================================================
// Pattern Management
// ==========================================================================
/**
* Convert a distilled memory to a pattern
*/
memoryToPattern(memory) {
const pattern = {
patternId: `pat_${memory.memoryId}`,
name: this.generatePatternName(memory),
domain: this.inferDomain(memory),
embedding: memory.embedding,
strategy: memory.strategy,
successRate: memory.quality,
usageCount: memory.usageCount,
qualityHistory: [memory.quality],
evolutionHistory: [],
createdAt: Date.now(),
updatedAt: Date.now(),
};
this.patterns.set(pattern.patternId, pattern);
return pattern;
}
/**
* Evolve a pattern based on new experience
*/
evolvePattern(patternId, newExperience) {
const pattern = this.patterns.get(patternId);
if (!pattern)
return;
const previousQuality = pattern.successRate;
// Update quality history
pattern.qualityHistory.push(newExperience.qualityScore);
if (pattern.qualityHistory.length > 100) {
pattern.qualityHistory = pattern.qualityHistory.slice(-100);
}
// Update success rate
pattern.successRate = pattern.qualityHistory.reduce((a, b) => a + b, 0) /
pattern.qualityHistory.length;
pattern.usageCount++;
pattern.updatedAt = Date.now();
// Record evolution
const evolutionType = this.determineEvolutionType(previousQuality, pattern.successRate);
pattern.evolutionHistory.push({
timestamp: Date.now(),
type: evolutionType,
previousQuality,
newQuality: pattern.successRate,
description: `Updated based on trajectory ${newExperience.trajectoryId}`,
});
// Emit event
this.emitEvent({
type: 'pattern_evolved',
patternId,
evolutionType,
});
}
/**
* Get all patterns
*/
getPatterns() {
return Array.from(this.patterns.values());
}
/**
* Find patterns matching a query
*/
async findPatterns(queryEmbedding, k = 5) {
const results = [];
for (const pattern of this.patterns.values()) {
const score = this.cosineSimilarity(queryEmbedding, pattern.embedding);
results.push({ pattern, score });
}
results.sort((a, b) => b.score - a.score);
return results.slice(0, k).map(r => r.pattern);
}
// ==========================================================================
// Trajectory Management
// ==========================================================================
/**
* Store a trajectory
*/
storeTrajectory(trajectory) {
this.trajectories.set(trajectory.trajectoryId, trajectory);
// Prune if over capacity
if (this.trajectories.size > this.config.maxTrajectories) {
this.pruneTrajectories();
}
}
/**
* Get trajectory by ID
*/
getTrajectory(trajectoryId) {
return this.trajectories.get(trajectoryId);
}
/**
* Get all trajectories
*/
getTrajectories() {
return Array.from(this.trajectories.values());
}
/**
* Get successful trajectories
*/
getSuccessfulTrajectories() {
return Array.from(this.trajectories.values())
.filter(t => t.verdict?.success);
}
/**
* Get failed trajectories
*/
getFailedTrajectories() {
return Array.from(this.trajectories.values())
.filter(t => t.isComplete && !t.verdict?.success);
}
// ==========================================================================
// Statistics
// ==========================================================================
/**
* Get ReasoningBank statistics
*/
getStats() {
return {
trajectoryCount: this.trajectories.size,
memoryCount: this.memories.size,
patternCount: this.patterns.size,
avgRetrievalTimeMs: this.retrievalCount > 0
? this.totalRetrievalTime / this.retrievalCount
: 0,
avgDistillationTimeMs: this.distillationCount > 0
? this.totalDistillationTime / this.distillationCount
: 0,
avgJudgeTimeMs: this.judgeCount > 0
? this.totalJudgeTime / this.judgeCount
: 0,
avgConsolidationTimeMs: this.consolidationCount > 0
? this.totalConsolidationTime / this.consolidationCount
: 0,
consolidatedMemories: Array.from(this.memories.values())
.filter(e => e.consolidated).length,
successfulTrajectories: this.getSuccessfulTrajectories().length,
failedTrajectories: this.getFailedTrajectories().length,
agentdbEnabled: this.agentdbAvailable ? 1 : 0,
retrievalCount: this.retrievalCount,
distillationCount: this.distillationCount,
judgeCount: this.judgeCount,
consolidationCount: this.consolidationCount,
};
}
/**
* Get detailed metrics for hooks
*/
getDetailedMetrics() {
const successfulTrajectories = this.getSuccessfulTrajectories();
const allTrajectories = this.getTrajectories();
const successRate = allTrajectories.length > 0
? successfulTrajectories.length / allTrajectories.length
: 0;
// Extract domain-based routing stats
const domainStats = new Map();
for (const t of allTrajectories) {
const domain = t.domain || 'general';
const stats = domainStats.get(domain) || { count: 0, successes: 0 };
stats.count++;
if (t.verdict?.success)
stats.successes++;
domainStats.set(domain, stats);
}
const topAgents = Array.from(domainStats.entries())
.map(([agent, stats]) => ({
agent,
count: stats.count,
successRate: stats.count > 0 ? stats.successes / stats.count : 0,
}))
.sort((a, b) => b.count - a.count)
.slice(0, 5);
// Extract common patterns
const patternStrategies = Array.from(this.patterns.values())
.sort((a, b) => b.usageCount - a.usageCount)
.slice(0, 5)
.map(p => p.strategy);
return {
routing: {
totalRoutes: allTrajectories.length,
avgConfidence: successfulTrajectories.length > 0
? successfulTrajectories.reduce((sum, t) => sum + (t.verdict?.confidence || 0), 0) / successfulTrajectories.length
: 0,
topAgents,
},
edits: {
totalEdits: this.memories.size,
successRate,
commonPatterns: patternStrategies.slice(0, 4),
},
commands: {
totalCommands: this.distillationCount,
successRate,
avgExecutionTime: this.totalDistillationTime / Math.max(this.distillationCount, 1),
commonCommands: patternStrategies.slice(0, 4),
},
};
}
// ==========================================================================
// Event System
// ==========================================================================
addEventListener(listener) {
this.eventListeners.add(listener);
}
removeEventListener(listener) {
this.eventListeners.delete(listener);
}
emitEvent(event) {
for (const listener of this.eventListeners) {
try {
listener(event);
}
catch (error) {
console.error('Error in ReasoningBank event listener:', error);
}
}
}
// ==========================================================================
// AgentDB Integration Helpers
// ==========================================================================
/**
* Store memory in AgentDB for vector search
*/
async storeInAgentDB(memory) {
if (!this.agentdb)
return;
try {
if (typeof this.agentdb.store === 'function') {
await this.agentdb.store(memory.memoryId, {
content: memory.strategy,
embedding: memory.embedding,
metadata: {
trajectoryId: memory.trajectoryId,
quality: memory.quality,
keyLearnings: memory.keyLearnings,
usageCount: memory.usageCount,
lastUsed: memory.lastUsed,
},
});
}
}
catch (error) {
console.warn('Failed to store in AgentDB:', error);
}
}
/**
* Search using AgentDB HNSW index
*/
async searchWithAgentDB(queryEmbedding, k) {
if (!this.agentdb)
return [];
try {
if (typeof this.agentdb.search === 'function') {
return await this.agentdb.search(queryEmbedding, k);
}
// Try HNSW controller if available
const hnsw = this.agentdb.getController?.('hnsw');
if (hnsw) {
const results = await hnsw.search(queryEmbedding, k);
return results.map((r) => ({
id: String(r.id),
similarity: r.similarity || 1 - r.distance,
}));
}
}
catch {
// Fall through to return empty
}
return [];
}
/**
* Delete from AgentDB
*/
async deleteFromAgentDB(memoryId) {
if (!this.agentdb)
return;
try {
if (typeof this.agentdb.delete === 'function') {
await this.agentdb.delete(memoryId);
}
}
catch {
// Ignore deletion errors
}
}
// ==========================================================================
// Private Helper Methods
// ==========================================================================
cosineSimilarity(a, b) {
if (a.length !== b.length)
return 0;
let dot = 0, normA = 0, normB = 0;
for (let i = 0; i < a.length; i++) {
dot += a[i] * b[i];
normA += a[i] * a[i];
normB += b[i] * b[i];
}
const denom = Math.sqrt(normA) * Math.sqrt(normB);
// Clamp to [0, 1] to handle floating point precision issues
const similarity = denom > 0 ? dot / denom : 0;
return Math.max(0, Math.min(1, similarity));
}
computeContentSimilarity(query, content) {
const queryWords = new Set(query.toLowerCase().split(/\s+/));
const contentWords = content.toLowerCase().split(/\s+/);
let matches = 0;
for (const word of contentWords) {
if (queryWords.has(word))
matches++;
}
return contentWords.length > 0 ? matches / contentWords.length : 0;
}
computeMaxSimilarity(entry, selected) {
let maxSim = 0;
for (const sel of selected) {
const sim = this.cosineSimilarity(entry.memory.embedding, sel.memory.embedding);
maxSim = Math.max(maxSim, sim);
}
return maxSim;
}
analyzeSteps(steps) {
const rewardSum = steps.reduce((s, step) => s + step.reward, 0);
const positiveSteps = steps.filter(s => s.reward > 0.5).length;
return {
totalSteps: steps.length,
avgReward: steps.length > 0 ? rewardSum / steps.length : 0,
positiveRatio: steps.length > 0 ? positiveSteps / steps.length : 0,
trajectory: steps.length > 1
? (steps[steps.length - 1].reward - steps[0].reward)
: 0,
};
}
identifyStrengths(trajectory, analysis) {
const strengths = [];
if (analysis.avgReward > 0.7) {
strengths.push('High average reward across steps');
}
if (analysis.trajectory > 0.2) {
strengths.push('Positive reward trajectory');
}
if (trajectory.qualityScore > 0.8) {
strengths.push('High overall quality');
}
if (analysis.totalSteps < 5 && trajectory.qualityScore > 0.6) {
strengths.push('Efficient solution (few steps)');
}
return strengths;
}
identifyWeaknesses(trajectory, analysis) {
const weaknesses = [];
if (analysis.avgReward < 0.4) {
weaknesses.push('Low average reward');
}
if (analysis.trajectory < -0.1) {
weaknesses.push('Declining reward trajectory');
}
if (analysis.positiveRatio < 0.5) {
weaknesses.push('Many negative/neutral steps');
}
if (analysis.totalSteps > 10 && trajectory.qualityScore < 0.7) {
weaknesses.push('Long trajectory with mediocre outcome');
}
return weaknesses;
}
generateImprovements(weaknesses) {
const improvements = [];
for (const weakness of weaknesses) {
if (weakness.includes('Low average reward')) {
improvements.push('Consider alternative strategies for each step');
}
if (weakness.includes('Declining')) {
improvements.push('Re-evaluate approach when reward decreases');
}
if (weakness.includes('negative/neutral')) {
improvements.push('Focus on steps with clearer positive signals');
}
if (weakness.includes('Long trajectory')) {
improvements.push('Look for shortcuts or more direct approaches');
}
}
return improvements;
}
computeRelevanceScore(trajectory) {
// Base relevance on quality and recency
const qualityFactor = trajectory.qualityScore;
const ageDays = (Date.now() - trajectory.startTime) / (1000 * 60 * 60 * 24);
const recencyFactor = Math.exp(-ageDays / 30); // Decay over 30 days
return qualityFactor * 0.7 + recencyFactor * 0.3;
}
computeConfidence(trajectory, analysis) {
// More steps = more confidence
const stepFactor = Math.min(analysis.totalSteps / 10, 1);
// Consistent rewards = more confidence
const consistencyFactor = analysis.positiveRatio;
// Clear outcome = more confidence
const outcomeFactor = Math.abs(trajectory.qualityScore - 0.5) * 2;
return (stepFactor * 0.3 + consistencyFactor * 0.4 + outcomeFactor * 0.3);
}
extractStrategy(trajectory) {
const actions = trajectory.steps.map(s => s.action);
const uniqueActions = [...new Set(actions)];
if (uniqueActions.length <= 3) {
return `Apply ${uniqueActions.join(' -> ')}`;
}
return `Multi-step approach: ${uniqueActions.slice(0, 3).join(', ')}...`;
}
extractKeyLearnings(trajectory) {
const learnings = [];
const verdict = trajectory.verdict;
// Add key success factors
if (verdict.success) {
learnings.push(`Successful approach for ${trajectory.domain} domain`);
for (const strength of verdict.strengths.slice(0, 2)) {
learnings.push(`Strength: ${strength}`);
}
}
else {
learnings.push(`Approach needs refinement`);
for (const improvement of verdict.improvements.slice(0, 2)) {
learnings.push(`Improvement: ${improvement}`);
}
}
return learnings;
}
computeAggregateEmbedding(trajectory) {
if (trajectory.steps.length === 0) {
return new Float32Array(this.config.vectorDimension);
}
const dim = trajectory.steps[0].stateAfter.length;
const aggregate = new Float32Array(dim);
// Weighted average of step embeddings (higher weight for later steps)
let totalWeight = 0;
for (let i = 0; i < trajectory.steps.length; i++) {
const weight = (i + 1) / trajectory.steps.length;
totalWeight += weight;
const step = trajectory.steps[i];
for (let j = 0; j < dim; j++) {
aggregate[j] += step.stateAfter[j] * weight;
}
}
// Normalize
for (let j = 0; j < dim; j++) {
aggregate[j] /= totalWeight;
}
return aggregate;
}
async deduplicateMemories() {
let removed = 0;
const entries = Array.from(this.memories.entries());
for (let i = 0; i < entries.length; i++) {
for (let j = i + 1; j < entries.length; j++) {
const sim = this.cosineSimilarity(entries[i][1].memory.embedding, entries[j][1].memory.embedding);
if (sim > this.config.dedupThreshold) {
// Keep the higher quality one
if (entries[i][1].memory.quality >= entries[j][1].memory.quality) {
this.memories.delete(entries[j][0]);
await this.deleteFromAgentDB(entries[j][0]);
}
else {
this.memories.delete(entries[i][0]);
await this.deleteFromAgentDB(entries[i][0]);
}
removed++;
}
}
}
return removed;
}
async detectContradictions() {
let contradictions = 0;
const entries = Array.from(this.memories.values());
for (let i = 0; i < entries.length; i++) {
for (let j = i + 1; j < entries.length; j++) {
// Similar context but opposite outcomes
const sim = this.cosineSimilarity(entries[i].memory.embedding, entries[j].memory.embedding);
if (sim > 0.8) {
const qualityDiff = Math.abs(entries[i].memory.quality - entries[j].memory.quality);
if (qualityDiff > 0.4) {
contradictions++;
// Mark lower quality as consolidated (to exclude from retrieval)
if (entries[i].memory.quality < entries[j].memory.quality) {
entries[i].consolidated = true;
}
else {
entries[j].consolidated = true;
}
}
}
}
}
return contradictions;
}
async pruneOldPatterns() {
const now = Date.now();
const maxAge = this.config.maxPatternAgeDays * 24 * 60 * 60 * 1000;
let pruned = 0;
for (const [id, pattern] of this.patterns) {
const age = now - pattern.updatedAt;
if (age > maxAge && pattern.usageCount < 5) {
this.patterns.delete(id);
pruned++;
}
}
return pruned;
}
async mergePatterns() {
let merged = 0;
const patterns = Array.from(this.patterns.entries());
for (let i = 0; i < patterns.length; i++) {
for (let j = i + 1; j < patterns.length; j++) {
const sim = this.cosineSimilarity(patterns[i][1].embedding, patterns[j][1].embedding);
if (sim > 0.9 && patterns[i][1].domain === patterns[j][1].domain) {
// Merge into higher quality pattern
const [keepId, keep] = patterns[i][1].successRate >= patterns[j][1].successRate
? patterns[i]
: patterns[j];
const [removeId, remove] = patterns[i][1].successRate < patterns[j][1].successRate
? patterns[i]
: patterns[j];
// Combine statistics
keep.usageCount += remove.usageCount;
keep.qualityHistory.push(...remove.qualityHistory);
keep.evolutionHistory.push({
timestamp: Date.now(),
type: 'merge',
previousQuality: keep.successRate,
newQuality: (keep.successRate + remove.successRate) / 2,
description: `Merged with pattern ${removeId}`,
});
this.patterns.delete(removeId);
merged++;
}
}
}
return merged;
}
pruneTrajectories() {
const entries = Array.from(this.trajectories.entries())
.sort((a, b) => a[1].qualityScore - b[1].qualityScore);
const toRemove = entries.length - Math.floor(this.config.maxTrajectories * 0.8);
for (let i = 0; i < toRemove && i < entries.length; i++) {
this.trajectories.delete(entries[i][0]);
}
}
generatePatternName(memory) {
const words = memory.strategy.split(' ').slice(0, 4);
return words.join('_').toLowerCase().replace(/[^a-z0-9_]/g, '');
}
inferDomain(memory) {
// First check if we have the trajectory directly in our store
const trajectory = this.trajectories.get(memory.trajectoryId);
if (trajectory?.domain) {
return trajectory.domain;
}
// Check if the memory entry has the trajectory with domain info
const memoryEntry = this.memories.get(memory.memoryId);
if (memoryEntry?.trajectory?.domain) {
return memoryEntry.trajectory.domain;
}
return 'general';
}
determineEvolutionType(prev, curr) {
const delta = curr - prev;
if (delta > 0.05)
return 'improvement';
if (delta < -0.1)
return 'prune';
return 'improvement';
}
/**
* Check if AgentDB is available and initialized
*/
isAgentDBAvailable() {
return this.agentdbAvailable;
}
}
// ============================================================================
// Factory Function
// ============================================================================
/**
* Factory function for creating ReasoningBank
*/
export function createReasoningBank(config) {
return new ReasoningBank(config);
}
/**
* Create and initialize a ReasoningBank instance
*/
export async function createInitializedReasoningBank(config) {
const bank = new ReasoningBank(config);
await bank.initialize();
return bank;
}
//# sourceMappingURL=reasoning-bank.js.map