| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| pub mod knowledge; |
| pub mod rules; |
| pub mod analogy; |
| pub mod synthesis; |
| pub mod pathways; |
| pub mod chain; |
| pub mod chain_display; |
|
|
| use crate::persistence::{Memory, BeliefState}; |
| use crate::math::MathEngine; |
| use pathways::PathwayFusion; |
| use knowledge::RelationType; |
|
|
| |
| #[derive(Clone)] |
| #[allow(dead_code)] |
| pub struct ReasoningEngine { |
| |
| knowledge: knowledge::KnowledgeGraph, |
| |
| rules: rules::RuleEngine, |
| |
| analogy: analogy::AnalogyEngine, |
| |
| fusion: PathwayFusion, |
| |
| working_memory: Vec<WorkingItem>, |
| } |
|
|
| #[derive(Debug, Clone)] |
| pub struct WorkingItem { |
| pub content: String, |
| pub source: WorkingSource, |
| pub confidence: Option<f64>, |
| } |
|
|
| #[derive(Debug, Clone, Copy)] |
| pub enum WorkingSource { |
| Retrieved, |
| Inferred, |
| Assumed, |
| } |
|
|
| impl ReasoningEngine { |
| |
| pub fn knowledge(&self) -> &knowledge::KnowledgeGraph { |
| &self.knowledge |
| } |
|
|
| |
| pub fn knowledge_mut(&mut self) -> &mut knowledge::KnowledgeGraph { |
| &mut self.knowledge |
| } |
|
|
| pub fn new() -> Self { |
| let knowledge = knowledge::KnowledgeGraph::new(); |
| let kg_arc = std::sync::Arc::new(std::sync::RwLock::new(knowledge.clone())); |
| Self { |
| knowledge, |
| rules: rules::RuleEngine::new(), |
| analogy: analogy::AnalogyEngine::new().with_knowledge_graph(kg_arc), |
| fusion: PathwayFusion::new(), |
| working_memory: Vec::new(), |
| } |
| } |
|
|
| |
| pub fn add_knowledge(&mut self, subject: &str, fact: &str) { |
| self.knowledge.add_fact(subject, fact); |
| |
| self.knowledge.add_entity(subject); |
| } |
|
|
| |
| |
| |
| pub fn reason(&mut self, query: &str, memories: &[Memory]) -> ReasoningResult { |
| self.working_memory.clear(); |
| |
| |
| for mem in memories { |
| self.working_memory.push(WorkingItem { |
| content: mem.content.clone(), |
| source: WorkingSource::Retrieved, |
| confidence: mem.confidence, |
| }); |
| |
| |
| self.ingest_memory(mem); |
| } |
| |
| |
| let query_type = self.classify_query(query); |
| |
| |
| match query_type { |
| QueryType::WhatIs => self.answer_what_is(query), |
| QueryType::Why => self.answer_why(query), |
| QueryType::How => self.answer_how(query), |
| QueryType::Does => self.answer_does(query), |
| QueryType::Should => self.answer_should(query), |
| QueryType::Novel => self.answer_novel(query), |
| QueryType::Math => self.answer_math(query), |
| QueryType::Unknown => self.answer_unknown(query), |
| } |
| } |
|
|
| |
| fn classify_query(&self, query: &str) -> QueryType { |
| let lower = query.to_lowercase(); |
|
|
| |
| |
| let has_number = query.chars().any(|c| c.is_ascii_digit()); |
| let has_operator = query.contains('+') |
| || query.contains('-') |
| || query.contains('*') |
| || query.contains('/') |
| || query.contains('^'); |
| |
| let has_word_math = lower.contains("divided by") |
| || lower.contains("times") |
| || lower.contains("multiplied by") |
| || lower.contains("plus") |
| || lower.contains("minus"); |
| if (has_number && has_operator) || has_word_math { |
| return QueryType::Math; |
| } |
|
|
| if lower.starts_with("what is") || lower.starts_with("what are") || lower.starts_with("what's") { |
| QueryType::WhatIs |
| } else if lower.starts_with("why") { |
| QueryType::Why |
| } else if lower.starts_with("how") { |
| QueryType::How |
| } else if lower.starts_with("does") || lower.starts_with("do ") || lower.starts_with("is ") { |
| QueryType::Does |
| } else if lower.starts_with("should") || lower.contains(" ought ") { |
| QueryType::Should |
| } else if lower.contains(" if ") || lower.contains(" would happen") { |
| QueryType::Novel |
| } else { |
| QueryType::Unknown |
| } |
| } |
|
|
| fn ingest_memory(&mut self, mem: &Memory) { |
| |
| |
| let content = &mem.content; |
| let words: Vec<&str> = content.split_whitespace().collect(); |
| |
| |
| let stop_words: std::collections::HashSet<&str> = [ |
| "the", "and", "for", "are", "but", "not", "you", "all", "can", "had", |
| "her", "was", "one", "our", "out", "has", "have", "been", "were", "they", |
| "this", "that", "with", "from", "its", "about", "which", "also", "such", |
| ].into_iter().collect(); |
| |
| |
| |
| let n = words.len(); |
| for (i, word) in words.iter().enumerate() { |
| let word_lower = word.to_lowercase(); |
| let cleaned = word.trim_matches(|c| !char::is_alphanumeric(c)); |
| if cleaned.len() < 2 || stop_words.contains(word_lower.as_str()) { |
| continue; |
| } |
| |
| |
| |
| let is_entity = word.chars().next().map(|c| c.is_uppercase()).unwrap_or(false) |
| || (i == 0 && word.chars().next().map(|c| c.is_lowercase()).unwrap_or(false)); |
| |
| if !is_entity && i > 0 { |
| continue; |
| } |
| |
| |
| |
| if i < n - 2 { |
| let v1 = words[i + 1].to_lowercase(); |
| let v2 = words.get(i + 2).map(|s| s.to_lowercase()).unwrap_or_default(); |
| |
| if ["is", "are", "was", "were"].contains(&v1.as_str()) { |
| |
| let entity_name = cleaned; |
| let value = words[i + 2].trim_matches(|c: char| !char::is_alphanumeric(c)); |
| if value.len() > 1 { |
| self.knowledge.add_relationship(entity_name, RelationType::IsA, value); |
| } |
| } else if ["requires", "needs", "needs:", "uses"].contains(&v1.as_str()) { |
| let entity_name = cleaned; |
| |
| let rest: Vec<&str> = words[i+2..].iter() |
| .map(|w| w.trim_matches(|c: char| !char::is_alphanumeric(c))) |
| .filter(|w| !w.is_empty() && !stop_words.contains(w.to_lowercase().as_str())) |
| .collect(); |
| if !rest.is_empty() { |
| let obj = rest.join(" "); |
| self.knowledge.add_relationship(entity_name, RelationType::Enables, &obj); |
| } |
| } else if ["produces", "creates", "generates"].contains(&v1.as_str()) { |
| let entity_name = cleaned; |
| let rest: Vec<&str> = words[i+2..].iter() |
| .map(|w| w.trim_matches(|c: char| !char::is_alphanumeric(c))) |
| .filter(|w| !w.is_empty() && !stop_words.contains(w.to_lowercase().as_str())) |
| .collect(); |
| if !rest.is_empty() { |
| let obj = rest.join(" "); |
| self.knowledge.add_relationship(entity_name, RelationType::Causes, &obj); |
| } |
| } else if ["causes", "leads", "to"].contains(&v1.as_str()) && v2 == "to" { |
| let entity_name = cleaned; |
| let rest: Vec<&str> = words[i+3..].iter() |
| .map(|w| w.trim_matches(|c: char| !char::is_alphanumeric(c))) |
| .filter(|w| !w.is_empty() && !stop_words.contains(w.to_lowercase().as_str())) |
| .collect(); |
| if !rest.is_empty() { |
| let obj = rest.join(" "); |
| self.knowledge.add_relationship(entity_name, RelationType::Causes, &obj); |
| } |
| } |
| } |
| |
| |
| if cleaned.len() > 2 { |
| self.knowledge.add_entity(cleaned); |
| } |
| } |
| |
| |
| if content.to_lowercase().contains(" if ") && content.to_lowercase().contains(" then ") { |
| if let Some(rule) = self.rules.parse_rule(content) { |
| self.rules.add_rule(rule); |
| } |
| } |
| } |
|
|
| fn answer_what_is(&mut self, query: &str) -> ReasoningResult { |
| |
| let target = query |
| .to_lowercase() |
| .replace("what is", "") |
| .replace("what are", "") |
| .replace("what's", "") |
| .replace("?", "") |
| .trim() |
| .to_string(); |
| |
| |
| let entities = self.knowledge.get_entity(&target); |
| |
| if let Some(entity) = &entities { |
| let facts = self.knowledge.get_facts_about(&target); |
| if !facts.is_empty() { |
| let answer = format!("{} β {}", entity.description.as_deref().unwrap_or(&entity.name), facts.join("; ")); |
| return ReasoningResult { |
| answer: Some(answer), |
| confidence: BeliefState::Knows, |
| reasoning_chain: facts, |
| confidence_score: Some(0.85), |
| }; |
| } else { |
| return ReasoningResult { |
| answer: Some(format!("I know about {}.", target)), |
| confidence: BeliefState::Thinks, |
| reasoning_chain: vec![format!("Entity '{}' found in knowledge graph", target)], |
| confidence_score: Some(0.5), |
| }; |
| } |
| } |
| |
| |
| let relevant: Vec<_> = self.working_memory.iter() |
| .filter(|w| w.content.to_lowercase().contains(&target)) |
| .collect(); |
| |
| if let Some(item) = relevant.first() { |
| ReasoningResult { |
| answer: Some(item.content.to_string()), |
| confidence: item.confidence.map(|c| if c > 0.7 { BeliefState::Thinks } else { BeliefState::Believes }) |
| .unwrap_or(BeliefState::Believes), |
| reasoning_chain: vec![item.content.clone()], |
| confidence_score: item.confidence, |
| } |
| } else { |
| ReasoningResult { |
| answer: Some(format!("I don't know what {} is.", target)), |
| confidence: BeliefState::Unknown, |
| reasoning_chain: vec![], |
| confidence_score: None, |
| } |
| } |
| } |
|
|
| fn answer_why(&mut self, query: &str) -> ReasoningResult { |
| |
| let topic = query |
| .to_lowercase() |
| .replace("why does", "") |
| .replace("why do", "") |
| .replace("why is", "") |
| .replace("why are", "") |
| .replace("why", "") |
| .replace("?", "") |
| .trim() |
| .to_string(); |
| |
| |
| let causes = self.knowledge.get_causes(&topic); |
| |
| if !causes.is_empty() { |
| let answer = format!("{} because {}", topic, causes.join(" and ")); |
| return ReasoningResult { |
| answer: Some(answer), |
| confidence: BeliefState::Thinks, |
| reasoning_chain: causes.clone(), |
| confidence_score: Some(0.7), |
| }; |
| } |
| |
| |
| let hypothesis = self.abduct_cause(&topic); |
| if let Some(h) = hypothesis { |
| ReasoningResult { |
| answer: Some(format!("I don't know for certain, but: {}", h)), |
| confidence: BeliefState::Suspects, |
| reasoning_chain: vec![format!("Abduced cause for '{}': {}", topic, h)], |
| confidence_score: Some(0.4), |
| } |
| } else { |
| ReasoningResult { |
| answer: Some(format!("I don't know why {}.", topic)), |
| confidence: BeliefState::Unknown, |
| reasoning_chain: vec![], |
| confidence_score: None, |
| } |
| } |
| } |
|
|
| fn answer_how(&mut self, query: &str) -> ReasoningResult { |
| |
| let topic = query |
| .to_lowercase() |
| .replace("how does", "") |
| .replace("how do", "") |
| .replace("how to", "") |
| .replace("how", "") |
| .replace("?", "") |
| .trim() |
| .to_string(); |
|
|
| |
| let mechanisms = self.knowledge.get_mechanisms(&topic); |
|
|
| if !mechanisms.is_empty() { |
| let answer = format!("{} through: {}", topic, mechanisms.join(", ")); |
| return ReasoningResult { |
| answer: Some(answer), |
| confidence: BeliefState::Thinks, |
| reasoning_chain: mechanisms.clone(), |
| confidence_score: Some(0.6), |
| }; |
| } |
|
|
| |
| |
| let stop_words: std::collections::HashSet<&str> = [ |
| "the", "a", "an", "does", "do", "to", "of", "in", "on", "for", "with", "by", |
| ].into_iter().collect(); |
|
|
| let keywords: Vec<&str> = topic.split_whitespace() |
| .filter(|w| !stop_words.contains(*w) && w.len() > 1) |
| .collect(); |
|
|
| let mut all_mechanisms: Vec<String> = Vec::new(); |
| let mut all_facts: Vec<String> = Vec::new(); |
|
|
| for kw in &keywords { |
| let mech = self.knowledge.get_mechanisms(kw); |
| for m in mech { |
| if !all_mechanisms.contains(&m) { |
| all_mechanisms.push(m); |
| } |
| } |
| let facts = self.knowledge.get_facts_about(kw); |
| for f in facts { |
| if !all_facts.contains(&f) { |
| all_facts.push(f); |
| } |
| } |
| } |
|
|
| |
| for item in &self.working_memory { |
| let content_lower = item.content.to_lowercase(); |
| if keywords.iter().any(|kw| content_lower.contains(kw)) |
| && !all_facts.contains(&item.content) { |
| all_facts.push(item.content.clone()); |
| } |
| } |
|
|
| if !all_mechanisms.is_empty() { |
| let answer = format!("{} through: {}", topic, all_mechanisms.join(", ")); |
| return ReasoningResult { |
| answer: Some(answer), |
| confidence: BeliefState::Thinks, |
| reasoning_chain: all_mechanisms, |
| confidence_score: Some(0.6), |
| }; |
| } |
|
|
| if !all_facts.is_empty() { |
| return ReasoningResult { |
| answer: Some(format!("{}: {}", topic, all_facts.join("; "))), |
| confidence: BeliefState::Believes, |
| reasoning_chain: all_facts, |
| confidence_score: Some(0.4), |
| }; |
| } |
|
|
| ReasoningResult { |
| answer: Some(format!("I don't know how {}.", topic)), |
| confidence: BeliefState::Unknown, |
| reasoning_chain: vec![], |
| confidence_score: None, |
| } |
| } |
|
|
| fn answer_does(&mut self, query: &str) -> ReasoningResult { |
| |
| let normalized = query.to_lowercase() |
| .replace("does ", "") |
| .replace("do ", "") |
| .replace("is ", "") |
| .replace("are ", "") |
| .replace("?", "") |
| .trim() |
| .to_string(); |
|
|
| |
| let facts = self.knowledge.get_facts_containing(&normalized); |
|
|
| if !facts.is_empty() { |
| return ReasoningResult { |
| answer: Some(facts.first().cloned().unwrap()), |
| confidence: BeliefState::Thinks, |
| reasoning_chain: facts.clone(), |
| confidence_score: Some(0.7), |
| }; |
| } |
|
|
| |
| let stop_words: std::collections::HashSet<&str> = [ |
| "the", "a", "an", "does", "do", "is", "are", "to", "of", "in", "on", "for", "with", "by", |
| ].into_iter().collect(); |
|
|
| let keywords: Vec<&str> = normalized.split_whitespace() |
| .filter(|w| !stop_words.contains(*w) && w.len() > 1) |
| .collect(); |
|
|
| let mut all_facts: Vec<String> = Vec::new(); |
|
|
| for kw in &keywords { |
| let facts = self.knowledge.get_facts_about(kw); |
| for f in facts { |
| if !all_facts.contains(&f) { |
| all_facts.push(f); |
| } |
| } |
| let containing = self.knowledge.get_facts_containing(kw); |
| for f in containing { |
| if !all_facts.contains(&f) { |
| all_facts.push(f); |
| } |
| } |
| } |
|
|
| |
| for item in &self.working_memory { |
| let content_lower = item.content.to_lowercase(); |
| if keywords.iter().any(|kw| content_lower.contains(kw)) |
| && !all_facts.contains(&item.content) { |
| all_facts.push(item.content.clone()); |
| } |
| } |
|
|
| if !all_facts.is_empty() { |
| return ReasoningResult { |
| answer: Some(format!("Based on what I know: {}", all_facts.join("; "))), |
| confidence: BeliefState::Believes, |
| reasoning_chain: all_facts.clone(), |
| confidence_score: Some(0.5), |
| }; |
| } |
|
|
| ReasoningResult { |
| answer: Some("I don't know whether that's true.".to_string()), |
| confidence: BeliefState::Unknown, |
| reasoning_chain: vec![], |
| confidence_score: None, |
| } |
| } |
|
|
| fn answer_should(&mut self, query: &str) -> ReasoningResult { |
| |
| let topic = query |
| .to_lowercase() |
| .replace("should", "") |
| .replace(" ought ", "") |
| .replace("?", "") |
| .trim() |
| .to_string(); |
| |
| |
| let _values = self.knowledge.get_values_related(&topic); |
| |
| |
| let analogies = self.analogy.find_analogies(&topic); |
| |
| if !analogies.is_empty() { |
| let analogy = &analogies[0]; |
| let answer = format!( |
| "Should {}? Well, {} is to {} as {} is to {}. Does that help?", |
| topic, analogy.source, analogy.source_relation, |
| analogy.target, analogy.target_relation |
| ); |
| return ReasoningResult { |
| answer: Some(answer), |
| confidence: BeliefState::Suspects, |
| reasoning_chain: vec![format!("Analogy: {}", analogy.explanation())], |
| confidence_score: Some(0.4), |
| }; |
| } |
| |
| ReasoningResult { |
| answer: Some(format!("I don't have a clear answer on whether {} is right.", topic)), |
| confidence: BeliefState::Unknown, |
| reasoning_chain: vec![], |
| confidence_score: None, |
| } |
| } |
|
|
| fn answer_novel(&mut self, query: &str) -> ReasoningResult { |
| |
| let topic = query.replace("?", "").trim().to_string(); |
| |
| |
| let synthesis = self.synthesize(&topic); |
| |
| if let Some(result) = synthesis { |
| ReasoningResult { |
| answer: Some(result.insight), |
| confidence: if result.is_novel { BeliefState::Suspects } else { BeliefState::Believes }, |
| reasoning_chain: result.chain, |
| confidence_score: Some(result.confidence), |
| } |
| } else { |
| ReasoningResult { |
| answer: Some("That's a hard one.".to_string()), |
| confidence: BeliefState::Unknown, |
| reasoning_chain: vec![], |
| confidence_score: None, |
| } |
| } |
| } |
|
|
| |
| fn answer_math(&mut self, query: &str) -> ReasoningResult { |
| let mut engine = MathEngine::new(); |
|
|
| |
| let result = engine.solve(query); |
| let answer_str = result.answer(); |
|
|
| |
| if answer_str.starts_with("Error: ") || answer_str == "Error: Could not parse or solve: " { |
| |
| let lower_query = query.to_lowercase(); |
| let cleaned = lower_query |
| .trim() |
| .strip_prefix("what is") |
| .unwrap_or(query) |
| .trim() |
| .strip_prefix("calculate") |
| .unwrap_or(query) |
| .trim() |
| .strip_prefix("solve") |
| .unwrap_or(query) |
| .trim() |
| .strip_prefix("what's") |
| .unwrap_or(query) |
| .trim() |
| .to_string(); |
|
|
| |
| if cleaned != query || answer_str.starts_with("Error: ") { |
| let result = engine.solve(&cleaned); |
| let answer = result.answer(); |
| if !answer.starts_with("Error: ") && !answer.is_empty() { |
| return ReasoningResult { |
| answer: Some(answer.clone()), |
| confidence: BeliefState::Knows, |
| reasoning_chain: vec![format!("Evaluated: {} = {}", cleaned, answer)], |
| confidence_score: Some(0.95), |
| }; |
| } |
| } |
|
|
| ReasoningResult { |
| answer: Some(format!("I couldn't parse that as math: {}", query)), |
| confidence: BeliefState::Unknown, |
| reasoning_chain: vec![], |
| confidence_score: None, |
| } |
| } else if !answer_str.is_empty() { |
| |
| ReasoningResult { |
| answer: Some(answer_str.clone()), |
| confidence: BeliefState::Knows, |
| reasoning_chain: vec![format!("Evaluated: {} = {}", query, answer_str)], |
| confidence_score: Some(0.95), |
| } |
| } else { |
| ReasoningResult { |
| answer: Some("I couldn't evaluate that expression.".to_string()), |
| confidence: BeliefState::Unknown, |
| reasoning_chain: vec![], |
| confidence_score: None, |
| } |
| } |
| } |
|
|
| fn answer_unknown(&mut self, query: &str) -> ReasoningResult { |
| |
| let topic = query.replace("?", "").trim().to_string(); |
|
|
| if topic.len() < 3 { |
| return ReasoningResult { |
| answer: Some("Say that differently?".to_string()), |
| confidence: BeliefState::Unknown, |
| reasoning_chain: vec![], |
| confidence_score: None, |
| }; |
| } |
|
|
| let lower_topic = topic.to_lowercase(); |
| let mut reasoning_chain = Vec::new(); |
|
|
| |
| let entities = self.knowledge.get_entity(&lower_topic); |
| if let Some(entity) = entities { |
| let facts = self.knowledge.get_facts_about(&lower_topic); |
| if !facts.is_empty() { |
| let answer = format!( |
| "{} β {}", |
| entity.description.as_deref().unwrap_or(&topic), |
| facts.join("; ") |
| ); |
| reasoning_chain.extend(facts.clone()); |
| return ReasoningResult { |
| answer: Some(answer), |
| confidence: BeliefState::Knows, |
| reasoning_chain, |
| confidence_score: Some(0.85), |
| }; |
| } |
| } |
|
|
| |
| let analogies = self.analogy.find_analogies(&topic); |
| if !analogies.is_empty() { |
| let best = &analogies[0]; |
| let answer = format!( |
| "I don't know '{}' directly, but it's similar to {} β {}", |
| topic, |
| best.source, |
| best.structure |
| ); |
| reasoning_chain.push(format!("Analogy: {} ~ {}", topic, best.source)); |
| return ReasoningResult { |
| answer: Some(answer), |
| confidence: BeliefState::Believes, |
| reasoning_chain, |
| confidence_score: Some(0.4), |
| }; |
| } |
|
|
| |
| if let Some(result) = self.synthesize(&topic) { |
| reasoning_chain.extend(result.chain.clone()); |
| return ReasoningResult { |
| answer: Some(result.insight), |
| confidence: BeliefState::Suspects, |
| reasoning_chain, |
| confidence_score: Some(0.3), |
| }; |
| } |
|
|
| |
| let relevant: Vec<_> = self.working_memory.iter() |
| .filter(|w| { |
| w.content.to_lowercase().contains(&lower_topic) |
| || lower_topic.split_whitespace().any(|word| w.content.to_lowercase().contains(word)) |
| }) |
| .take(3) |
| .collect(); |
|
|
| if !relevant.is_empty() { |
| let contents: Vec<_> = relevant.iter().map(|w| w.content.clone()).collect(); |
| reasoning_chain.extend(contents.clone()); |
| return ReasoningResult { |
| answer: Some(format!( |
| "I don't know much about '{}', but {} β based on what I do know.", |
| topic, |
| contents.join("; ") |
| )), |
| confidence: BeliefState::Believes, |
| reasoning_chain, |
| confidence_score: Some(0.3), |
| }; |
| } |
|
|
| |
| if let Some(cause) = self.abduct_cause(&topic) { |
| return ReasoningResult { |
| answer: Some(cause.clone()), |
| confidence: BeliefState::Suspects, |
| reasoning_chain: vec![format!("Abduced: {} about {}", cause, topic)], |
| confidence_score: Some(0.25), |
| }; |
| } |
|
|
| |
| |
| let approaches = [ |
| format!( |
| "I don't know anything about '{}'. I'd need to learn about it β can you teach me or should I look it up?", |
| topic |
| ), |
| format!( |
| "'{}' is outside what I know right now. If you tell me more or let me search for it, I could reason about it.", |
| topic |
| ), |
| format!( |
| "I have no information about '{}'. Want to /search for it, or teach me directly?", |
| topic |
| ), |
| ]; |
| let idx = (topic.len() + query.len()) % approaches.len(); |
|
|
| ReasoningResult { |
| answer: Some(approaches[idx].clone()), |
| confidence: BeliefState::Unknown, |
| reasoning_chain: vec![format!("Exhausted KG, analogy, synthesis, abduction, memory β no match for '{}'", topic)], |
| confidence_score: Some(0.0), |
| } |
| } |
|
|
| |
| fn abduct_cause(&self, observation: &str) -> Option<String> { |
| let effects = self.knowledge.get_effects(observation); |
| |
| if !effects.is_empty() { |
| return effects.first().cloned(); |
| } |
| |
| None |
| } |
|
|
| |
| fn synthesize(&self, query: &str) -> Option<SynthesisResult> { |
| let query_lower = query.to_lowercase(); |
| |
| |
| let relevant: Vec<_> = self.working_memory.iter() |
| .filter(|w| { |
| let content_lower = w.content.to_lowercase(); |
| query_lower.split_whitespace().any(|word| |
| content_lower.contains(word) || word.len() > 5 |
| ) |
| }) |
| .collect(); |
| |
| if relevant.len() < 2 { |
| return None; |
| } |
| |
| |
| if let Some(analogy) = self.analogy.find_analogy_between(&relevant) { |
| return Some(SynthesisResult { |
| insight: format!( |
| "Here's something: {} β that reminds me of {}, except {}. {}", |
| analogy.source, analogy.target, |
| analogy.target_relation, analogy.structure |
| ), |
| is_novel: true, |
| confidence: 0.5, |
| chain: vec![ |
| format!("Source: {}", analogy.source), |
| format!("Target: {}", analogy.target), |
| analogy.explanation(), |
| ], |
| }); |
| } |
| |
| None |
| } |
|
|
| |
| pub fn check_consistency(&self, statement: &str) -> ConsistencyResult { |
| let lower = statement.to_lowercase(); |
| |
| |
| for entity in self.knowledge.entities() { |
| let facts = self.knowledge.get_facts_about(&entity); |
| for fact in facts { |
| |
| if lower.contains("not") && fact.to_lowercase().contains("is ") && fact.to_lowercase().contains(&lower[..lower.find(' ').unwrap_or(0)]) { |
| return ConsistencyResult::Contradiction { |
| fact: fact.clone() |
| }; |
| } |
| } |
| } |
| |
| ConsistencyResult::Consistent |
| } |
|
|
| |
| pub fn knowledge_graph(&self) -> &knowledge::KnowledgeGraph { |
| &self.knowledge |
| } |
| } |
|
|
| impl Default for ReasoningEngine { |
| fn default() -> Self { |
| Self::new() |
| } |
| } |
|
|
| #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| pub enum QueryType { |
| WhatIs, |
| Why, |
| How, |
| Does, |
| Should, |
| Novel, |
| Math, |
| Unknown, |
| } |
|
|
| |
| #[derive(Debug, Clone)] |
| pub struct ReasoningResult { |
| pub answer: Option<String>, |
| pub confidence: BeliefState, |
| pub reasoning_chain: Vec<String>, |
| pub confidence_score: Option<f64>, |
| } |
|
|
| |
| #[derive(Debug)] |
| pub struct SynthesisResult { |
| pub insight: String, |
| pub is_novel: bool, |
| pub confidence: f64, |
| pub chain: Vec<String>, |
| } |
|
|
| |
| #[derive(Debug)] |
| pub enum ConsistencyResult { |
| Consistent, |
| Contradiction { fact: String }, |
| NeedsReview { reason: String }, |
| } |
|
|
| |
| |
| |
|
|
| #[allow(dead_code)] |
| fn relation_type_from_word(word: &str) -> knowledge::RelationType { |
| match word.to_lowercase().as_str() { |
| "is" | "are" | "was" | "were" | "be" => knowledge::RelationType::IsA, |
| "has" | "have" | "had" => knowledge::RelationType::HasProperty, |
| "causes" | "caused" | "because" => knowledge::RelationType::Causes, |
| "enables" | "allows" | "helps" => knowledge::RelationType::Enables, |
| "prevents" | "blocks" | "stops" => knowledge::RelationType::Prevents, |
| "part_of" | "within" | "inside" => knowledge::RelationType::PartOf, |
| "uses" | "through" | "via" => knowledge::RelationType::Uses, |
| "like" | "similar" | "resembles" => knowledge::RelationType::SimilarTo, |
| "opposite" | "unlike" => knowledge::RelationType::OppositeOf, |
| _ => knowledge::RelationType::RelatedTo, |
| } |
| } |
|
|