// ๐ŸŒŒ [๋ธ”๋ž™ํ™€ ์‚ฌ๊ฑด์˜ ์ง€ํ‰์„  ํ™€๋กœ๊ทธ๋ž˜ํ”ฝ RAG] (src/dynamic_knowledge_rag.rs) // ์ด๋ก  ๊ธฐ๋ฐ˜: ๋ฒ ์ผ„์Šˆํƒ€์ธ-ํ˜ธํ‚น ์ •๋ณด ํ•œ๊ณ„(Bekenstein Bound) & 't Hooft-Susskind ํ™€๋กœ๊ทธ๋ž˜ํ”ฝ ์›๋ฆฌ use std::fs::{self, File, OpenOptions}; use std::io::{BufRead, BufReader, Write}; use std::path::Path; #[derive(Clone, Debug)] pub struct DynamicDocument { pub id: usize, pub title: String, pub category: String, pub content: String, pub horizon_x: u8, // 256x256 ์‚ฌ๊ฑด์˜ ์ง€ํ‰์„  2D X ์ขŒํ‘œ pub horizon_y: u8, // 256x256 ์‚ฌ๊ฑด์˜ ์ง€ํ‰์„  2D Y ์ขŒํ‘œ pub hawking_mass: f32, // ์ค‘๋ ฅ ํฌํ…์…œ ์งˆ๋Ÿ‰ (์ •๋ณด ๋ฐ€๋„) pub keywords: Vec, } pub struct DynamicKnowledgeRAG { pub documents: Vec, pub corpus_dir: String, pub horizon_grid: [[f32; 256]; 256], // 2D ์‚ฌ๊ฑด์˜ ์ง€ํ‰์„  ์ค‘๋ ฅ ๊ณก๋ฅ  ํ…์„œ } impl DynamicKnowledgeRAG { pub fn new(corpus_dir: &str) -> Self { let mut rag = DynamicKnowledgeRAG { documents: Vec::new(), corpus_dir: corpus_dir.to_string(), horizon_grid: [[0.0; 256]; 256], }; if !Path::new(corpus_dir).exists() { let _ = fs::create_dir_all(corpus_dir); } rag.initialize_default_corpus(); rag.reload_all_documents(); rag } fn initialize_default_corpus(&self) { let sample_file = format!("{}/world_knowledge_2026.jsonl", self.corpus_dir); if !Path::new(&sample_file).exists() { if let Ok(mut f) = File::create(&sample_file) { let initial_data = r#"{"id":1,"category":"์ตœ์‹ IT","title":"2026 ์ฐจ์„ธ๋Œ€ ์–‘์ž-๊ด‘ํ•™ AI ๊ฐ€์†๊ธฐ QuantumX-9000","content":"QuantumX-9000์€ 2026๋…„ ์ถœ์‹œ๋œ ์„ธ๊ณ„ ์ตœ์ดˆ์˜ ์ƒ์˜จ ์‹ค๋ฆฌ์ฝ˜ ํฌํ† ๋‹‰์Šค ๊ธฐ๋ฐ˜ ์–‘์ž ๊ฐ„์„ญ ๊ฐ€์†๊ธฐ๋กœ, ํ…Œ๋ผํ”Œ๋กญ์Šค๋‹น 0.05W์˜ ๊ทน์ €์ „๋ ฅ์„ ์†Œ๋ชจํ•˜๋ฉฐ 1.58๋น„ํŠธ ์—ฐ์‚ฐ ์ „์šฉ ๊ด‘ํ•™ ํ…์„œ ์ฝ”์–ด๋ฅผ ๋‚ด์žฅํ•จ."} {"id":2,"category":"์šฐ์ฃผํƒ์‚ฌ","title":"2026 ์•„๋ฅดํ…Œ๋ฏธ์Šค 3ํ˜ธ ์œ ์ธ ๋‹ฌ ์ฐฉ๋ฅ™ ๋ฐ ์˜๊ตฌ ๊ธฐ์ง€ ๊ฑด์„ค","content":"2026๋…„ ์ธ๋ฅ˜๋Š” 50์—ฌ ๋…„ ๋งŒ์— ๋‹ฌ ๋‚จ๊ทน ์—์ด์ปจ ๋ถ„์ง€์— ์œ ์ธ ์ฐฉ๋ฅ™ํ•˜์—ฌ ์–ผ์Œ ์ˆ˜์ž์›์„ ์ฑ„๊ตดํ•˜๊ณ  ์˜๊ตฌ์ ์ธ ์œ ์ธ ์—ฐ๊ตฌ ๊ธฐ์ง€ ์•„๋ฅดํ…Œ๋ฏธ์Šค ๋ฒ ์ด์Šค์บ ํ”„๋ฅผ ๊ณต์‹ ๊ฐ€๋™ํ•จ."} {"id":3,"category":"์ƒ๋ช…๊ณตํ•™","title":"2026 ๋‚˜๋…ธ๋กœ๋ด‡ ๊ธฐ๋ฐ˜ ํ‘œ์  ์•”์„ธํฌ ์ง์ ‘ ์ œ๊ฑฐ ์ž„์ƒ ์„ฑ๊ณต","content":"2026๋…„ ํ˜ˆ๊ด€์„ ํƒ€๊ณ  ์ด๋™ํ•˜๋ฉฐ ํŠน์ • ์•”์„ธํฌ ํ‘œ๋ฉด ํ•ญ์›๋งŒ์„ ๊ฐ์ง€ํ•ด ์ •ํ™•ํžˆ ํŒŒ๊ดดํ•˜๋Š” ์ž์œจ ์œ ๋„ DNA ์˜ค๋ฆฌ๊ฐ€๋ฏธ ๋‚˜๋…ธ๋กœ๋ด‡์˜ 3์ƒ ์ž„์ƒ ์‹œํ—˜์ด ์„ธ๊ณ„ ์ตœ์ดˆ๋กœ ์Šน์ธ๋จ."} "#; let _ = f.write_all(initial_data.as_bytes()); } } } pub fn reload_all_documents(&mut self) { self.documents.clear(); self.horizon_grid = [[0.0; 256]; 256]; if let Ok(entries) = fs::read_dir(&self.corpus_dir) { for entry in entries.flatten() { let path = entry.path(); if path.extension().and_then(|s| s.to_str()) == Some("jsonl") { if let Ok(file) = File::open(&path) { let reader = BufReader::new(file); for line in reader.lines().flatten() { if line.trim().is_empty() { continue; } if let Some(mut doc) = self.parse_jsonl_line(&line) { // ํ™€๋กœ๊ทธ๋ž˜ํ”ฝ ์‚ฌ๊ฑด์˜ ์ง€ํ‰์„  2D ์ขŒํ‘œ ํˆฌ์˜ let (hx, hy, mass) = Self::compute_holographic_projection(&doc.title, &doc.content); doc.horizon_x = hx; doc.horizon_y = hy; doc.hawking_mass = mass; self.horizon_grid[hy as usize][hx as usize] += mass; self.documents.push(doc); } } } } } } } /// [ํ™€๋กœ๊ทธ๋ž˜ํ”ฝ ํˆฌ์˜]: ๊ณ ์ฐจ์› ํ…์ŠคํŠธ๋ฅผ 2D ์ง€ํ‰์„  256x256 ์ขŒํ‘œ์™€ ํ˜ธํ‚น ์ค‘๋ ฅ ๊ณก๋ฅ ๋กœ ํˆฌ์˜ fn compute_holographic_projection(title: &str, content: &str) -> (u8, u8, f32) { let mut hx: u32 = 0; let mut hy: u32 = 0; for (i, b) in title.bytes().enumerate() { hx = (hx * 31 + b as u32 + i as u32) % 256; } for (i, b) in content.bytes().enumerate() { hy = (hy * 37 + b as u32 + (i * 7) as u32) % 256; } let mass = (content.len() as f32).ln().max(1.0); (hx as u8, hy as u8, mass) } fn parse_jsonl_line(&self, line: &str) -> Option { let get_val = |key: &str| -> String { if let Some(pos) = line.find(&format!("\"{}\":", key)) { let rest = &line[pos + key.len() + 3..]; if rest.starts_with('"') { if let Some(end) = rest[1..].find('"') { return rest[1..1 + end].to_string(); } } else if let Some(end) = rest.find(|c: char| c == ',' || c == '}') { return rest[..end].trim().to_string(); } } String::new() }; let title = get_val("title"); let category = get_val("category"); let content = get_val("content"); let id_str = get_val("id"); let id = id_str.parse::().unwrap_or(self.documents.len() + 1); if !title.is_empty() && !content.is_empty() { let mut keywords = Vec::new(); for word in content.split_whitespace().chain(title.split_whitespace()) { let clean: String = word.chars().filter(|c| c.is_alphanumeric()).collect(); if clean.len() >= 2 { keywords.push(clean.to_lowercase()); } } Some(DynamicDocument { id, title, category, content, horizon_x: 0, horizon_y: 0, hawking_mass: 1.0, keywords }) } else { None } } /// [์‹ค์‹œ๊ฐ„ ์ง€์‹ ์ฃผ์ž…]: ์‚ฌ๊ฑด์˜ ์ง€ํ‰์„ ์— ์ฆ‰์‹œ ๋ธ”๋ž™ํ™€ ์ค‘๋ ฅ ์šฐ๋ฌผ ํ˜•์„ฑ pub fn ingest_new_knowledge(&mut self, category: &str, title: &str, content: &str) { let doc_id = self.documents.len() + 1; let (hx, hy, mass) = Self::compute_holographic_projection(title, content); let mut keywords = Vec::new(); for word in content.split_whitespace().chain(title.split_whitespace()) { let clean: String = word.chars().filter(|c| c.is_alphanumeric()).collect(); if clean.len() >= 2 { keywords.push(clean.to_lowercase()); } } let doc = DynamicDocument { id: doc_id, title: title.to_string(), category: category.to_string(), content: content.to_string(), horizon_x: hx, horizon_y: hy, hawking_mass: mass, keywords, }; self.horizon_grid[hy as usize][hx as usize] += mass; self.documents.push(doc.clone()); let sample_file = format!("{}/world_knowledge_2026.jsonl", self.corpus_dir); if let Ok(mut f) = OpenOptions::new().create(true).append(true).open(&sample_file) { let line = format!("{{\"id\":{},\"category\":\"{}\",\"title\":\"{}\",\"content\":\"{}\"}}\n", doc_id, category, title, content); let _ = f.write_all(line.as_bytes()); } } /// [๋ธ”๋ž™ํ™€ ์ค‘๋ ฅ ํฌํ…์…œ ์ธก์ง€์„  ๊ฒ€์ƒ‰ (Holographic Geodesic RAG)]: /// ์งˆ์˜๊ฐ€ ์‹œ๊ณต๊ฐ„ ์ง€ํ‰์„ ์— ๋˜์ ธ์กŒ์„ ๋•Œ, ๊ฐ€์žฅ ๊ฐ•ํ•œ ์ค‘๋ ฅ ์šฐ๋ฌผ(Singularity Well)๋กœ ๋นจ๋ ค ๋“ค์–ด๊ฐ€๋Š” ๋ฌธ์„œ๋ฅผ ์ฆ‰์‹œ ์ธ์ถœ! pub fn search_documents(&self, query: &str, top_k: usize) -> Vec<(&DynamicDocument, f32)> { let (qx, qy, _qmass) = Self::compute_holographic_projection(query, query); let query_lower = query.to_lowercase(); let query_words: Vec = query_lower .split_whitespace() .map(|w| w.chars().filter(|c| c.is_alphanumeric()).collect()) .filter(|w: &String| w.len() >= 2) .collect(); let mut scored: Vec<(&DynamicDocument, f32)> = self.documents.iter().map(|doc| { // 1. ์‚ฌ๊ฑด์˜ ์ง€ํ‰์„  2D ๊ณต๊ฐ„ ๊ฑฐ๋ฆฌ (์ธก์ง€์„  ์ธ๋ ฅ) let dx = (doc.horizon_x as f32 - qx as f32).abs(); let dy = (doc.horizon_y as f32 - qy as f32).abs(); let dist_sq = dx * dx + dy * dy + 1.0; let gravitational_pull = (doc.hawking_mass * 100.0) / dist_sq; // 2. ์˜๋ฏธ๋ก ์  ํ‚ค์›Œ๋“œ ๊ณต๋ช… let mut semantic_score: f32 = 0.0; if doc.title.to_lowercase().contains(&query_lower) { semantic_score += 20.0; } for qw in &query_words { if doc.title.to_lowercase().contains(qw) { semantic_score += 10.0; } if doc.content.to_lowercase().contains(qw) { semantic_score += 5.0; } } let total_score = gravitational_pull * 0.3 + semantic_score * 0.7; (doc, total_score) }).filter(|(_, score)| *score > 0.0).collect(); scored.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal)); scored.truncate(top_k); scored } }