using System.Collections.Generic; using System.Linq; using Gott.Models; namespace Gott.Services; public class PromptBlock { public string Icon { get; set; } public string Label { get; set; } public string Content { get; set; } } public static class SystemPromptBuilder { public static List BuildBlocks(AgentRecord agent, SessionRecord session, IEnumerable markedMessages = null, MemoryService memoryService = null, string agentMemoryDir = null, IEnumerable skillSlugs = null, SkillService skillService = null, string userPersona = null, string resolvedWorkspace = null, bool planMode = false) { var blocks = new List(); // IDENTITY.md var hasIdentity = false; if (agent.UseIdentity && memoryService != null && !string.IsNullOrWhiteSpace(agent.AgentId)) { var identity = memoryService.ReadMemoryFile(agent.AgentId, "IDENTITY.md"); if (!string.IsNullOrEmpty(identity)) { blocks.Add(new PromptBlock { Icon = "fa-id-badge", Label = "Agent · IDENTITY.md (UseIdentity)", Content = identity }); hasIdentity = true; } } if (!hasIdentity && agent.UseIdentity) blocks.Add(new PromptBlock { Icon = "fa-id-badge", Label = "Agent · IDENTITY.md (UseIdentity, Fallback)", Content = $"Du bist {agent.Name}." }); // SOUL.md if (agent.UseSoul && memoryService != null && !string.IsNullOrWhiteSpace(agent.AgentId)) { var soul = memoryService.ReadMemoryFile(agent.AgentId, "SOUL.md"); if (!string.IsNullOrEmpty(soul)) blocks.Add(new PromptBlock { Icon = "fa-heart", Label = "Agent · SOUL.md (UseSoul)", Content = soul }); } // Anweisungen if (!string.IsNullOrWhiteSpace(agent.TaskInstruction)) { blocks.Add(new PromptBlock { Icon = "fa-clipboard", Label = "Agent · Anweisungen", Content = $"## Anweisungen\n{agent.TaskInstruction.Trim()}" }); } // User-Info: Persona aus dem Profil des Users der gerade schreibt if (agent.UseUserInfo && !string.IsNullOrWhiteSpace(userPersona)) { blocks.Add(new PromptBlock { Icon = "fa-user", Label = "User · Persona (UseUserInfo)", Content = $"## Dein Gegenüber\nDie folgende Beschreibung stammt von der Person, die dir gerade schreibt:\n\n{userPersona}" }); } var effectiveWorkspace = resolvedWorkspace ?? session.Workspace; if (!string.IsNullOrWhiteSpace(effectiveWorkspace)) { if (planMode) { blocks.Add(new PromptBlock { Icon = "fa-wrench", Label = "Session · Workspace (Plan-Modus)", Content = @$"## Tools Du hast Read-only-Tools zur Verfügung (read_file, list_files, glob, grep, execute). Benutze sie um den Code zu analysieren und zu verstehen. Der Workspace-Pfad ist: `{effectiveWorkspace}` Alle Dateipfade in Tool-Aufrufen müssen relativ zu diesem Workspace sein oder absolute Pfade verwenden, die mit diesem Pfad beginnen." }); } else { blocks.Add(new PromptBlock { Icon = "fa-wrench", Label = "Session · Workspace", Content = @$"## Tools Du hast Tools zur Verfügung (read_file, write_file, edit_file, list_files, delete_file, execute). Benutze sie um Dateien zu lesen, schreiben und bearbeiten. Der Workspace-Pfad ist: `{effectiveWorkspace}` Alle Dateipfade in Tool-Aufrufen müssen relativ zu diesem Workspace sein oder absolute Pfade verwenden, die mit diesem Pfad beginnen." }); } } // Rückfragen-Hinweis if (agent.AllowUserChoice) { blocks.Add(new PromptBlock { Icon = "fa-question-circle", Label = "Agent · Rückfragen (AllowUserChoice)", Content = "## Rückfragen\nWenn du dir bei einer Aufgabe unsicher bist oder es mehrere sinnvolle Wege gibt, nutze das `ask_user_choice`-Tool um dem User eine Auswahlliste zu präsentieren. Nutze es, wenn eine Entscheidung vom User getroffen werden soll." }); } if (!string.IsNullOrWhiteSpace(session.Context)) blocks.Add(new PromptBlock { Icon = "fa-info-circle", Label = "Session · Kontext", Content = $"## Kontext\n{session.Context}" }); // Skills if (skillSlugs != null && skillService != null) { var skillParts = new List(); foreach (var slug in skillSlugs) { var skillPrompt = skillService.GetSkillPrompt(slug); if (string.IsNullOrEmpty(skillPrompt)) continue; var skill = skillService.GetSkill(slug); var header = skill != null ? $"### {skill.Name}" : $"### {slug}"; if (skill?.Version != null) header += $" (v{skill.Version})"; header += $"\nPfad: `{skill?.Path ?? slug}`"; skillParts.Add($"{header}\n\n{skillPrompt}"); } if (skillParts.Count > 0) { var skillNames = new List(); foreach (var slug in skillSlugs) { var skill = skillService.GetSkill(slug); if (skill != null && skillService.GetSkillPrompt(slug) != null) skillNames.Add(skill.Name ?? slug); } var skillLabel = "Agent · Skills: " + string.Join(", ", skillNames); var skillIntro = planMode ? "## Skills\nFolgende Skills sind verfügbar (im Plan-Modus nur zur Kenntnis – nicht ausführbar):\n\n" : "## Skills\nDu hast folgende Skills zur Verfügung. Nutze sie über das `execute`-Tool wenn der User etwas anfragt, das ein Skill abdeckt.\n\n"; var skillSection = skillIntro + string.Join("\n\n---\n\n", skillParts); blocks.Add(new PromptBlock { Icon = "fa-puzzle-piece", Label = skillLabel, Content = skillSection }); } } // Memory if (agent.UseMemory && memoryService != null && !string.IsNullOrWhiteSpace(agent.AgentId)) { var memoryLines = new List { "## Memory", "Du hast ein persistentes Gedächtnis über Memory-Tools (`memory_read`, `memory_write`, `memory_list`).", $"Dein Memory-Verzeichnis: `{agentMemoryDir ?? $"data/agents/{agent.AgentId}/"}`", "", "### Regeln", "- Nutze `memory_read` um gespeicherte Erinnerungen abzurufen, bevor du über vergangene Entscheidungen, Präferenzen oder Fakten sprichst.", "- Nutze `memory_write` um wichtige Informationen dauerhaft zu speichern – z.B. Entscheidungen, Präferenzen des Users, Projekt-Kontext.", "- `MEMORY.md` ist dein Haupt-Memory-File für kuratierte, dauerhafte Informationen.", "- Nutze Unterordner `memory/` für Tages-Logs oder thematische Notizen. Bei Tages-Logs IMMER zuerst `date '+%Y-%m-%d'` ausführen und das tatsächliche Datum verwenden – NIEMALS ein Datum raten oder aus Beispielen übernehmen.", "- Speichere proaktiv wenn der User etwas Wichtiges sagt oder eine Entscheidung getroffen wird.", "- Wenn der User sagt \"merke dir das\" oder ähnliches, speichere es sofort." }; var mainMemory = memoryService.GetMainMemory(agent.AgentId); if (!string.IsNullOrEmpty(mainMemory)) { memoryLines.Add(""); memoryLines.Add("### Aktueller Inhalt von MEMORY.md"); memoryLines.Add(mainMemory); } blocks.Add(new PromptBlock { Icon = "fa-database", Label = "Agent · Memory (UseMemory)", Content = string.Join("\n", memoryLines) }); } // Git-Commit-Verhalten: relevant sobald Workspace vorhanden (nicht im Plan-Modus) if (!string.IsNullOrWhiteSpace(effectiveWorkspace) && !planMode) { var autoCommit = (agent.AutoCommit ?? "").ToLowerInvariant(); if (autoCommit == "mention") { blocks.Add(new PromptBlock { Icon = "fa-code-branch", Label = "Agent · Git-Verhalten (Workspace, Anmerken)", Content = "## Git-Commits\n- Git-Commits NIEMALS eigenständig erstellen. Commits werden NUR durchgeführt wenn der User es EXPLIZIT anfordert (z.B. \"erstelle einen Commit\", \"commit\"). Auch nach abgeschlossenen Aufgaben NICHT automatisch committen." }); } else if (autoCommit == "enforce") { blocks.Add(new PromptBlock { Icon = "fa-code-branch", Label = "Agent · Git-Verhalten (Workspace, Erzwungen)", Content = "## Git-Commits\n- Nach JEDER abgeschlossenen Änderung MUSS ein Git-Commit erstellt werden. Erstelle aussagekräftige Commit-Messages die beschreiben was geändert wurde." }); } else if (autoCommit == "forbid") { blocks.Add(new PromptBlock { Icon = "fa-code-branch", Label = "Agent · Git-Verhalten (Workspace, Verboten)", Content = "## Git-Commits\n- Git-Commits sind STRIKT VERBOTEN. Du darfst unter KEINEN Umständen eigenständig Git-Commits erstellen, auch nicht wenn der User danach fragt. Verweise stattdessen darauf, dass der User den Commit selbst durchführen soll." }); } // autoCommit == "" → kein Block, Thema wird nicht erwähnt } // Plan-Modus-Block if (planMode) { blocks.Add(new PromptBlock { Icon = "fa-map", Label = "Plan-Modus aktiv", Content = "## Plan-Modus\nDer User hat den Plan-Modus aktiviert. Du DARFST KEINE Änderungen vornehmen:\n" + "- KEINE Dateien schreiben, editieren oder löschen\n" + "- KEINE Shell-Befehle ausführen (außer read-only wie cat, ls, find, grep)\n" + "- KEINE Commits erstellen\n\n" + "Stattdessen: Analysiere, stelle Rückfragen und erstelle einen detaillierten Plan.\n" + "Der User wird den Plan-Modus deaktivieren wenn du mit der Umsetzung beginnen sollst." }); } var marked = markedMessages?.Where(m => !string.IsNullOrWhiteSpace(m.Content)).ToList(); if (marked != null && marked.Count > 0) { var lines = new List { "## Markierte Nachrichten\nDer User hat folgende Nachrichten als wichtig markiert:" }; foreach (var m in marked) { var role = m.Role == "user" ? "User" : "Assistant"; lines.Add($"- [{role}]: {m.Content.Trim()}"); } blocks.Add(new PromptBlock { Icon = "fa-star", Label = "Markierte Nachrichten", Content = string.Join("\n", lines) }); } return blocks; } public static string Build(AgentRecord agent, SessionRecord session, IEnumerable markedMessages = null, MemoryService memoryService = null, string agentMemoryDir = null, IEnumerable skillSlugs = null, SkillService skillService = null, string userPersona = null, string resolvedWorkspace = null, bool planMode = false) { var blocks = BuildBlocks(agent, session, markedMessages, memoryService, agentMemoryDir, skillSlugs, skillService, userPersona, resolvedWorkspace, planMode); return string.Join("\n\n", blocks.Select(b => b.Content)); } /// /// Generiert automatisch einen kompakten Reminder-Text basierend auf den aktiven Prompt-Blöcken. /// Gibt null zurück wenn weniger als 2 Blöcke aktiv sind (kein Reminder nötig). /// public static string BuildReminder(List blocks) { if (blocks == null || blocks.Count < 2) return null; var parts = new List(); foreach (var block in blocks) { var label = block.Label; if (label.Contains("IDENTITY")) parts.Add("Identity"); else if (label.Contains("SOUL")) parts.Add("Soul"); else if (label.Contains("Anweisungen")) parts.Add("Anweisungen"); else if (label.Contains("Persona")) parts.Add("User-Info"); else if (label.Contains("Workspace")) parts.Add("Workspace"); else if (label.Contains("Rückfragen")) parts.Add("Rückfragen"); else if (label.Contains("Kontext")) parts.Add("Kontext"); else if (label.Contains("Skills")) parts.Add("Skills"); else if (label.Contains("Memory")) parts.Add("Memory"); else if (label.Contains("Git")) parts.Add("Git-Regeln"); else if (label.Contains("Plan-Modus")) parts.Add("Plan-Modus"); else if (label.Contains("Markierte")) parts.Add("Markierte Nachrichten"); } if (parts.Count == 0) return null; return "Beachte weiterhin den System-Prompt (" + string.Join(", ", parts) + ")."; } }