use crate::types::Result; use crate::error::NazarickError; /// Verantwortlich für das Zusammensetzen des System-Prompts. /// /// Reihenfolge ist bewusst gewählt — höchste Priorität zuerst: /// 1. shared_core.md — gilt für alle Agenten, Sicherheit /// 2. soul_core.md — agenten-spezifische Erweiterungen /// 3. soul_personality.md — Identität, Charakter, entwickelbarer Stil /// /// Spätere Abschnitte können frühere nie überschreiben. pub struct PromptBuilder { /// Pfad zu shared_core.md — systemweite Regeln für alle Agenten shared_core_path: String, /// Pfad zu soul_core.md — agenten-spezifische unveränderliche Regeln soul_core_path: String, /// Pfad zu soul_personality.md — entwickelbarer Persönlichkeitsteil soul_personality_path: String, } impl PromptBuilder { /// Erstellt einen neuen PromptBuilder mit den angegebenen Dateipfaden. pub fn new( shared_core_path: impl Into, soul_core_path: impl Into, soul_personality_path: impl Into, ) -> Self { Self { shared_core_path: shared_core_path.into(), soul_core_path: soul_core_path.into(), soul_personality_path: soul_personality_path.into(), } } /// Liest alle soul-Dateien und kombiniert sie zum finalen System-Prompt. /// shared_core und soul_core sind Pflicht. /// soul_personality ist optional — graceful fallback auf leere Persönlichkeit. pub fn build(&self) -> Result { // shared_core ist Pflicht — systemweite Sicherheitsregeln let shared = std::fs::read_to_string(&self.shared_core_path) .map_err(|e| NazarickError::Config( format!("shared_core.md nicht gefunden unter '{}': {}", self.shared_core_path, e) ))?; // soul_core ist Pflicht — agenten-spezifische Regeln let core = std::fs::read_to_string(&self.soul_core_path) .map_err(|e| NazarickError::Config( format!("soul_core.md nicht gefunden unter '{}': {}", self.soul_core_path, e) ))?; // soul_personality ist optional — graceful fallback let personality = std::fs::read_to_string(&self.soul_personality_path) .unwrap_or_default(); // Zusammensetzen: shared zuerst, dann core, dann personality let mut parts = vec![shared]; if !core.trim().is_empty() { parts.push(core); } if !personality.trim().is_empty() { parts.push(personality); } Ok(parts.join("\n\n---\n\n")) } }