Skip to main content

microscope_memory/
knowledge_base.rs

1//! Knowledge Base — a memóriában tárolt architektúrákból, szimulációs eredményekből
2//! és döntési mintákból épített kereshető tudásbázis.
3//!
4//! Ez a modul folyamatosan tanul a szimulációkból, döntésekből és eureka pillanatokból,
5//! és strukturált tudást biztosít a heurisztikus döntéshozó számára.
6
7use std::collections::HashMap;
8use std::sync::{Arc, RwLock};
9use std::time::{SystemTime, UNIX_EPOCH};
10
11use crate::architecture_simulator::{Architecture, SimulationMetrics};
12use crate::heuristic_decision::{DecisionLogEntry, HeuristicPattern};
13
14// ─── Alap típusok ───────────────────────────────────────────────────────────
15
16/// Egy tudásbázis bejegyzés típusa
17#[derive(Debug, Clone, PartialEq, Eq, Hash)]
18pub enum KnowledgeEntryType {
19    /// Architektúra minta
20    ArchitecturePattern,
21    /// Szimulációs eredmény
22    SimulationResult,
23    /// Döntési minta
24    DecisionPattern,
25    /// Eureka/Insight
26    Insight,
27    /// Heurisztikus szabály
28    HeuristicRule,
29    /// Legjobb gyakorlat
30    BestPractice,
31    /// Ismert hiba / buktató
32    KnownPitfall,
33    /// Egyedi
34    Custom(String),
35}
36
37/// Egy tudásbázis bejegyzés
38#[derive(Debug, Clone)]
39pub struct KnowledgeEntry {
40    pub id: String,
41    pub entry_type: KnowledgeEntryType,
42    pub title: String,
43    pub description: String,
44    pub tags: Vec<String>,
45    /// Kapcsolódó bejegyzések ID-i
46    pub related_entries: Vec<String>,
47    /// Megbízhatósági pontszám (0.0 - 1.0)
48    pub confidence: f64,
49    /// Hányszor volt hasznos
50    pub usefulness: u64,
51    /// Létrehozás időpontja
52    pub created_at: u64,
53    /// Utolsó módosítás
54    pub updated_at: u64,
55    /// Forrás (pl. melyik architektúrából, döntésből származik)
56    pub source: String,
57    /// Kontextus (milyen körülmények között érvényes)
58    pub context: String,
59}
60
61/// Keresési eredmény
62#[derive(Debug, Clone)]
63pub struct SearchResult {
64    pub entry: KnowledgeEntry,
65    pub relevance_score: f64,
66    pub matched_tags: Vec<String>,
67}
68
69/// Tudásbázis statisztikák
70#[derive(Debug, Clone)]
71pub struct KnowledgeBaseStats {
72    pub total_entries: u64,
73    pub architecture_patterns: u64,
74    pub simulation_results: u64,
75    pub decision_patterns: u64,
76    pub insights: u64,
77    pub heuristic_rules: u64,
78    pub best_practices: u64,
79    pub known_pitfalls: u64,
80    pub avg_confidence: f64,
81    pub total_usefulness: u64,
82}
83
84// ─── Tudásbázis ─────────────────────────────────────────────────────────────
85
86/// A fő tudásbázis rendszer
87pub struct KnowledgeBase {
88    entries: Arc<RwLock<HashMap<String, KnowledgeEntry>>>,
89    tag_index: Arc<RwLock<HashMap<String, Vec<String>>>>, // tag -> entry_ids
90    type_index: Arc<RwLock<HashMap<KnowledgeEntryType, Vec<String>>>>, // type -> entry_ids
91    /// Tanult asszociációk (entry_id -> related_entry_id -> weight)
92    associations: Arc<RwLock<HashMap<String, HashMap<String, f64>>>>,
93    #[allow(dead_code)]
94    next_id: Arc<RwLock<u64>>,
95}
96
97impl Default for KnowledgeBase {
98    fn default() -> Self {
99        Self::new()
100    }
101}
102
103impl KnowledgeBase {
104    /// Létrehoz egy új tudásbázist
105    pub fn new() -> Self {
106        Self {
107            entries: Arc::new(RwLock::new(HashMap::new())),
108            tag_index: Arc::new(RwLock::new(HashMap::new())),
109            type_index: Arc::new(RwLock::new(HashMap::new())),
110            associations: Arc::new(RwLock::new(HashMap::new())),
111            next_id: Arc::new(RwLock::new(1)),
112        }
113    }
114
115    /// Hozzáad egy új tudásbázis bejegyzést
116    pub fn add_entry(&self, entry: KnowledgeEntry) -> String {
117        let mut entries = crate::sync_guard::write_lock(&self.entries);
118        let mut tag_index = crate::sync_guard::write_lock(&self.tag_index);
119        let mut type_index = crate::sync_guard::write_lock(&self.type_index);
120
121        let id = entry.id.clone();
122        entries.insert(id.clone(), entry.clone());
123
124        // Indexelés tagek szerint
125        for tag in &entry.tags {
126            tag_index.entry(tag.clone()).or_default().push(id.clone());
127        }
128
129        // Indexelés típus szerint
130        type_index
131            .entry(entry.entry_type.clone())
132            .or_default()
133            .push(id.clone());
134
135        id
136    }
137
138    /// Lekér egy bejegyzést ID alapján
139    pub fn get_entry(&self, id: &str) -> Option<KnowledgeEntry> {
140        let entries = crate::sync_guard::read_lock(&self.entries);
141        entries.get(id).cloned()
142    }
143
144    /// Keresés a tudásbázisban
145    pub fn search(&self, query: &str, max_results: usize) -> Vec<SearchResult> {
146        let entries = crate::sync_guard::read_lock(&self.entries);
147        let query_lower = query.to_lowercase();
148        let query_words: Vec<&str> = query_lower.split_whitespace().collect();
149
150        let mut scored: Vec<(f64, &KnowledgeEntry, Vec<String>)> = entries
151            .values()
152            .map(|entry| {
153                let mut score = 0.0;
154
155                // Cím egyezés
156                if entry.title.to_lowercase().contains(&query_lower) {
157                    score += 3.0;
158                }
159
160                // Leírás egyezés
161                if entry.description.to_lowercase().contains(&query_lower) {
162                    score += 2.0;
163                }
164
165                // Tag egyezés
166                let matched_tags: Vec<String> = entry
167                    .tags
168                    .iter()
169                    .filter(|tag| query_words.iter().any(|w| tag.to_lowercase().contains(w)))
170                    .cloned()
171                    .collect();
172                score += matched_tags.len() as f64 * 1.5;
173
174                // Kontextus egyezés
175                if entry.context.to_lowercase().contains(&query_lower) {
176                    score += 1.0;
177                }
178
179                // Hasznosság bónusz
180                score += (entry.usefulness as f64).ln_1p() * 0.5;
181
182                // Megbízhatóság bónusz
183                score += entry.confidence * 0.5;
184
185                (score, entry, matched_tags)
186            })
187            .filter(|(score, _, _)| *score > 0.0)
188            .collect();
189
190        // Rendezés pontszám szerint
191        scored.sort_by(|a, b| b.0.total_cmp(&a.0));
192
193        scored
194            .into_iter()
195            .take(max_results)
196            .map(|(score, entry, matched_tags)| SearchResult {
197                entry: entry.clone(),
198                relevance_score: score,
199                matched_tags,
200            })
201            .collect()
202    }
203
204    /// Bejegyzés hasznosságának növelése
205    pub fn mark_useful(&self, id: &str) {
206        let mut entries = crate::sync_guard::write_lock(&self.entries);
207        if let Some(entry) = entries.get_mut(id) {
208            entry.usefulness += 1;
209            entry.updated_at = SystemTime::now()
210                .duration_since(UNIX_EPOCH)
211                .unwrap()
212                .as_secs();
213        }
214    }
215
216    /// Asszociáció tanulása két bejegyzés között
217    pub fn learn_association(&self, from_id: &str, to_id: &str, weight: f64) {
218        let mut associations = crate::sync_guard::write_lock(&self.associations);
219        associations
220            .entry(from_id.to_string())
221            .or_default()
222            .entry(to_id.to_string())
223            .and_modify(|w| *w = (*w + weight) / 2.0) // mozgóátlag
224            .or_insert(weight);
225    }
226
227    /// Kapcsolódó bejegyzések lekérése
228    pub fn get_related(&self, id: &str, max_results: usize) -> Vec<KnowledgeEntry> {
229        let associations = crate::sync_guard::read_lock(&self.associations);
230        let entries = crate::sync_guard::read_lock(&self.entries);
231
232        if let Some(related) = associations.get(id) {
233            let mut scored: Vec<(f64, &KnowledgeEntry)> = related
234                .iter()
235                .filter_map(|(rel_id, weight)| entries.get(rel_id).map(|entry| (*weight, entry)))
236                .collect();
237
238            scored.sort_by(|a, b| b.0.total_cmp(&a.0));
239
240            scored
241                .into_iter()
242                .take(max_results)
243                .map(|(_, entry)| entry.clone())
244                .collect()
245        } else {
246            Vec::new()
247        }
248    }
249
250    /// Architektúra minta hozzáadása a szimulációs eredményből
251    pub fn add_from_simulation(&self, arch: &Architecture, metrics: &SimulationMetrics) -> String {
252        let now = SystemTime::now()
253            .duration_since(UNIX_EPOCH)
254            .unwrap()
255            .as_secs();
256
257        let mut tags = vec![
258            "architecture".to_string(),
259            arch.name.clone(),
260            format!("components_{}", arch.components.len()),
261            format!("connections_{}", arch.connections.len()),
262        ];
263
264        if !metrics.bottleneck_components.is_empty() {
265            tags.push("bottleneck".to_string());
266            for b in &metrics.bottleneck_components {
267                tags.push(format!("bottleneck_{}", b));
268            }
269        }
270
271        if metrics.stability_score > 0.8 {
272            tags.push("high_stability".to_string());
273        }
274        if metrics.resilience_score > 0.8 {
275            tags.push("high_resilience".to_string());
276        }
277
278        let entry = KnowledgeEntry {
279            id: format!("sim_{}", rand::random::<u32>()),
280            entry_type: KnowledgeEntryType::SimulationResult,
281            title: format!("Simulation: {} (stability={:.2}, resilience={:.2})",
282                arch.name, metrics.stability_score, metrics.resilience_score),
283            description: format!(
284                "Architecture '{}' simulated: avg_latency={:.2}ms, throughput={:.0}req/s, error_rate={:.2}%, bottlenecks={}",
285                arch.name, metrics.avg_latency_ms, metrics.throughput_req_per_sec,
286                metrics.error_rate * 100.0, metrics.bottleneck_components.len()
287            ),
288            tags,
289            related_entries: Vec::new(),
290            confidence: metrics.stability_score.min(metrics.resilience_score),
291            usefulness: 0,
292            created_at: now,
293            updated_at: now,
294            source: format!("simulation_{}", arch.id),
295            context: format!("load_pattern=standard, duration={}s", metrics.simulation_time_secs),
296        };
297
298        self.add_entry(entry)
299    }
300
301    /// Döntési minta hozzáadása
302    pub fn add_from_decision(
303        &self,
304        decision: &DecisionLogEntry,
305        pattern: &HeuristicPattern,
306    ) -> String {
307        let now = SystemTime::now()
308            .duration_since(UNIX_EPOCH)
309            .unwrap()
310            .as_secs();
311
312        let entry = KnowledgeEntry {
313            id: format!("decision_{}", rand::random::<u32>()),
314            entry_type: KnowledgeEntryType::DecisionPattern,
315            title: format!(
316                "Decision pattern: {} (success={:.1}%)",
317                pattern.name,
318                pattern.success_rate * 100.0
319            ),
320            description: format!(
321                "Decision type: {:?}, selected: {}, outcome_score: {:.2}, reflection: {}",
322                decision.decision_type,
323                decision.selected_option,
324                decision.outcome_score,
325                decision.reflection
326            ),
327            tags: vec![
328                "decision".to_string(),
329                format!("{:?}", decision.decision_type),
330                format!("success_{:.0}", pattern.success_rate * 100.0),
331            ],
332            related_entries: Vec::new(),
333            confidence: pattern.success_rate,
334            usefulness: pattern.usage_count,
335            created_at: now,
336            updated_at: now,
337            source: format!("decision_{}", decision.decision_id),
338            context: pattern.context.clone(),
339        };
340
341        self.add_entry(entry)
342    }
343
344    /// Eureka/Insight hozzáadása
345    pub fn add_insight(
346        &self,
347        title: &str,
348        description: &str,
349        tags: Vec<String>,
350        source: &str,
351    ) -> String {
352        let now = SystemTime::now()
353            .duration_since(UNIX_EPOCH)
354            .unwrap()
355            .as_secs();
356
357        let entry = KnowledgeEntry {
358            id: format!("insight_{}", rand::random::<u32>()),
359            entry_type: KnowledgeEntryType::Insight,
360            title: title.to_string(),
361            description: description.to_string(),
362            tags: {
363                let mut t = vec!["insight".to_string(), "eureka".to_string()];
364                t.extend(tags);
365                t
366            },
367            related_entries: Vec::new(),
368            confidence: 0.5, // insights start with medium confidence
369            usefulness: 0,
370            created_at: now,
371            updated_at: now,
372            source: source.to_string(),
373            context: "discovery".to_string(),
374        };
375
376        self.add_entry(entry)
377    }
378
379    /// Legjobb gyakorlat hozzáadása
380    pub fn add_best_practice(
381        &self,
382        title: &str,
383        description: &str,
384        context: &str,
385        tags: Vec<String>,
386    ) -> String {
387        let now = SystemTime::now()
388            .duration_since(UNIX_EPOCH)
389            .unwrap()
390            .as_secs();
391
392        let entry = KnowledgeEntry {
393            id: format!("practice_{}", rand::random::<u32>()),
394            entry_type: KnowledgeEntryType::BestPractice,
395            title: title.to_string(),
396            description: description.to_string(),
397            tags: {
398                let mut t = vec!["best_practice".to_string(), "recommendation".to_string()];
399                t.extend(tags);
400                t
401            },
402            related_entries: Vec::new(),
403            confidence: 0.7,
404            usefulness: 0,
405            created_at: now,
406            updated_at: now,
407            source: "knowledge_base".to_string(),
408            context: context.to_string(),
409        };
410
411        self.add_entry(entry)
412    }
413
414    /// Ismert hiba/buktátó hozzáadása
415    pub fn add_pitfall(
416        &self,
417        title: &str,
418        description: &str,
419        context: &str,
420        tags: Vec<String>,
421    ) -> String {
422        let now = SystemTime::now()
423            .duration_since(UNIX_EPOCH)
424            .unwrap()
425            .as_secs();
426
427        let entry = KnowledgeEntry {
428            id: format!("pitfall_{}", rand::random::<u32>()),
429            entry_type: KnowledgeEntryType::KnownPitfall,
430            title: title.to_string(),
431            description: description.to_string(),
432            tags: {
433                let mut t = vec![
434                    "pitfall".to_string(),
435                    "warning".to_string(),
436                    "anti_pattern".to_string(),
437                ];
438                t.extend(tags);
439                t
440            },
441            related_entries: Vec::new(),
442            confidence: 0.8,
443            usefulness: 0,
444            created_at: now,
445            updated_at: now,
446            source: "knowledge_base".to_string(),
447            context: context.to_string(),
448        };
449
450        self.add_entry(entry)
451    }
452
453    /// Heurisztikus szabály hozzáadása
454    pub fn add_heuristic_rule(
455        &self,
456        title: &str,
457        description: &str,
458        context: &str,
459        confidence: f64,
460        tags: Vec<String>,
461    ) -> String {
462        let now = SystemTime::now()
463            .duration_since(UNIX_EPOCH)
464            .unwrap()
465            .as_secs();
466
467        let entry = KnowledgeEntry {
468            id: format!("rule_{}", rand::random::<u32>()),
469            entry_type: KnowledgeEntryType::HeuristicRule,
470            title: title.to_string(),
471            description: description.to_string(),
472            tags: {
473                let mut t = vec!["heuristic".to_string(), "rule".to_string()];
474                t.extend(tags);
475                t
476            },
477            related_entries: Vec::new(),
478            confidence,
479            usefulness: 0,
480            created_at: now,
481            updated_at: now,
482            source: "knowledge_base".to_string(),
483            context: context.to_string(),
484        };
485
486        self.add_entry(entry)
487    }
488
489    /// Típus szerinti lekérdezés
490    pub fn get_by_type(&self, entry_type: &KnowledgeEntryType) -> Vec<KnowledgeEntry> {
491        let type_index = crate::sync_guard::read_lock(&self.type_index);
492        let entries = crate::sync_guard::read_lock(&self.entries);
493
494        type_index
495            .get(entry_type)
496            .map(|ids| {
497                ids.iter()
498                    .filter_map(|id| entries.get(id))
499                    .cloned()
500                    .collect()
501            })
502            .unwrap_or_default()
503    }
504
505    /// Tag szerinti lekérdezés
506    pub fn get_by_tag(&self, tag: &str) -> Vec<KnowledgeEntry> {
507        let tag_index = crate::sync_guard::read_lock(&self.tag_index);
508        let entries = crate::sync_guard::read_lock(&self.entries);
509
510        tag_index
511            .get(tag)
512            .map(|ids| {
513                ids.iter()
514                    .filter_map(|id| entries.get(id))
515                    .cloned()
516                    .collect()
517            })
518            .unwrap_or_default()
519    }
520
521    /// Statisztikák lekérése
522    pub fn get_stats(&self) -> KnowledgeBaseStats {
523        let entries = crate::sync_guard::read_lock(&self.entries);
524        let type_index = crate::sync_guard::read_lock(&self.type_index);
525
526        let total = entries.len() as u64;
527        let arch_count = type_index
528            .get(&KnowledgeEntryType::ArchitecturePattern)
529            .map(|v| v.len() as u64)
530            .unwrap_or(0);
531        let sim_count = type_index
532            .get(&KnowledgeEntryType::SimulationResult)
533            .map(|v| v.len() as u64)
534            .unwrap_or(0);
535        let dec_count = type_index
536            .get(&KnowledgeEntryType::DecisionPattern)
537            .map(|v| v.len() as u64)
538            .unwrap_or(0);
539        let ins_count = type_index
540            .get(&KnowledgeEntryType::Insight)
541            .map(|v| v.len() as u64)
542            .unwrap_or(0);
543        let rule_count = type_index
544            .get(&KnowledgeEntryType::HeuristicRule)
545            .map(|v| v.len() as u64)
546            .unwrap_or(0);
547        let prac_count = type_index
548            .get(&KnowledgeEntryType::BestPractice)
549            .map(|v| v.len() as u64)
550            .unwrap_or(0);
551        let pit_count = type_index
552            .get(&KnowledgeEntryType::KnownPitfall)
553            .map(|v| v.len() as u64)
554            .unwrap_or(0);
555
556        let avg_conf = if total > 0 {
557            entries.values().map(|e| e.confidence).sum::<f64>() / total as f64
558        } else {
559            0.0
560        };
561
562        let total_use = entries.values().map(|e| e.usefulness).sum();
563
564        KnowledgeBaseStats {
565            total_entries: total,
566            architecture_patterns: arch_count,
567            simulation_results: sim_count,
568            decision_patterns: dec_count,
569            insights: ins_count,
570            heuristic_rules: rule_count,
571            best_practices: prac_count,
572            known_pitfalls: pit_count,
573            avg_confidence: avg_conf,
574            total_usefulness: total_use,
575        }
576    }
577
578    /// Tudásbázis exportálása (minden bejegyzés)
579    pub fn export_all(&self) -> Vec<KnowledgeEntry> {
580        let entries = crate::sync_guard::read_lock(&self.entries);
581        entries.values().cloned().collect()
582    }
583
584    /// Tudásbázis törlése
585    pub fn clear(&self) {
586        crate::sync_guard::write_lock(&self.entries).clear();
587        crate::sync_guard::write_lock(&self.tag_index).clear();
588        crate::sync_guard::write_lock(&self.type_index).clear();
589        crate::sync_guard::write_lock(&self.associations).clear();
590    }
591}
592
593// ─── Automatikus tudásépítés ────────────────────────────────────────────────
594
595/// Automatikusan építi a tudásbázist a szimulációs eredményekből
596pub fn auto_build_from_simulations(
597    kb: &KnowledgeBase,
598    architectures: &[Architecture],
599    simulation_results: &HashMap<String, Vec<SimulationMetrics>>,
600) {
601    for arch in architectures {
602        if let Some(metrics_list) = simulation_results.get(&arch.id) {
603            for metrics in metrics_list {
604                let _entry_id = kb.add_from_simulation(arch, metrics);
605
606                // Bottleneck detektálás -> pitfall
607                if !metrics.bottleneck_components.is_empty() {
608                    kb.add_pitfall(
609                        &format!("Bottleneck in {}: {}", arch.name, metrics.bottleneck_components.join(", ")),
610                        &format!("Components {} became bottlenecks at load. Consider scaling or optimizing.",
611                            metrics.bottleneck_components.join(", ")),
612                        &format!("architecture={}, load_pattern=standard", arch.name),
613                        vec![
614                            "bottleneck".to_string(),
615                            arch.name.clone(),
616                            "performance".to_string(),
617                        ],
618                    );
619                }
620
621                // Magas stabilitás -> best practice
622                if metrics.stability_score > 0.9 {
623                    kb.add_best_practice(
624                        &format!("High stability architecture: {}", arch.name),
625                        &format!(
626                            "Architecture '{}' achieved {:.1}% stability under test conditions.",
627                            arch.name,
628                            metrics.stability_score * 100.0
629                        ),
630                        &format!("architecture={}, stability>0.9", arch.name),
631                        vec![
632                            "stability".to_string(),
633                            arch.name.clone(),
634                            "reference_architecture".to_string(),
635                        ],
636                    );
637                }
638            }
639        }
640    }
641}
642
643/// Automatikusan építi a tudásbázist a döntési mintákból
644pub fn auto_build_from_decisions(
645    kb: &KnowledgeBase,
646    decision_log: &[DecisionLogEntry],
647    patterns: &[HeuristicPattern],
648) {
649    for pattern in patterns {
650        // Csak a releváns döntéseket keressük
651        let relevant_decisions: Vec<&DecisionLogEntry> = decision_log
652            .iter()
653            .filter(|d| format!("{:?}", d.decision_type) == pattern.pattern_type)
654            .collect();
655
656        if let Some(latest) = relevant_decisions.last() {
657            kb.add_from_decision(latest, pattern);
658        }
659
660        // Magas sikerrátájú minták -> best practice
661        if pattern.success_rate > 0.8 && pattern.usage_count > 3 {
662            kb.add_best_practice(
663                &format!("Proven decision pattern: {}", pattern.name),
664                &format!(
665                    "Pattern '{}' has {:.1}% success rate after {} uses.",
666                    pattern.name,
667                    pattern.success_rate * 100.0,
668                    pattern.usage_count
669                ),
670                &format!("decision_type={}", pattern.pattern_type),
671                vec![
672                    "decision".to_string(),
673                    pattern.pattern_type.clone(),
674                    "proven".to_string(),
675                ],
676            );
677        }
678
679        // Alacsony sikerrátájú minták -> pitfall
680        if pattern.success_rate < 0.3 && pattern.usage_count > 2 {
681            kb.add_pitfall(
682                &format!("Unreliable decision pattern: {}", pattern.name),
683                &format!(
684                    "Pattern '{}' has only {:.1}% success rate. Consider alternative approaches.",
685                    pattern.name,
686                    pattern.success_rate * 100.0
687                ),
688                &format!("decision_type={}", pattern.pattern_type),
689                vec![
690                    "decision".to_string(),
691                    pattern.pattern_type.clone(),
692                    "unreliable".to_string(),
693                ],
694            );
695        }
696    }
697}
698
699/// Automatikus asszociáció tanulás a tudásbázisban
700pub fn auto_learn_associations(kb: &KnowledgeBase) {
701    let entries = kb.export_all();
702
703    for i in 0..entries.len() {
704        for j in (i + 1)..entries.len() {
705            let a = &entries[i];
706            let b = &entries[j];
707
708            // Közös tagek alapján asszociáció
709            let common_tags: Vec<&String> = a.tags.iter().filter(|t| b.tags.contains(t)).collect();
710
711            if !common_tags.is_empty() {
712                let weight =
713                    common_tags.len() as f64 / a.tags.len().clamp(1, b.tags.len().max(1)) as f64;
714                if weight > 0.3 {
715                    kb.learn_association(&a.id, &b.id, weight);
716                    kb.learn_association(&b.id, &a.id, weight);
717                }
718            }
719
720            // Típus alapján asszociáció (pl. simulation -> pitfall)
721            if a.entry_type == KnowledgeEntryType::SimulationResult
722                && b.entry_type == KnowledgeEntryType::KnownPitfall
723                && a.description.contains(&b.title[..b.title.len().min(20)])
724            {
725                kb.learn_association(&a.id, &b.id, 0.8);
726            }
727        }
728    }
729}
730
731#[cfg(test)]
732mod tests {
733    use super::*;
734
735    #[test]
736    fn test_add_and_search() {
737        let kb = KnowledgeBase::new();
738
739        kb.add_best_practice(
740            "Use microservices for scalability",
741            "Microservices allow independent scaling of components",
742            "high_load_systems",
743            vec!["microservices".to_string(), "scalability".to_string()],
744        );
745
746        kb.add_pitfall(
747            "Avoid tight coupling in distributed systems",
748            "Tight coupling reduces fault tolerance",
749            "distributed_systems",
750            vec!["coupling".to_string(), "distributed".to_string()],
751        );
752
753        let results = kb.search("microservices", 10);
754        assert!(!results.is_empty());
755        assert!(results[0].relevance_score > 0.0);
756    }
757
758    #[test]
759    fn test_get_stats() {
760        let kb = KnowledgeBase::new();
761        let stats = kb.get_stats();
762        assert_eq!(stats.total_entries, 0);
763
764        kb.add_best_practice("Test", "Test description", "test", vec![]);
765        let stats = kb.get_stats();
766        assert_eq!(stats.total_entries, 1);
767        assert_eq!(stats.best_practices, 1);
768    }
769
770    #[test]
771    fn test_auto_associations() {
772        let kb = KnowledgeBase::new();
773
774        let id_a = kb.add_best_practice(
775            "Test Practice",
776            "A test best practice",
777            "test",
778            vec!["test".to_string(), "practice".to_string()],
779        );
780
781        let id_b = kb.add_pitfall(
782            "Test Pitfall",
783            "A test pitfall",
784            "test",
785            vec!["test".to_string(), "pitfall".to_string()],
786        );
787
788        auto_learn_associations(&kb);
789        kb.learn_association(&id_a, &id_b, 0.5);
790
791        let related = kb.get_related(&id_a, 5);
792        assert!(!related.is_empty());
793    }
794}