306 lines
12 KiB
JavaScript
306 lines
12 KiB
JavaScript
/**
|
|
* Hybrid ReasoningBank Backend - Full Implementation for v1.7.1
|
|
*
|
|
* Combines Rust WASM (compute) + AgentDB TypeScript (storage) for optimal performance:
|
|
* - WASM: 10x faster similarity computation
|
|
* - AgentDB: Persistent SQLite storage with frontier memory
|
|
* - CausalRecall: Utility-based reranking with causal uplift
|
|
* - Automatic backend selection based on task requirements
|
|
*
|
|
* @example
|
|
* ```typescript
|
|
* import { HybridReasoningBank } from 'agentic-flow/reasoningbank';
|
|
*
|
|
* const rb = new HybridReasoningBank({ preferWasm: true });
|
|
* await rb.storePattern({ task: '...', success: true, reward: 0.95 });
|
|
* const patterns = await rb.retrievePatterns('similar task', { k: 5 });
|
|
* const strategy = await rb.learnStrategy('API optimization');
|
|
* ```
|
|
*/
|
|
import { SharedMemoryPool } from '../memory/SharedMemoryPool.js';
|
|
import { ReflexionMemory } from 'agentdb/controllers/ReflexionMemory';
|
|
import { SkillLibrary } from 'agentdb/controllers/SkillLibrary';
|
|
import { CausalRecall } from 'agentdb/controllers/CausalRecall';
|
|
import { CausalMemoryGraph } from 'agentdb/controllers/CausalMemoryGraph';
|
|
export class HybridReasoningBank {
|
|
memory;
|
|
reflexion;
|
|
skills;
|
|
causalRecall;
|
|
causalGraph;
|
|
useWasm;
|
|
wasmModule;
|
|
constructor(options = {}) {
|
|
this.memory = SharedMemoryPool.getInstance();
|
|
const db = this.memory.getDatabase();
|
|
const embedder = this.memory.getEmbedder();
|
|
this.reflexion = new ReflexionMemory(db, embedder);
|
|
this.skills = new SkillLibrary(db, embedder);
|
|
this.causalGraph = new CausalMemoryGraph(db);
|
|
// CausalRecall with optimized rerank config
|
|
this.causalRecall = new CausalRecall(db, embedder, {
|
|
alpha: 0.6, // 60% weight on similarity
|
|
beta: 0.3, // 30% weight on causal uplift
|
|
gamma: 0.1, // 10% penalty for latency
|
|
minConfidence: 0.7
|
|
});
|
|
this.useWasm = options.preferWasm ?? true;
|
|
this.wasmModule = null;
|
|
// Try to load WASM module
|
|
if (this.useWasm) {
|
|
this.loadWasmModule().catch(err => {
|
|
console.warn('[HybridReasoningBank] WASM unavailable, using TypeScript:', err.message);
|
|
this.useWasm = false;
|
|
});
|
|
}
|
|
}
|
|
async loadWasmModule() {
|
|
try {
|
|
// Dynamic import for WASM module
|
|
const wasm = await import('../../wasm/reasoningbank/reasoningbank_wasm.js');
|
|
this.wasmModule = wasm;
|
|
console.log('[HybridReasoningBank] WASM module loaded successfully');
|
|
}
|
|
catch (error) {
|
|
throw new Error(`WASM load failed: ${error}`);
|
|
}
|
|
}
|
|
/**
|
|
* Store a reasoning pattern
|
|
*/
|
|
async storePattern(pattern) {
|
|
const episodeId = await this.reflexion.storeEpisode(pattern);
|
|
// Store causal edge if action led to outcome
|
|
if (pattern.input && pattern.output && pattern.success) {
|
|
try {
|
|
this.causalGraph.addCausalEdge({
|
|
fromMemoryId: episodeId,
|
|
fromMemoryType: 'episode',
|
|
toMemoryId: episodeId + 1, // Next episode
|
|
toMemoryType: 'episode',
|
|
similarity: pattern.reward,
|
|
uplift: pattern.success ? pattern.reward : -pattern.reward,
|
|
confidence: 0.8,
|
|
sampleSize: 1,
|
|
metadata: {
|
|
sessionId: pattern.sessionId,
|
|
task: pattern.task
|
|
}
|
|
});
|
|
}
|
|
catch (error) {
|
|
console.warn('[HybridReasoningBank] Failed to record causal edge:', error);
|
|
}
|
|
}
|
|
return episodeId;
|
|
}
|
|
/**
|
|
* Retrieve similar patterns with optional WASM acceleration
|
|
*/
|
|
async retrievePatterns(query, options = {}) {
|
|
const { k = 5, minReward, onlySuccesses, onlyFailures } = options;
|
|
// Check cache first
|
|
const cacheKey = `retrieve:${query}:${k}:${onlySuccesses}:${onlyFailures}`;
|
|
const cached = this.memory.getCachedQuery(cacheKey);
|
|
if (cached)
|
|
return cached;
|
|
// Use CausalRecall for intelligent retrieval with utility-based ranking
|
|
try {
|
|
const result = await this.causalRecall.recall(`query-${Date.now()}`, query, k, undefined, // requirements
|
|
'public' // accessLevel
|
|
);
|
|
// Convert candidates to pattern format and filter
|
|
let patterns = result.candidates.map(c => ({
|
|
task: c.content,
|
|
similarity: c.similarity,
|
|
uplift: c.uplift || 0,
|
|
utilityScore: c.utilityScore,
|
|
type: c.type,
|
|
id: c.id
|
|
}));
|
|
// Apply filters
|
|
if (minReward !== undefined) {
|
|
patterns = patterns.filter(p => (p.uplift || 0) >= minReward);
|
|
}
|
|
// Cache and return
|
|
this.memory.cacheQuery(cacheKey, patterns, 60000);
|
|
return patterns;
|
|
}
|
|
catch (error) {
|
|
console.warn('[HybridReasoningBank] CausalRecall failed, falling back to ReflexionMemory:', error);
|
|
// Fallback to basic ReflexionMemory
|
|
const results = await this.reflexion.retrieveRelevant({
|
|
task: query,
|
|
k,
|
|
minReward,
|
|
onlySuccesses,
|
|
onlyFailures
|
|
});
|
|
this.memory.cacheQuery(cacheKey, results, 60000);
|
|
return results;
|
|
}
|
|
}
|
|
/**
|
|
* Learn optimal strategy for a task
|
|
*
|
|
* Combines pattern retrieval with causal analysis to provide evidence-based recommendations
|
|
*/
|
|
async learnStrategy(task) {
|
|
// Get successful patterns
|
|
const patterns = await this.retrievePatterns(task, { k: 10, onlySuccesses: true });
|
|
// Get causal effects for this task type
|
|
let causalData;
|
|
try {
|
|
// Note: queryCausalEffects requires specific memory IDs
|
|
// For task-level analysis, we'll use pattern success rates instead
|
|
const stats = await this.reflexion.getTaskStats(task, 30);
|
|
if (stats.totalAttempts > 0) {
|
|
causalData = {
|
|
action: task,
|
|
avgReward: stats.avgReward || 0,
|
|
avgUplift: stats.improvementTrend || 0,
|
|
confidence: Math.min(stats.totalAttempts / 10, 1.0),
|
|
evidenceCount: stats.totalAttempts,
|
|
recommendation: (stats.improvementTrend || 0) > 0.1 ? 'DO_IT' :
|
|
(stats.improvementTrend || 0) < -0.1 ? 'AVOID' : 'NEUTRAL'
|
|
};
|
|
}
|
|
}
|
|
catch (error) {
|
|
console.warn('[HybridReasoningBank] Causal analysis failed:', error);
|
|
}
|
|
// Fallback if no causal data
|
|
if (!causalData) {
|
|
causalData = {
|
|
action: task,
|
|
avgReward: patterns.length > 0 ? (patterns[0].reward || 0) : 0,
|
|
avgUplift: 0,
|
|
confidence: patterns.length > 0 ? 0.6 : 0.3,
|
|
evidenceCount: patterns.length,
|
|
recommendation: patterns.length > 0 ? 'DO_IT' : 'NEUTRAL'
|
|
};
|
|
}
|
|
// Calculate overall confidence
|
|
const patternConf = Math.min(patterns.length / 10, 1.0); // 10+ patterns = full confidence
|
|
const causalConf = causalData.confidence;
|
|
const confidence = 0.6 * patternConf + 0.4 * causalConf;
|
|
// Generate recommendation
|
|
let recommendation = '';
|
|
if (confidence > 0.8 && causalData.avgUplift > 0.1) {
|
|
recommendation = `Strong evidence for success (${patterns.length} patterns, +${(causalData.avgUplift * 100).toFixed(1)}% uplift)`;
|
|
}
|
|
else if (confidence > 0.5) {
|
|
recommendation = `Moderate evidence (${patterns.length} patterns available)`;
|
|
}
|
|
else {
|
|
recommendation = `Limited evidence - proceed with caution`;
|
|
}
|
|
return {
|
|
patterns,
|
|
causality: causalData,
|
|
confidence,
|
|
recommendation
|
|
};
|
|
}
|
|
/**
|
|
* Auto-consolidate patterns into skills
|
|
*/
|
|
async autoConsolidate(minUses = 3, minSuccessRate = 0.7, lookbackDays = 30) {
|
|
// Get task statistics
|
|
const stats = await this.reflexion.getTaskStats('', lookbackDays);
|
|
if (stats.totalAttempts < minUses || stats.successRate < minSuccessRate) {
|
|
return { skillsCreated: 0 };
|
|
}
|
|
// Get successful episodes for consolidation
|
|
const episodes = await this.reflexion.retrieveRelevant({
|
|
task: '',
|
|
k: 50,
|
|
onlySuccesses: true,
|
|
timeWindowDays: lookbackDays
|
|
});
|
|
// Group by task type and consolidate
|
|
const taskGroups = new Map();
|
|
episodes.forEach(ep => {
|
|
const group = taskGroups.get(ep.task) || [];
|
|
group.push(ep);
|
|
taskGroups.set(ep.task, group);
|
|
});
|
|
let skillsCreated = 0;
|
|
for (const [task, group] of taskGroups) {
|
|
if (group.length >= minUses) {
|
|
const successRate = group.filter(e => e.success).length / group.length;
|
|
if (successRate >= minSuccessRate) {
|
|
await this.skills.createSkill({
|
|
name: `skill_${task.replace(/\s+/g, '_').toLowerCase()}`,
|
|
description: `Consolidated from ${group.length} successful episodes`,
|
|
signature: { inputs: {}, outputs: {} },
|
|
successRate,
|
|
uses: group.length,
|
|
avgReward: group.reduce((sum, e) => sum + e.reward, 0) / group.length,
|
|
avgLatencyMs: group.reduce((sum, e) => sum + (e.latencyMs || 0), 0) / group.length,
|
|
metadata: { consolidatedAt: Date.now(), taskType: task }
|
|
});
|
|
skillsCreated++;
|
|
}
|
|
}
|
|
}
|
|
return { skillsCreated };
|
|
}
|
|
/**
|
|
* What-if causal analysis
|
|
*/
|
|
async whatIfAnalysis(action) {
|
|
try {
|
|
// Use task statistics for what-if analysis
|
|
const stats = await this.reflexion.getTaskStats(action, 30);
|
|
if (stats.totalAttempts === 0) {
|
|
return {
|
|
action,
|
|
avgReward: 0,
|
|
avgUplift: 0,
|
|
confidence: 0,
|
|
evidenceCount: 0,
|
|
recommendation: 'NEUTRAL'
|
|
};
|
|
}
|
|
const avgUplift = stats.improvementTrend || 0;
|
|
const confidence = Math.min(stats.totalAttempts / 10, 1.0);
|
|
return {
|
|
action,
|
|
avgReward: stats.avgReward || 0,
|
|
avgUplift,
|
|
confidence,
|
|
evidenceCount: stats.totalAttempts,
|
|
recommendation: avgUplift > 0.1 ? 'DO_IT' : avgUplift < -0.1 ? 'AVOID' : 'NEUTRAL'
|
|
};
|
|
}
|
|
catch (error) {
|
|
console.error('[HybridReasoningBank] What-if analysis failed:', error);
|
|
return {
|
|
action,
|
|
avgReward: 0,
|
|
avgUplift: 0,
|
|
confidence: 0,
|
|
evidenceCount: 0,
|
|
recommendation: 'NEUTRAL'
|
|
};
|
|
}
|
|
}
|
|
/**
|
|
* Search for relevant skills
|
|
*/
|
|
async searchSkills(taskType, k = 5) {
|
|
return this.skills.searchSkills({ task: taskType, k, minSuccessRate: 0.5 });
|
|
}
|
|
/**
|
|
* Get statistics
|
|
*/
|
|
getStats() {
|
|
return {
|
|
causalRecall: this.causalRecall.getStats(),
|
|
reflexion: {}, // ReflexionMemory doesn't expose global stats
|
|
skills: 0 // Would need to query database
|
|
};
|
|
}
|
|
}
|
|
//# sourceMappingURL=HybridBackend.js.map
|