File size: 8,019 Bytes
de07704 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 | // ==========================================
// MEEZAN - ميزان Knowledge Gap Tracker
// Identifies what Meezan doesn't know well
// and prioritizes what to learn next
// ==========================================
import * as fs from 'fs';
import * as path from 'path';
import { KnowledgeGap } from '../types';
// Topic patterns to detect knowledge domains
const TOPIC_PATTERNS: Array<{
topic: string;
topicAr: string;
category: KnowledgeGap['category'];
patterns: string[];
}> = [
// Fiqh topics
{ topic: 'Inheritance (Faraidh)', topicAr: 'الميراث والفرائض', category: 'fiqh',
patterns: ['ميراث', 'فرائض', 'وصية', 'inheritance', 'faraidh', 'تركة'] },
{ topic: 'Islamic Marriage (Nikah)', topicAr: 'النكاح والزواج', category: 'fiqh',
patterns: ['نكاح', 'زواج', 'مهر', 'طلاق', 'marriage', 'nikah', 'divorce', 'khul'] },
{ topic: 'Halal Food Details', topicAr: 'تفاصيل الأطعمة الحلال', category: 'fiqh',
patterns: ['ذبح', 'تذكية', 'مستفسر', 'slaughter', 'zabiha', 'gelatin', 'rennet'] },
{ topic: 'Business Ethics in Islam', topicAr: 'أخلاقيات التجارة الإسلامية', category: 'fiqh',
patterns: ['غش', 'تدليس', 'عقد', 'بيع', 'شراء', 'fraud', 'contract', 'gharar'] },
// Islamic Finance
{ topic: 'Sukuk Details', topicAr: 'تفاصيل الصكوك', category: 'finance',
patterns: ['صكوك', 'sukuk', 'islamic bond', 'صك'] },
{ topic: 'Islamic Insurance (Takaful)', topicAr: 'التأمين الإسلامي', category: 'finance',
patterns: ['تكافل', 'تأمين', 'takaful', 'insurance'] },
{ topic: 'Cryptocurrency in Islam', topicAr: 'العملات الرقمية في الإسلام', category: 'finance',
patterns: ['بيتكوين', 'كريبتو', 'عملة رقمية', 'bitcoin', 'crypto', 'nft', 'defi', 'web3'] },
{ topic: 'Kuwait Stock Exchange', topicAr: 'بورصة الكويت', category: 'finance',
patterns: ['بورصة الكويت', 'boursa kuwait', 'كويت للأسهم', 'kuwaitstock', 'tadawul'] },
// Kuwait Government
{ topic: 'Kuwait Visa Process', topicAr: 'إجراءات التأشيرة الكويتية', category: 'government',
patterns: ['تأشيرة', 'visa', 'iqama', 'إقامة', 'residence permit', 'work permit'] },
{ topic: 'Kuwait Labor Law', topicAr: 'قانون العمل الكويتي', category: 'government',
patterns: ['عمالة', 'labor', 'employment', 'عقد عمل', 'راتب', 'فصل', 'resignation'] },
{ topic: 'Kuwait VAT / Tax', topicAr: 'الضرائب الكويتية', category: 'government',
patterns: ['ضريبة', 'vat', 'tax', 'زكاة الشركات', 'corporate tax'] },
// Culture
{ topic: 'Ramadan Practices', topicAr: 'أحكام رمضان', category: 'culture',
patterns: ['رمضان', 'صيام', 'إفطار', 'سحور', 'fasting', 'ramadan', 'iftar', 'suhoor'] },
{ topic: 'Hajj and Umrah', topicAr: 'الحج والعمرة', category: 'culture',
patterns: ['حج', 'عمرة', 'مكة', 'hajj', 'umrah', 'mecca', 'ihram'] },
{ topic: 'GCC Cultural Etiquette', topicAr: 'الآداب الخليجية', category: 'culture',
patterns: ['عادات', 'تقاليد', 'آداب', 'culture', 'etiquette', 'customs', 'خليج'] },
];
export class KnowledgeGapTracker {
private dbPath: string;
private gaps: Map<string, KnowledgeGap>;
constructor(dbPath: string = './data/knowledge_gaps.json') {
this.dbPath = dbPath;
this.gaps = new Map();
this.load();
}
/**
* Analyze a conversation exchange and detect potential knowledge gaps
*/
analyzeExchange(
userMessage: string,
assistantResponse: string,
wasConfused: boolean,
wasNegative: boolean
): KnowledgeGap[] {
const detectedGaps: KnowledgeGap[] = [];
if (!wasConfused && !wasNegative) return detectedGaps;
const combined = (userMessage + ' ' + assistantResponse).toLowerCase();
for (const topicDef of TOPIC_PATTERNS) {
const matches = topicDef.patterns.filter(p => combined.includes(p.toLowerCase()));
if (matches.length > 0) {
const gapId = `gap_${topicDef.topic.replace(/\s+/g, '_').toLowerCase()}`;
const existing = this.gaps.get(gapId);
if (existing) {
// Increment frequency
existing.frequency++;
existing.lastSeen = new Date().toISOString();
if (!existing.exampleQuestions.includes(userMessage)) {
existing.exampleQuestions = [...existing.exampleQuestions.slice(-4), userMessage];
}
// Upgrade priority if frequent enough
if (existing.frequency >= 10 && existing.priority !== 'high') {
existing.priority = 'high';
} else if (existing.frequency >= 5 && existing.priority === 'low') {
existing.priority = 'medium';
}
this.gaps.set(gapId, existing);
detectedGaps.push(existing);
} else {
// New gap detected
const newGap: KnowledgeGap = {
id: gapId,
topic: topicDef.topic,
topicAr: topicDef.topicAr,
category: topicDef.category,
frequency: 1,
firstSeen: new Date().toISOString(),
lastSeen: new Date().toISOString(),
exampleQuestions: [userMessage],
status: 'identified',
priority: 'low'
};
this.gaps.set(gapId, newGap);
detectedGaps.push(newGap);
}
}
}
if (detectedGaps.length > 0) this.save();
return detectedGaps;
}
/**
* Get top priority knowledge gaps to address
*/
getTopGaps(limit: number = 5): KnowledgeGap[] {
const allGaps = Array.from(this.gaps.values())
.filter(g => g.status !== 'resolved');
// Sort by priority then frequency
const priorityScore = (g: KnowledgeGap) => {
const p = g.priority === 'high' ? 3 : g.priority === 'medium' ? 2 : 1;
return p * 100 + g.frequency;
};
return allGaps
.sort((a, b) => priorityScore(b) - priorityScore(a))
.slice(0, limit);
}
/**
* Mark a gap as resolved with a resolution note
*/
resolveGap(gapId: string, resolution: string): void {
const gap = this.gaps.get(gapId);
if (gap) {
gap.status = 'resolved';
gap.resolution = resolution;
this.gaps.set(gapId, gap);
this.save();
}
}
/**
* Get summary stats for the learning dashboard
*/
getStats(): {
total: number;
high: number;
medium: number;
low: number;
resolved: number;
topTopics: string[];
} {
const all = Array.from(this.gaps.values());
return {
total: all.length,
high: all.filter(g => g.priority === 'high' && g.status !== 'resolved').length,
medium: all.filter(g => g.priority === 'medium' && g.status !== 'resolved').length,
low: all.filter(g => g.priority === 'low' && g.status !== 'resolved').length,
resolved: all.filter(g => g.status === 'resolved').length,
topTopics: this.getTopGaps(3).map(g => g.topicAr)
};
}
getAllGaps(): KnowledgeGap[] {
return Array.from(this.gaps.values());
}
private load(): void {
const dir = path.dirname(this.dbPath);
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
if (fs.existsSync(this.dbPath)) {
try {
const data = JSON.parse(fs.readFileSync(this.dbPath, 'utf-8'));
for (const gap of data) {
this.gaps.set(gap.id, gap);
}
} catch { /* start fresh */ }
}
}
private save(): void {
const data = Array.from(this.gaps.values());
fs.writeFileSync(this.dbPath, JSON.stringify(data, null, 2), 'utf-8');
}
}
export const knowledgeGapTracker = new KnowledgeGapTracker(
process.env.DB_PATH?.replace('memory.json', 'knowledge_gaps.json') || './data/knowledge_gaps.json'
);
|